/// <summary> /// Get feed caching operation /// </summary> /// <param name="table">Store table</param> /// <param name="partitionKey">Partition key</param> /// <param name="key">Key for object or feed</param> /// <param name="itemKey">Item key for feed</param> /// <param name="cacheEntity">Cache entity</param> /// <param name="persistentEntity">Persistent store entity</param> /// <param name="checkNotEmpty">A value indicating whether we should check if the feed is not empty in cache during the operation</param> /// <returns>Caching operation</returns> private Operation GetFeedCachingOperation(Table table, string partitionKey, string key, string itemKey, Entity cacheEntity, Entity persistentEntity, bool checkNotEmpty) { Operation cachingOperation = null; if (cacheEntity != null && this.IsCacheEntityExpired(cacheEntity)) { if (persistentEntity != null) { if (table is FeedTable) { cachingOperation = Operation.Replace(table as FeedTable, partitionKey, key, itemKey, persistentEntity as FeedEntity); cachingOperation.Entity.ETag = cacheEntity.ETag; cachingOperation.Entity.CustomETag = persistentEntity.ETag; } } else { if (table is FeedTable) { FeedEntity deleteEntity = new FeedEntity(); deleteEntity.ETag = cacheEntity.ETag; cachingOperation = Operation.Delete(table as FeedTable, partitionKey, key, itemKey, deleteEntity); } } } else if (cacheEntity == null) { if (persistentEntity != null) { if (table is FeedTable) { if (checkNotEmpty) { cachingOperation = Operation.InsertIfNotEmpty(table as FeedTable, partitionKey, key, itemKey, persistentEntity as FeedEntity); cachingOperation.Entity.CustomETag = persistentEntity.ETag; } else { cachingOperation = Operation.Insert(table as FeedTable, partitionKey, key, itemKey, persistentEntity as FeedEntity); cachingOperation.Entity.CustomETag = persistentEntity.ETag; } } } } return(cachingOperation); }
/// <summary> /// Get rollback operation for store operation. /// The rollback operation typically deletes the invalid cache entity /// </summary> /// <param name="preOperation">Pre operation</param> /// <param name="preResult">Result of pre operation</param> /// <returns>Rollback operation</returns> private Operation GetRollbackOperation(Operation preOperation, Result preResult) { Table table = preOperation.Table; Operation rollbackOperation = null; if (table is ObjectTable) { ObjectEntity deleteEntity = new ObjectEntity(); deleteEntity.ETag = preResult.ETag; rollbackOperation = Operation.Delete(preOperation.Table as ObjectTable, preOperation.PartitionKey, preOperation.Key, deleteEntity); } else if (table is FixedObjectTable) { ObjectEntity deleteEntity = new ObjectEntity(); deleteEntity.ETag = preResult.ETag; rollbackOperation = Operation.Delete(preOperation.Table as FixedObjectTable, preOperation.PartitionKey, preOperation.Key, deleteEntity); } else if (table is FeedTable) { if (preResult.EntitiesAffected == 0) { return(null); } // To rollback, we cannot delete the invalidated item in a feed. // This will violate the cache feed invariant. // The best we can do is to expire the item right away so that the next read can correct the cache. // Not the negative sign in the parameter. FeedEntity invalidEntity = this.GenerateInvalidCacheEntity <FeedEntity>(-this.config.CacheExpiryInSeconds); invalidEntity.ETag = preResult.ETag; rollbackOperation = Operation.Replace(preOperation.Table as FeedTable, preOperation.PartitionKey, preOperation.Key, preOperation.ItemKey, invalidEntity); } else if (table is CountTable) { CountEntity deleteEntity = new CountEntity(); deleteEntity.ETag = preResult.ETag; rollbackOperation = Operation.Delete(preOperation.Table as CountTable, preOperation.PartitionKey, preOperation.Key, deleteEntity); } if (rollbackOperation != null) { this.SetupCacheEntityETag(rollbackOperation); } return(rollbackOperation); }
/// <summary> /// Get pre operation for a store operation in default mode. /// The pre operation is typically a cache invalidation operation. /// </summary> /// <param name="operation">Store operation</param> /// <returns>Pre operation for default mode</returns> private Operation GetPreOperation(Operation operation) { Table table = operation.Table; OperationType operationType = operation.OperationType; Operation preOperation = null; if (table is ObjectTable) { if (operationType != OperationType.Insert) { ObjectEntity entity = this.GenerateInvalidCacheEntity <ObjectEntity>(this.config.CacheExpiryInSeconds); preOperation = Operation.InsertOrReplace(table as ObjectTable, operation.PartitionKey, operation.Key, entity); } } else if (table is FixedObjectTable) { if (operationType != OperationType.Insert) { ObjectEntity entity = this.GenerateInvalidCacheEntity <ObjectEntity>(this.config.CacheExpiryInSeconds); preOperation = Operation.InsertOrReplace(table as FixedObjectTable, operation.PartitionKey, operation.Key, entity); } } else if (table is FeedTable) { FeedEntity entity = this.GenerateInvalidCacheEntity <FeedEntity>(this.config.CacheExpiryInSeconds); preOperation = Operation.InsertOrReplaceIfNotLast(table as FeedTable, operation.PartitionKey, operation.Key, operation.ItemKey, entity); } else if (table is CountTable) { if (operationType != OperationType.Insert) { preOperation = Operation.InsertOrReplace(table as CountTable, operation.PartitionKey, operation.Key, 0); preOperation.Entity = this.GenerateInvalidCacheEntity <CountEntity>(this.config.CacheExpiryInSeconds); } } if (preOperation != null) { this.SetupCacheEntityETag(preOperation); } return(preOperation); }
/// <summary> /// Get post operation for a store operation in default mode. /// The post operation typically makes the cache consistent with the store. /// There are exceptions: For merge operations, we simple delete the cache entry /// A read operation on the whole item should bring it back to the cache. /// </summary> /// <param name="operation">Store operation</param> /// <param name="preResult">Result of pre operation</param> /// <param name="result">Result of operations</param> /// <returns>Post operation</returns> private Operation GetPostOperation(Operation operation, Result preResult, Result result) { Table table = operation.Table; OperationType operationType = operation.OperationType; Entity entity = operation.Entity; Operation postOperation = null; if (table is ObjectTable) { switch (operationType) { case OperationType.Insert: postOperation = Operation.Insert(table as ObjectTable, operation.PartitionKey, operation.Key, entity as ObjectEntity); postOperation.Entity.CustomETag = result.ETag; return(postOperation); case OperationType.Replace: case OperationType.InsertOrReplace: postOperation = Operation.Replace(table as ObjectTable, operation.PartitionKey, operation.Key, entity as ObjectEntity); postOperation.Entity.ETag = preResult.ETag; postOperation.Entity.CustomETag = result.ETag; return(postOperation); case OperationType.Delete: case OperationType.DeleteIfExists: case OperationType.Merge: case OperationType.InsertOrMerge: ObjectEntity deleteEntity = new ObjectEntity(); deleteEntity.ETag = preResult.ETag; return(Operation.Delete(table as ObjectTable, operation.PartitionKey, operation.Key, deleteEntity)); } } else if (table is FixedObjectTable) { switch (operationType) { case OperationType.Insert: postOperation = Operation.Insert(table as FixedObjectTable, operation.PartitionKey, operation.Key, entity as ObjectEntity); postOperation.Entity.CustomETag = result.ETag; return(postOperation); case OperationType.Replace: case OperationType.InsertOrReplace: postOperation = Operation.Replace(table as FixedObjectTable, operation.PartitionKey, operation.Key, entity as ObjectEntity); postOperation.Entity.ETag = preResult.ETag; postOperation.Entity.CustomETag = result.ETag; return(postOperation); case OperationType.Delete: case OperationType.DeleteIfExists: ObjectEntity deleteEntity = new ObjectEntity(); deleteEntity.ETag = preResult.ETag; return(Operation.Delete(table as FixedObjectTable, operation.PartitionKey, operation.Key, deleteEntity)); } } else if (table is FeedTable) { if (preResult.EntitiesAffected == 0) { return(null); } switch (operationType) { case OperationType.Insert: case OperationType.Replace: case OperationType.InsertOrReplace: postOperation = Operation.Replace(table as FeedTable, operation.PartitionKey, operation.Key, operation.ItemKey, entity as FeedEntity); postOperation.Entity.ETag = preResult.ETag; postOperation.Entity.CustomETag = result.ETag; return(postOperation); case OperationType.Delete: case OperationType.DeleteIfExists: FeedEntity deleteEntity = new FeedEntity(); deleteEntity.ETag = preResult.ETag; return(Operation.Delete(table as FeedTable, operation.PartitionKey, operation.Key, operation.ItemKey, deleteEntity)); } } else if (table is CountTable) { switch (operationType) { case OperationType.Insert: postOperation = Operation.Insert(table as CountTable, operation.PartitionKey, operation.Key, (entity as CountEntity).Count); postOperation.Entity.CustomETag = result.ETag; return(postOperation); case OperationType.Replace: case OperationType.InsertOrReplace: postOperation = Operation.Replace(table as CountTable, operation.PartitionKey, operation.Key, entity as CountEntity); postOperation.Entity.ETag = preResult.ETag; postOperation.Entity.CustomETag = result.ETag; return(postOperation); case OperationType.Increment: case OperationType.InsertOrIncrement: postOperation = Operation.Replace(table as CountTable, operation.PartitionKey, operation.Key, new CountEntity() { Count = (double)result.Value }); postOperation.Entity.ETag = preResult.ETag; postOperation.Entity.CustomETag = result.ETag; break; case OperationType.Delete: case OperationType.DeleteIfExists: CountEntity deleteEntity = new CountEntity(); deleteEntity.ETag = preResult.ETag; return(Operation.Delete(table as CountTable, operation.PartitionKey, operation.Key, deleteEntity)); } } return(postOperation); }
/// <summary> /// Validate feed table parameters and throw exceptions /// </summary> /// <param name="table">Feed table</param> /// <param name="partitionKey">Partition key</param> /// <param name="feedKey">Feed key</param> /// <param name="itemKey">Item key</param> /// <param name="entity">Object entity</param> private static void ValidateFeedTableParameters(Table table, string partitionKey, string feedKey, string itemKey, FeedEntity entity) { ValidateFeedTableParameters(table, partitionKey, feedKey, itemKey); if (entity == null) { throw new ArgumentNullException("Entity cannot be null"); } }
/// <summary> /// Insert if not empty operation on feed table /// </summary> /// <param name="table">Feed table</param> /// <param name="partitionKey">Partition key for feed</param> /// <param name="feedKey">Key for feed</param> /// <param name="itemKey">Item key for feed entity</param> /// <param name="entity">Feed entity</param> /// <returns>Table operation</returns> internal static Operation InsertIfNotEmpty(FeedTable table, string partitionKey, string feedKey, string itemKey, FeedEntity entity) { ValidateFeedTableParameters(table, partitionKey, feedKey, itemKey, entity); return(new Operation() { Table = table, OperationType = OperationType.InsertIfNotEmpty, PartitionKey = partitionKey, Key = feedKey, ItemKey = itemKey, Entity = entity.Clone() }); }
/// <summary> /// Replace operation on feed table /// </summary> /// <param name="table">Feed table</param> /// <param name="partitionKey">Partition key for feed</param> /// <param name="feedKey">Key for feed</param> /// <param name="itemKey">Item key for feed entity</param> /// <param name="entity">Feed entity</param> /// <returns>Table operation</returns> public static Operation Replace(FeedTable table, string partitionKey, string feedKey, string itemKey, FeedEntity entity) { ValidateFeedTableParameters(table, partitionKey, feedKey, itemKey, entity); return(new Operation() { Table = table, OperationType = OperationType.Replace, PartitionKey = partitionKey, Key = feedKey, ItemKey = itemKey, Entity = entity.Clone() }); }
/// <summary> /// Delete operation on feed table /// </summary> /// <param name="table">Feed table</param> /// <param name="partitionKey">Partition key for feed</param> /// <param name="feedKey">Key for feed</param> /// <param name="itemKey">Item key for feed entity</param> /// <param name="entity">Feed entity</param> /// <returns>Table operation</returns> public static Operation Delete(FeedTable table, string partitionKey, string feedKey, string itemKey, FeedEntity entity = null) { ValidateFeedTableParameters(table, partitionKey, feedKey, itemKey); return(new Operation() { Table = table, OperationType = OperationType.Delete, PartitionKey = partitionKey, Key = feedKey, ItemKey = itemKey, Entity = entity != null?entity.Clone() : null }); }