/// <summary> /// Creates sessions concurrently and stores a random binary blob in each session. This /// method then waits for all the sessions to expire and verifies that each session has in /// fact been removed from the provider and the provider's memory cache. It also takes a /// snapshot of the memory usage pre session creation, post session creation and post /// session expiration. This method will fail is any session still exists in the provider /// or there was excessive memory usage. /// </summary> /// <param name="provider">The MongoDB provider to use for the test.</param> /// <param name="numberOfSessions">The number of sessions to create concurrently.</param> /// <param name="sessionSizeInMegabytes">The number of MB of buffer to create and store in each session.</param> private void CreateConcurrentSessionObjects(MongoDBSessionStateProvider provider, int numberOfSessions, byte sessionSizeInMegabytes) { var sessions = new System.Collections.Generic.SynchronizedCollection <string>(); double initialWorkingSet, postSessionCreationWorkingSet, postSessionExpirationWorkingSet; var currentProcess = System.Diagnostics.Process.GetCurrentProcess(); initialWorkingSet = (currentProcess.WorkingSet64 / 1024.0 / 1000.0); int sessionSize = (sessionSizeInMegabytes * 1024 * 1024); var result = Parallel.For(0, numberOfSessions, idx => { System.Threading.Thread.Sleep(50); // Give a little wiggle room HttpRequest request = null; HttpResponse response = null; HttpContext context = GetContext(out request, out response); string sessionId = _SessionIdManager.CreateSessionID(context); sessions.Add(sessionId); byte[] dummyData = new byte[sessionSize]; (new Random()).NextBytes(dummyData); var dataStore = provider.CreateNewStoreData(context, (_TimeoutInSeconds / 60)); dataStore.Items["Data"] = dummyData; provider.SetAndReleaseItemExclusive(context, sessionId, dataStore, null, true); TestContext.WriteLine("Created session {0} with dummy data", sessionId); }); while (!result.IsCompleted) { System.Threading.Thread.Sleep(1000); } GC.Collect(); currentProcess.Refresh(); postSessionCreationWorkingSet = (currentProcess.WorkingSet64 / 1024.0 / 1000.0); var counter = _TimeoutInSeconds + 60; TestContext.WriteLine("Waiting {0} seconds for session expiration...", counter); while (counter > 0) { System.Threading.Thread.Sleep(1000); counter--; } TestContext.WriteLine("Checking Sessions in store and cache..."); var sessionIds = sessions.ToArray(); result = Parallel.ForEach <string>(sessionIds, sessionId => { HttpRequest request = null; HttpResponse response = null; HttpContext context = GetContext(out request, out response); bool locked; TimeSpan lockAge; object lockId; System.Web.SessionState.SessionStateActions actions; var storeData = provider.GetItem(context, sessionId, out locked, out lockAge, out lockId, out actions); Assert.IsNull(storeData); // Now check the cache! ISessionStateData sessionStateData; bool existsInCache = CheckSessionExistsInCache(provider, sessionId, out sessionStateData); if (existsInCache || sessionStateData != null) { TestContext.WriteLine("Session {0} still exists in cache!", sessionId); } else { sessions.Remove(sessionId); } }); while (!result.IsCompleted) { System.Threading.Thread.Sleep(1000); } GC.Collect(); GC.WaitForPendingFinalizers(); currentProcess.Refresh(); postSessionExpirationWorkingSet = (currentProcess.WorkingSet64 / 1024.0 / 1000.0); TestContext.WriteLine("Memory Usage: Initial = {0}MB, PostSessionCreation = {1}MB, PostSessionExpiration = {2}MB", initialWorkingSet, postSessionCreationWorkingSet, postSessionExpirationWorkingSet); TestContext.WriteLine("After expiration, session count = {0}", sessions.Count); currentProcess.Dispose(); double memoryDifference = postSessionExpirationWorkingSet - initialWorkingSet; TestContext.WriteLine("Memory Difference -> {0}MB", memoryDifference); bool isMemoryExhausted = (memoryDifference > 20); // This should be based on the buffer size and number of sessions if (sessions.Count != 0) { Assert.Fail("{0} Sessions still exist in memory. The memory has grown from {1}MB to {2}MB", sessions.Count, initialWorkingSet, postSessionExpirationWorkingSet); } if (isMemoryExhausted) { Assert.Fail("Excessive Memory Consumption. Memory has grown by {0}MB", memoryDifference); } }
/// <summary> /// Method that creates a new session using the provider supplied. This method /// also verifies that the session was created, then modifies the session, then /// deletes the session and verifies deletion from the store and the memory cache /// in the provider. /// </summary> /// <param name="provider">The MongoDB provider to use for the test.</param> private void VerifySessionPersistence(MongoDBSessionStateProvider provider) { string itemName = "DummyItem"; string itemValue = "Value"; HttpRequest request = null; HttpResponse response = null; HttpContext context = GetContext(out request, out response); TestContext.WriteLine("Creating Store Data"); var dataStore = provider.CreateNewStoreData(context, (_TimeoutInSeconds / 60)); dataStore.Items[itemName] = itemValue; var sessionId = _SessionIdManager.CreateSessionID(context); TestContext.WriteLine("Writing Store Data"); provider.SetAndReleaseItemExclusive(context, sessionId, dataStore, null, true); System.Threading.Thread.Sleep(2000); bool locked; TimeSpan lockAge; object lockId; System.Web.SessionState.SessionStateActions actions; TestContext.WriteLine("Retrieving new Store Data"); var retrievedDataStore = provider.GetItemExclusive(context, sessionId, out locked, out lockAge, out lockId, out actions); if (retrievedDataStore == null || retrievedDataStore.Items.Count == 0) { Assert.Fail("Retrieved data store does not contain session data"); } TestContext.WriteLine("Testing Store Data"); var dummyValue = retrievedDataStore.Items[itemName]; Assert.AreEqual(itemValue, dummyValue); itemValue = "NewValue"; retrievedDataStore.Items[itemName] = itemValue; TestContext.WriteLine("Updating Store Data"); provider.SetAndReleaseItemExclusive(context, sessionId, retrievedDataStore, lockId, false); System.Threading.Thread.Sleep(2000); var retrievedDataStore2 = provider.GetItemExclusive(context, sessionId, out locked, out lockAge, out lockId, out actions); if (retrievedDataStore2 == null || retrievedDataStore2.Items.Count == 0) { Assert.Fail("Retrieved data store does not contain session data"); } TestContext.WriteLine("Testing Store Data"); Assert.AreEqual(itemValue, retrievedDataStore2.Items[itemName]); TestContext.WriteLine("Releasing Store Data"); provider.ReleaseItemExclusive(context, sessionId, lockId); System.Threading.Thread.Sleep(2000); retrievedDataStore2 = provider.GetItemExclusive(context, sessionId, out locked, out lockAge, out lockId, out actions); TestContext.WriteLine("Deleting Store Data"); provider.RemoveItem(context, sessionId, lockId, retrievedDataStore2); System.Threading.Thread.Sleep(2000); TestContext.WriteLine("Ensuring store was deleted"); var retrievedDataStore3 = provider.GetItem(context, sessionId, out locked, out lockAge, out lockId, out actions); Assert.IsNull(retrievedDataStore3); System.Threading.Thread.Sleep(2000); TestContext.WriteLine("Ensuring cache is empty"); ISessionStateData sessionStateDataFromCache = null; bool sessionExistsInCache = CheckSessionExistsInCache(provider, sessionId, out sessionStateDataFromCache); if (sessionExistsInCache) { Assert.Fail("Session should have been removed but still exists in cache"); } TestContext.WriteLine("Success"); }
/// <summary> /// Method that creates a new session, waits for it to automatically expire and then /// verifies that the session has been removed from the provider as well as the provider's /// memory cache. /// </summary> /// <param name="mongoSessionStateProvider">The MongoDB provider to use for the test.</param> private void VerifySessionExpiration(MongoDBSessionStateProvider mongoSessionStateProvider) { var mongoSessionStateProviderBaseType = mongoSessionStateProvider.GetType().BaseType; HttpRequest httpRequest = null; HttpResponse httpResponse = null; HttpContext httpContext = GetContext(out httpRequest, out httpResponse); var timeoutInMinutes = (_TimeoutInSeconds / 60); var storeData = mongoSessionStateProvider.CreateNewStoreData(httpContext, timeoutInMinutes); storeData.Items["DummyEntry"] = "DummyValue"; string sessionId = null; lock (_SessionIdManager) { sessionId = _SessionIdManager.CreateSessionID(httpContext); } object lockId = null; // New items don't have a lockId mongoSessionStateProvider.SetAndReleaseItemExclusive(httpContext, sessionId, storeData, lockId, true); TestContext.WriteLine("Created Session {0}", sessionId); int counter = _TimeoutInSeconds + 60; TestContext.WriteLine("Waiting {0} seconds...", counter); while (counter > 0) { System.Threading.Thread.Sleep(1000); counter--; } bool locked; TimeSpan lockAge; object lockId2 = null; System.Web.SessionState.SessionStateActions actions; var storeDataAfterExpiry = mongoSessionStateProvider.GetItem(httpContext, sessionId, out locked, out lockAge, out lockId2, out actions); if (storeDataAfterExpiry == null || storeDataAfterExpiry.Items.Count == 0) { TestContext.WriteLine("Session expired from Session State Provider. Verifying session provider cache..."); ISessionStateData objectInCache = null; bool objectExistsInCache = CheckSessionExistsInCache(mongoSessionStateProvider, sessionId, out objectInCache); if (objectInCache != null) { Assert.Fail("Session data exists in cache when should have expired - Expires = {0}", objectInCache.Expires); } else if (objectInCache == null && objectExistsInCache) { Assert.Fail("Session data has expired but the cache is retaining the object!"); } else { TestContext.WriteLine("Success - session data does not exist in cache."); } } else { Assert.Fail("Session expired but MongoDB Sessions State Provider still contains data!"); } }
/// <summary> /// Method that creates a new session and touches it right before the expiry a couple of times. /// The final time it will not wait until after the expiration which the method will then /// expect the session to no longer be available. /// </summary> /// <param name="provider">The MongoDB provider to use for the test.</param> private void VerifySessionSlidingExpiration(MongoDBSessionStateProvider provider) { HttpRequest request = null; HttpResponse response = null; HttpContext context = GetContext(out request, out response); var sessionId = _SessionIdManager.CreateSessionID(context); var dataStore = provider.CreateNewStoreData(context, (_TimeoutInSeconds / 60)); dataStore.Items["Dummy"] = "Value"; TestContext.WriteLine("Creating dummy session with id {0}", sessionId); provider.SetAndReleaseItemExclusive(context, sessionId, dataStore, null, true); int iterations = 4; for (int i = 0; i < iterations; i++) { bool isLastIteration = (i == iterations - 1); int counter = _TimeoutInSeconds + (isLastIteration ? 10 : -10); TestContext.WriteLine("Waiting {0} seconds (expiration set to {1} seconds)...", counter, _TimeoutInSeconds); while (counter > 0) { System.Threading.Thread.Sleep(1000); counter--; } TestContext.WriteLine("Retrieving session again to reset expiry"); bool locked; TimeSpan lockAge; object lockId; System.Web.SessionState.SessionStateActions actions; var dataStore2 = provider.GetItemExclusive(context, sessionId, out locked, out lockAge, out lockId, out actions); if (isLastIteration) { if (dataStore2 != null) { Assert.Fail("Session has NOT expired."); } ISessionStateData dataFromCache; bool existsInCache = CheckSessionExistsInCache(provider, sessionId, out dataFromCache); if (existsInCache) { Assert.Fail("Session has expired however it still exists in the memory cache."); } } else { if (dataStore2 == null || dataStore2.Items.Count == 0) { Assert.Fail("Session Missing prior to expiry??"); } else { TestContext.WriteLine("Session retrieved successfully during iteration {0}", (i + 1)); } } if (lockId != null) { provider.ReleaseItemExclusive(context, sessionId, lockId); } } }