public IEnumerator GetEnumerator() { // we are going to return the default enumerator implementation of ConcurrentDictionary, but // there is one snafu. The keys that have not been read yet are of type // NotYetDeserializedPlaceholderValue, we need to deserialize them now. lock (this.enumLock) { foreach (KeyValuePair <string, object> itm in this.Items) { if (itm.Value is NotYetDeserializedPlaceholderValue) { this.MemoizedDeserializeGet(itm.Key); // deserialize the value } } try { return(this.Items.GetEnumerator()); } catch (Exception exc) { RedisSessionConfig.LogSessionException(exc); } } return(new ConcurrentDictionary <string, object>().GetEnumerator()); }
/// <summary> /// Gets a Session from Redis, indicating a non-exclusive lock on the Session. Note that GetItemExclusive /// calls this method internally, meaning we do not support locks at all retrieving the Session. /// </summary> /// <param name="context">The HttpContext of the current request</param> /// <param name="id">The Session Id, which is the key name in Redis</param> /// <param name="locked">Whether or not the Session is locked to exclusive access for a single request /// thread</param> /// <param name="lockAge">The age of the lock</param> /// <param name="lockId">The object used to lock the Session</param> /// <param name="actions">Whether or not to initialize the Session (never)</param> /// <returns>The Session objects wrapped in a SessionStateStoreData object</returns> public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions) { locked = false; lockAge = new TimeSpan(0); lockId = null; actions = SessionStateActions.None; try { return(new SessionStateStoreData(GetSessionItems(context, id), SessionStateUtility.GetSessionStaticObjects(context), Convert.ToInt32(RedisSessionConfig.SessionTimeout.TotalMinutes))); } catch (Exception e) { RedisSessionConfig.LogSessionException(e); } return(CreateNewStoreData(context, Convert.ToInt32(RedisSessionConfig.SessionTimeout.TotalMinutes))); }
/// <summary> /// Returns a class that allows iteration over the objects in the Session collection that have changed /// since being pulled from Redis. Each key is checked for being all of the following: key was /// accessed, key was not removed or set to null, if the key holds the same object reference as it /// did when pulled from redis, then the current object at the reference address is serialized and /// the serialized string is compared with the serialized string that was originally pulled from /// Redis. The last condition is to ensure Dirty checking is correct, i.e. /// ((List<string>)Session["a"]).Add("a"); /// does not change what Session["a"] refers to, but it does change the object and we need to write /// that back to Redis at the end. /// </summary> /// <returns>an IEnumerator that allows iteration over the changed elements of the Session.</returns> public IEnumerable <KeyValuePair <string, string> > GetChangedObjectsEnumerator() { var changedObjs = new List <KeyValuePair <string, string> >(); lock (enumLock) { try { // for items that have definitely changed (ints, strings, value types and // reference types whose refs have changed), add to the resulting list // and reset their serialized raw data foreach (KeyValuePair <string, ActionAndValue> changeData in ChangedKeysDict) { if (changeData.Value is SetValue) { UpdateSerializedDataForChanges(changedObjs, changeData); } else if (changeData.Value is DeleteValue) { UpdateSerializedDataForDeletes(changedObjs, changeData); } } // check each key that was accessed for changes foreach (KeyValuePair <string, object> itm in this.Items) { try { UpdateChangedObjectList(changedObjs, itm); } catch (Exception) { } } } catch (Exception enumExc) { RedisSessionConfig.LogSessionException(enumExc); } } return(changedObjs); }
/// <summary> /// Gets a hash from Redis and passes it to the constructor of RedisSessionStateItemCollection /// </summary> /// <param name="redisKey">The key of the Redis hash</param> /// <param name="context">The HttpContext of the current web request</param> /// <returns>An instance of RedisSessionStateItemCollection, may be empty if Redis call fails</returns> public static RedisSessionStateItemCollection GetItemFromRedis(string redisKey, HttpContextBase context, TimeSpan expirationTimeout) { RedisConnectionWrapper rConnWrap = RedisSessionStateStoreProvider.RedisConnWrapperFromContext(context); IDatabase redisConn = rConnWrap.GetConnection(redisKey); try { HashEntry[] redisData = redisConn.HashGetAll(redisKey); redisConn.KeyExpire(redisKey, expirationTimeout, CommandFlags.FireAndForget); return(new RedisSessionStateItemCollection(redisData, redisKey)); } catch (Exception e) { RedisSessionConfig.LogSessionException(e); } return(new RedisSessionStateItemCollection()); }
static void EnsureLocalCacheFreshness(object sender, ElapsedEventArgs e) { try { rwlock.EnterWriteLock(); cacheFreshnessTimer.Stop(); LocalSessionInfo removed; foreach (string expKey in GetPurgableKeys()) { localCache.TryRemove(expKey, out removed); } } catch (Exception sharedDictExc) { RedisSessionConfig.LogSessionException(sharedDictExc); } finally { rwlock.ExitWriteLock(); cacheFreshnessTimer.Start(); } }
/// <summary> /// Checks if any items have changed in the Session, and stores the results to Redis /// </summary> /// <param name="context">The HttpContext of the current web request</param> /// <param name="id">The Session Id and Redis key name</param> /// <param name="item">The Session properties</param> /// <param name="lockId">The object used to exclusively lock the Session (there shouldn't be one)</param> /// <param name="newItem">Whether or not the Session was created in this HttpContext</param> public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem) { try { string currentRedisHashId = RedisHashIdFromSessionId(new HttpContextWrapper(context), id); LocalSharedSessionDictionary sharedSessDict = new LocalSharedSessionDictionary(); // we were unable to pull it from shared cache, meaning either this is the first request or // something went wrong with the local cache. We still have all the parts needed to write // to redis, however, by looking at SessionStateStoreData passed in from the Session module // and the current hash key provided by the id parameter. var redisItems = sharedSessDict.GetSessionForEndRequest(currentRedisHashId) ?? (RedisSessionStateItemCollection)item.Items; if (redisItems != null) { SerializeToRedis(new HttpContextWrapper(context), redisItems, currentRedisHashId, RedisSessionConfig.SessionTimeout); } } catch (Exception e) { RedisSessionConfig.LogSessionException(e); } }