実装の詳細
Sitecoreは、セッション状態管理のために「ASP.NET SessionStateModule」のデフォルトの実装を使用しています。
セッション ストアでのデータの衝突や予期しないセッション状態の動作を回避するために、「SessionStateModule」クラス及び「SessionStateStoreProviderBase」クラスには、ASP.NETページの実行中に特定のセッションのセッション ストア アイテムを排他的にロックする機能が含まれています。
「SessionStateModule」インスタンスは、「GetItemExclusive」メソッドまたは「GetItem」メソッドのいずれかの呼び出し中にロックされたセッション データを検出すると、ロックが解除されるか、「ExecutionTimeout」プロパティで指定された時間が経過するまで、0.5秒間隔でセッション データを再リクエストします。
この挙動については、MSDNの「Locking Session-Store Data」の記事で詳しく記述されています。また参照番号「117091416336585」をMicrosoftサポートに送信することにより、特定の「ASP.NET SessionStateModule」の挙動に関する詳細情報を取得できます。
過負荷の原因
同じASP.NETセッション識別子を持つ複数のリクエストが着信した場合、一度に1つしか成功しない場合でも、各リクエストはセッション アイテムの排他ロックを取得しようとします。この挙動により、複数のスレッドが同じユニットを同時にロックしようとして、セッション状態ストアに過度の負荷がかかります。
セッション状態ストアに大きな負荷がかかると、次の症状が発生する可能性があります:
セッション状態ストアに送信される同時リクエスト数が自然と急増する(例えば、MS SQLセッション状態プロバイダーの「GetItemExclusive」コマンドの急増など)。
セッション状態ストアの応答時間が長くなったり、Azure SQLデータベースサービスのDTUクォータに達したりする。
Message: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
Source: System.Data
at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
...
at System.Web.SessionState.SqlSessionStateStore.SqlStateConnection..ctor(SqlPartitionInfo sqlPartitionInfo, TimeSpan retryInterval)
Message: Unable to connect to SQL Server session database.
Source: System.Web
at System.Web.SessionState.SqlSessionStateStore.ThrowSqlConnectionException(SqlConnection conn, Exception e)
at System.Web.SessionState.SqlSessionStateStore.SqlStateConnection..ctor(SqlPartitionInfo sqlPartitionInfo, TimeSpan retryInterval)
...
at System.Web.SessionState.SessionStateModule.GetSessionStateItem()
at System.Web.SessionState.SessionStateModule.PollLockedSessionCallback(Object state)
Exception type: TimeoutException
Exception message: Timeout performing EVAL, inst: 14, mgr: Inactive, err: never, queue: 71, qu: 0, qs: 71, qc: 0, wr: 0, wq: 0, in: 65536, ar: 0, IOCP: (Busy=1,Free=999,Min=8,Max=1000), WORKER: (Busy=19,Free=32748,Min=8,Max=32767), clientName: RD0003FF85401C
at StackExchange.Redis.ConnectionMultiplexer.ExecuteSyncImpl[T](Message message, ResultProcessor`1 processor, ServerEndPoint server)
at StackExchange.Redis.RedisBase.ExecuteSync[T](Message message, ResultProcessor`1 processor, ServerEndPoint server)
at StackExchange.Redis.RedisDatabase.ScriptEvaluate(String script, RedisKey[] keys, RedisValue[] values, CommandFlags flags)
at Sitecore.SessionProvider.Redis.StackExchangeClientConnection.<>c__DisplayClass7.<Eval>b__6()
at Sitecore.SessionProvider.Redis.StackExchangeClientConnection.RetryForScriptNotFound(Func`1 redisOperation)
at Sitecore.SessionProvider.Redis.StackExchangeClientConnection.RetryLogic(Func`1 redisOperation)
at Sitecore.SessionProvider.Redis.StackExchangeClientConnection.Eval(String script, String[] keyArgs, Object[] valueArgs)
at Sitecore.SessionProvider.Redis.RedisConnectionWrapper.TryTakeWriteLockAndGetData(String sessionId, DateTime lockTime, Object& lockId, ISessionStateItemCollection& data, Int32& sessionTimeout)
at Sitecore.SessionProvider.Redis.RedisSessionStateProvider.GetItemFromSessionStore(Boolean isWriteLockRequired, HttpContext context, String id, Boolean& locked, TimeSpan& lockAge, Object& lockId, SessionStateActions& actions)
at Sitecore.SessionProvider.Redis.RedisSessionStateProvider.GetItemExclusive(HttpContext context, String id, Boolean& locked, TimeSpan& lockAge, Object& lockId, SessionStateActions& actions)
at System.Web.SessionState.SessionStateModule.GetSessionStateItem()
ERROR Application error.
Exception: System.TimeoutException
Message: Timeout waiting for a MongoConnection.
Source: MongoDB.Driver
at MongoDB.Driver.Internal.MongoConnectionPool.AcquireConnection(AcquireConnectionOptions options)
...
at Sitecore.SessionProvider.MongoDB.MongoSessionStateProvider.GetItemExclusive(HttpContext context, String id, Boolean& locked, TimeSpan& lockAge, Object& lockId, SessionStateActions& actions)
at System.Web.SessionState.SessionStateModule.GetSessionStateItem()
at System.Web.SessionState.SessionStateModule.BeginAcquireState(Object source, EventArgs e, AsyncCallback cb, Object extraData)
at System.Web.HttpApplication.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
ERROR Application error.
Exception: MongoDB.Driver.MongoConnectionException
Message: Too many threads are already waiting for a connection.
Source: MongoDB.Driver
at MongoDB.Driver.Internal.MongoConnectionPool.AcquireConnection(AcquireConnectionOptions options)
at MongoDB.Driver.MongoServerInstance.AcquireConnection()
at MongoDB.Driver.MongoServer.AcquireConnection(ReadPreference readPreference)
at MongoDB.Driver.MongoCollection.Update(IMongoQuery query, IMongoUpdate update, MongoUpdateOptions options)
at Sitecore.SessionProvider.MongoDB.MongoSessionStateStore.InsertItem(String application, String id, Int32 flags, SessionStateStoreData sessionState)
at System.Web.SessionState.SessionStateModule.OnReleaseState(Object source, EventArgs eventArgs)
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
ERROR Application error.
Exception: System.Web.HttpException
Message: The request queue limit of the session is exceeded.
Source: System.Web at System.Web.SessionState.SessionStateModule.QueueRef()
at System.Web.SessionState.SessionStateModule.PollLockedSession()
at System.Web.SessionState.SessionStateModule.GetSessionStateItem()
at System.Web.SessionState.SessionStateModule.BeginAcquireState(Object source, EventArgs e, AsyncCallback cb, Object extraData)
at System.Web.HttpApplication.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
この事象を解決するには、以下のオプションの実施を検討してください:
セッションにカスタム オブジェクトを保存することは避けてください。セッション サイズが大きい場合、セッションのメカニズムがボトルネックになる可能性があります。代わりに、カスタム キャッシュまたは共有ストレージを使用してください。
セッション状態の使用を避けるか、ReadOnly MVCコントローラーを利用して可能な限り読み取り専用モードで使用するように明示的に指定するか、またはASP.NET Webフォームページのページ ディレクティブで「EnableSessionState="Readonly"」属性を使用します。
以下のドキュメントをご参照いただき、セッション状態プロバイダーを設定してください: セッション状態のスケーリングと設定
註:Sitecore XP 10.0以降では、Shared Session State Storeロールがデフォルトで無効になっています。Sitecore XP 9.3またはそれ以前のバージョンにおいても、Shared Session State Storeロールを無効にすることを検討してください。詳細については、こちらをご覧ください。
「Web.config」ファイル及び「Sitecore.Analytics.Tracking.config」ファイルで、プライベートおよび共有セッション状態プロバイダーの両方の定義の「pollingInterval」属性値を2秒から60秒に増やします。
「Web.config」ファイルの「httpRuntime」要素で、「executionTimeout」属性値を110秒(デフォルトのASP.NETの設定値)に減らします。本属性は、リクエストの許可された実行時間と、リクエストの最大排他ロック期間を制御します。ロック期間を短くすると、ロックが以前に解放されていなかった場合に、他のリクエストがより速くロックを取得できるようになります。
セッションごとの同時リクエスト数のスロットルを構成し、デフォルト(50)よりも低い値を設定します:
<add key="aspnet:RequestQueueLimitPerSession" value="25"/>
<configuration>
...
<system.web>
...
<compilation targetFramework="4.5.2"/>
<httpRuntime targetFramework="4.5.2"/>
...
</system.web>
...
</configuration>
Javascript(例えば、Ajaxコールなど)を使用して、ASPXまたはCSHTMLページからSitecoreXPに、複数のリクエストを送り返さないようにしてください。