public override void CreateUninitializedItem(HttpContext context, string id, int timeout) { SecUtility.CheckParameter(ref id, true, true, false, ProviderConfiguration.MaxStringPropertySizeInChars, "id"); if (timeout < 0) { throw new ArgumentException("Parameter timeout must be a non-negative integer!"); } try { TableServiceContext svc = CreateDataServiceContext(); var session = new SessionRow(id, applicationName) { Lock = 0, Initialized = false, Id = id, Timeout = timeout, ExpiresUtc = DateTime.UtcNow.AddMinutes(timeout) }; svc.AddObject(tableName, session); svc.SaveChangesWithRetries(); } catch (InvalidOperationException e) { var innerEx = e.InnerException as DataServiceClientException; if (innerEx != null && innerEx.StatusCode == (int) HttpStatusCode.Conflict) { // the data already exists so we can return because we are reusing a session return; } throw new ProviderException("Error accessing the data store.", e); } }
public override void CreateUninitializedItem(HttpContext context, string id, int timeout) { SecUtility.CheckParameter(ref id, true, true, false, ProviderConfiguration.MaxStringPropertySizeInChars, "id"); if (timeout < 0) { throw new ArgumentException("Parameter timeout must be a non-negative integer!"); } try { TableServiceContext svc = CreateDataServiceContext(); var session = new SessionRow(id, applicationName) { Lock = 0, Initialized = false, Id = id, Timeout = timeout, ExpiresUtc = DateTime.UtcNow.AddMinutes(timeout) }; svc.AddObject(tableName, session); svc.SaveChangesWithRetries(); } catch (InvalidOperationException e) { var innerEx = e.InnerException as DataServiceClientException; if (innerEx != null && innerEx.StatusCode == (int)HttpStatusCode.Conflict) { // the data already exists so we can return because we are reusing a session return; } throw new ProviderException("Error accessing the data store.", e); } }
public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item) { SecUtility.CheckParameter(ref id, true, true, false, ProviderConfiguration.MaxStringPropertySizeInChars, "id"); try { TableServiceContext svc = CreateDataServiceContext(); SessionRow session = GetSession(id, svc); if (session == null) { return; } if (session.Lock != (int)lockId) { return; } svc.DeleteObject(session); svc.SaveChangesWithRetries(); } catch (InvalidOperationException e) { throw new ProviderException("Error accessing the data store!", e); } // delete associated blobs try { IEnumerable <IListBlobItem> e = blobProvider.ListBlobs(GetBlobNamePrefix(id)); if (e == null) { return; } IEnumerator <IListBlobItem> props = e.GetEnumerator(); if (props == null) { return; } while (props.MoveNext()) { if (props.Current != null) { if (!blobProvider.DeleteBlob(props.Current.Uri.ToString())) { // ignore this; it is possible that another thread could try to delete the blob // at the same time } } } } catch (Exception e) { throw new ProviderException("Error accessing blob storage.", e); } }
private static void ReleaseItemExclusive(TableServiceContext svc, SessionRow session, object lockId) { if ((int)lockId != session.Lock) { return; } session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout); session.Locked = false; svc.UpdateObject(session); svc.SaveChangesWithRetries(); }
public override void ResetItemTimeout(HttpContext context, string id) { SecUtility.CheckParameter(ref id, true, true, false, ProviderConfiguration.MaxStringPropertySizeInChars, "id"); providerRetry(() => { TableServiceContext svc = CreateDataServiceContext(); SessionRow session = GetSession(id, svc); session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout); svc.UpdateObject(session); svc.SaveChangesWithRetries(); }); }
public override void ReleaseItemExclusive(HttpContext context, string id, object lockId) { SecUtility.CheckParameter(ref id, true, true, false, ProviderConfiguration.MaxStringPropertySizeInChars, "id"); try { TableServiceContext svc = CreateDataServiceContext(); SessionRow session = GetSession(id, svc); ReleaseItemExclusive(svc, session, lockId); } catch (InvalidOperationException e) { throw new ProviderException("Error accessing the data store!", e); } }
private static void ReleaseItemExclusive(TableServiceContext svc, SessionRow session, object lockId) { if ((int) lockId != session.Lock) { return; } session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout); session.Locked = false; svc.UpdateObject(session); svc.SaveChangesWithRetries(); }
public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem) { SecUtility.CheckParameter(ref id, true, true, false, ProviderConfiguration.MaxStringPropertySizeInChars, "id"); providerRetry(() => { TableServiceContext svc = CreateDataServiceContext(); SessionRow session; if (!newItem) { session = GetSession(id, svc); if (session == null || session.Lock != (int) lockId) { return; } } else { session = new SessionRow(id, applicationName) { Lock = 1, LockDateUtc = DateTime.UtcNow }; } session.Initialized = true; session.Timeout = item.Timeout; session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout); session.Locked = false; // yes, we always create a new blob here session.BlobName = GetBlobNamePrefix(id) + Guid.NewGuid().ToString("N"); // Serialize the session and write the blob byte[] items, statics; SerializeSession(item, out items, out statics); string serializedItems = Convert.ToBase64String(items); string serializedStatics = Convert.ToBase64String(statics); var output = new MemoryStream(); var writer = new StreamWriter(output); try { writer.WriteLine(serializedItems); writer.WriteLine(serializedStatics); writer.Flush(); // for us, it shouldn't matter whether newItem is set to true or false // because we always create the entire blob and cannot append to an // existing one blobProvider.UploadStream(session.BlobName, output); writer.Close(); output.Close(); } catch (Exception e) { if (!newItem) { ReleaseItemExclusive(svc, session, lockId); } throw new ProviderException("Error accessing the data store.", e); } finally { writer.Close(); output.Close(); } if (newItem) { svc.AddObject(tableName, session); svc.SaveChangesWithRetries(); } else { // Unlock the session and save changes ReleaseItemExclusive(svc, session, lockId); } }); }
// we don't use the retry policy itself in this function because out parameters are not well handled by // retry policies private SessionStateStoreData GetSession(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions, bool exclusive) { SecUtility.CheckParameter(ref id, true, true, false, ProviderConfiguration.MaxStringPropertySizeInChars, "id"); SessionRow session = null; int curRetry = 0; bool retry; // Assign default values to out parameters locked = false; lockId = null; lockAge = TimeSpan.Zero; actions = SessionStateActions.None; do { retry = false; try { TableServiceContext svc = CreateDataServiceContext(); session = GetSession(id, svc); // Assign default values to out parameters locked = false; lockId = null; lockAge = TimeSpan.Zero; actions = SessionStateActions.None; // if the blob does not exist, we return null // ASP.NET will call the corresponding method for creating the session if (session == null) { return(null); } if (session.Initialized == false) { actions = SessionStateActions.InitializeItem; } session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout); if (exclusive) { if (!session.Locked) { if (session.Lock == Int32.MaxValue) { session.Lock = 0; } else { session.Lock++; } session.LockDateUtc = DateTime.UtcNow; } lockId = session.Lock; locked = session.Locked; session.Locked = true; } lockAge = DateTime.UtcNow.Subtract(session.LockDateUtc); lockId = session.Lock; if (locked) { return(null); } // let's try to write this back to the data store // in between, someone else could have written something to the store for the same session // we retry a number of times; if all fails, we throw an exception svc.UpdateObject(session); svc.SaveChangesWithRetries(); } catch (InvalidOperationException e) { // precondition fails indicates problems with the status code // not found means we have had the session deleted from under us if (e.InnerException is DataServiceClientException && (e.InnerException as DataServiceClientException).StatusCode == (int)HttpStatusCode.PreconditionFailed) { retry = true; } else if (e.InnerException is DataServiceClientException && (e.InnerException as DataServiceClientException).StatusCode == (int)HttpStatusCode.NotFound) { return(null); } else { throw new ProviderException("Error accessing the data store.", e); } } } while (retry && curRetry++ < NumRetries); // ok, now we have successfully written back our state // we can now read the blob // we do not need to care about read/write locking when accessing the // blob because each time we write a new session we create a new blob with a different name if (actions == SessionStateActions.InitializeItem || string.IsNullOrEmpty(session.BlobName)) { // Return an empty SessionStateStoreData return(new SessionStateStoreData( new SessionStateItemCollection(), SessionStateUtility.GetSessionStaticObjects(context), session.Timeout)); } try { BlobProperties properties; using (MemoryStream stream = blobProvider.GetBlobContent(session.BlobName, out properties)) using (var reader = new StreamReader(stream)) { // Read Items, StaticObjects, and Timeout from the file byte[] items = Convert.FromBase64String(reader.ReadLine()); byte[] statics = Convert.FromBase64String(reader.ReadLine()); int timeout = session.Timeout; // Deserialize the session return(DeserializeSession(items, statics, timeout)); } } catch (Exception e) { throw new ProviderException("Couldn't read session blob!", e); } }
public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem) { SecUtility.CheckParameter(ref id, true, true, false, ProviderConfiguration.MaxStringPropertySizeInChars, "id"); providerRetry(() => { TableServiceContext svc = CreateDataServiceContext(); SessionRow session; if (!newItem) { session = GetSession(id, svc); if (session == null || session.Lock != (int)lockId) { return; } } else { session = new SessionRow(id, applicationName) { Lock = 1, LockDateUtc = DateTime.UtcNow }; } session.Initialized = true; session.Timeout = item.Timeout; session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout); session.Locked = false; // yes, we always create a new blob here session.BlobName = GetBlobNamePrefix(id) + Guid.NewGuid().ToString("N"); // Serialize the session and write the blob byte[] items, statics; SerializeSession(item, out items, out statics); string serializedItems = Convert.ToBase64String(items); string serializedStatics = Convert.ToBase64String(statics); var output = new MemoryStream(); var writer = new StreamWriter(output); try { writer.WriteLine(serializedItems); writer.WriteLine(serializedStatics); writer.Flush(); // for us, it shouldn't matter whether newItem is set to true or false // because we always create the entire blob and cannot append to an // existing one blobProvider.UploadStream(session.BlobName, output); writer.Close(); output.Close(); } catch (Exception e) { if (!newItem) { ReleaseItemExclusive(svc, session, lockId); } throw new ProviderException("Error accessing the data store.", e); } finally { writer.Close(); output.Close(); } if (newItem) { svc.AddObject(tableName, session); svc.SaveChangesWithRetries(); } else { // Unlock the session and save changes ReleaseItemExclusive(svc, session, lockId); } }); }