/// <summary> /// SetItem T By Key /// </summary> /// <typeparam name="T">Type Of Cached Value</typeparam> /// <param name="key">Key Lookup</param> /// <param name="litter">Item T To Be Cached</param> /// <returns>Success True|False (For Each Cache)</returns> public async Task <StorageResult[]> SetItem <T>(string key, LitterBoxItem <T> litter) { if (string.IsNullOrWhiteSpace(key)) { this.RaiseException(new ArgumentException($"{nameof(this.SetItem)}=>{nameof(key)} Cannot Be NullOrWhiteSpace")); return(null); } if (litter == null) { this.RaiseException(new ArgumentException($"{nameof(this.SetItem)}=>{nameof(litter)} Cannot Be Null")); return(null); } var results = new StorageResult[this._caches.Length]; for (var i = 0; i < this._caches.Length; i++) { var cache = this._caches[i]; var success = await cache.SetItem(key, litter).ConfigureAwait(false); results[i] = new StorageResult { CacheType = cache.GetType(), IsSuccessful = success }; } return(results); }
/// <summary> /// Initializes a new instance of the <see cref="CacheItem{T}" /> class. /// </summary> /// <param name="key">Key/ID Of Document</param> /// <param name="litter">Existing T LitterBoxItem</param> /// <param name="partitionKey">The default partition Key</param> public CacheItem(string key, LitterBoxItem <T> litter, string partitionKey = "none") { this.ID = key; this.Value = litter.Value; this.Created = litter.Created; this.TimeToLive = litter.TimeToLive; this.TimeToRefresh = litter.TimeToRefresh; this.PartitionKey = partitionKey; }
/// <summary> /// SetItem T (Fire Forget) By Key, Generator, TimeToRefresh, TimeToLive /// </summary> /// <typeparam name="T">Type Of Cached Value</typeparam> /// <param name="key">Key Lookup</param> /// <param name="generator">Generator Action</param> /// <param name="timeToRefresh">How Long After Creation To Be Considered "Good"</param> /// <param name="timeToLive">How Long After Creation To Auto-Delete</param> public void SetItemFireAndForget <T>(string key, Func <Task <T> > generator, TimeSpan?timeToRefresh = null, TimeSpan?timeToLive = null) { if (string.IsNullOrWhiteSpace(key)) { this.RaiseException(new ArgumentException($"{nameof(this.SetItemFireAndForget)}=>{nameof(key)} Cannot Be NullOrWhiteSpace")); return; } if (generator == null) { this.RaiseException(new ArgumentException($"{nameof(this.SetItemFireAndForget)}=>{nameof(generator)} Cannot Be Null")); return; } if (!this._inProcess.TryAdd(key, true)) { return; } Task.Run( async() => { int?toLive = null; int?toRefresh = null; if (timeToLive != null) { toLive = (int)((TimeSpan)timeToLive).TotalSeconds; } if (timeToRefresh != null) { toRefresh = (int)((TimeSpan)timeToRefresh).TotalSeconds; } try { var item = await generator().ConfigureAwait(false); if (item != null) { var litter = new LitterBoxItem <T> { Value = item, TimeToLive = toLive, TimeToRefresh = toRefresh }; await this.SetItem(key, litter).ConfigureAwait(false); } } catch (Exception ex) { this.RaiseException(ex); } }).ConfigureAwait(false); }
/// <summary> /// SetItem T By Key /// </summary> /// <typeparam name="T">Type Of Cached Value</typeparam> /// <param name="key">Key Lookup</param> /// <param name="original">Item T To Be Cached</param> /// <returns>Success True|False</returns> public async Task <bool> SetItem <T>(string key, LitterBoxItem <T> original) { if (string.IsNullOrWhiteSpace(key)) { this.RaiseException(new ArgumentException($"{nameof(this.SetItem)}=>{nameof(key)} Cannot Be NullOrWhiteSpace")); return(false); } if (original == null) { this.RaiseException(new ArgumentException($"{nameof(this.SetItem)}=>{nameof(original)} Cannot Be Null")); return(false); } var success = false; // when using multi-caching; modifying the TTR and TTL on the object collides with other caches var litter = original.Clone(); litter.TimeToRefresh = litter.TimeToRefresh ?? (int)this._configuration.DefaultTimeToRefresh.TotalSeconds; litter.TimeToLive = litter.TimeToLive ?? (int)this._configuration.DefaultTimeToLive.TotalSeconds; try { var uri = UriFactory.CreateDocumentCollectionUri(this._configuration.Database, this._configuration.Collection); var document = new CacheItem <T>(key, litter, this._configuration.PartitionKey); var requestOptions = new RequestOptions { ConsistencyLevel = ConsistencyLevel.Eventual, DisableRUPerMinuteUsage = true, PartitionKey = new PartitionKey(this._configuration.PartitionKey) }; var result = await this.GetPooledConnection <DocumentDBConnection>().Cache.UpsertDocumentAsync(uri, document, requestOptions).ConfigureAwait(false); if (result != null) { success = true; } } catch (Exception ex) { this.RaiseException(ex); } this._inProcess.TryRemove(key, out var itemSet); return(success); }
/// <summary> /// SetItem T By Key /// </summary> /// <typeparam name="T">Type Of Cached Value</typeparam> /// <param name="key">Key Lookup</param> /// <param name="original">Item T To Be Cached</param> /// <returns>Success True|False</returns> public async Task <bool> SetItem <T>(string key, LitterBoxItem <T> original) { var success = false; if (string.IsNullOrWhiteSpace(key)) { this.RaiseException(new ArgumentException($"{nameof(this.SetItem)}=>{nameof(key)} Cannot Be NullOrWhiteSpace")); return(success); } if (original == null) { this.RaiseException(new ArgumentException($"{nameof(this.SetItem)}=>{nameof(original)} Cannot Be Null")); return(success); } // when using multi-caching; modifying the TTR and TTL on the object collides with other caches var litter = original.Clone(); litter.TimeToRefresh = litter.TimeToRefresh ?? (int)this._configuration.DefaultTimeToRefresh.TotalSeconds; litter.TimeToLive = litter.TimeToLive ?? (int)this._configuration.DefaultTimeToLive.TotalSeconds; try { var json = Utilities.Serialize(litter); if (!string.IsNullOrWhiteSpace(json)) { var connection = this.GetPooledConnection <RedisConnection>(); var hashSetSuccess = await connection.Cache.HashSetAsync(key, "litter", Compression.Zip(json)).ConfigureAwait(false); var keyExpireSuccess = await connection.Cache.KeyExpireAsync(key, TimeSpan.FromSeconds((double)litter.TimeToLive)).ConfigureAwait(false); success = hashSetSuccess & keyExpireSuccess; } } catch (Exception ex) { this.RaiseException(ex); } this._inProcess.TryRemove(key, out var removed); return(success); }
/// <summary> /// SetItem T (Fire Forget) By Key, LitterBoxItem T /// </summary> /// <typeparam name="T">Type Of Cached Value</typeparam> /// <param name="key">Key Lookup</param> /// <param name="litter">Item T To Be Cached</param> public void SetItemFireAndForget <T>(string key, LitterBoxItem <T> litter) { if (string.IsNullOrWhiteSpace(key)) { this.RaiseException(new ArgumentException($"{nameof(this.SetItemFireAndForget)}=>{nameof(key)} Cannot Be NullOrWhiteSpace")); return; } if (litter == null) { this.RaiseException(new ArgumentException($"{nameof(this.SetItemFireAndForget)}=>{nameof(litter)} Cannot Be Null")); return; } if (!this._inProcess.TryAdd(key, true)) { return; } Task.Run(() => this.SetItem(key, litter)).ConfigureAwait(false); }
/// <summary> /// SetItem T (Fire Forget) By Key, LitterBoxItem T /// </summary> /// <typeparam name="T">Type Of Cached Value</typeparam> /// <param name="key">Key Lookup</param> /// <param name="litter">Item T To Be Cached</param> public void SetItemFireAndForget <T>(string key, LitterBoxItem <T> litter) { if (string.IsNullOrWhiteSpace(key)) { this.RaiseException(new ArgumentException($"{nameof(this.SetItemFireAndForget)}=>{nameof(key)} Cannot Be NullOrWhiteSpace")); return; } if (litter == null) { this.RaiseException(new ArgumentException($"{nameof(this.SetItemFireAndForget)}=>{nameof(litter)} Cannot Be Null")); return; } Task.Run( () => { foreach (var cache in this._caches) { cache.SetItemFireAndForget(key, litter); } }).ConfigureAwait(false); }
/// <summary> /// GetItem T By Key, Generator, TimeToRefresh, TimeToLive /// </summary> /// <typeparam name="T">Type Of Cached Value</typeparam> /// <param name="key">Key Lookup</param> /// <param name="generator">Generator Action If Not Found</param> /// <param name="timeToRefresh">How Long After Creation To Be Considered "Good"</param> /// <param name="timeToLive">How Long After Creation To Auto-Delete</param> /// <returns>TenancyItem => LitterBoxItem T</returns> public async Task <LitterBoxItem <T> > GetItem <T>(string key, Func <Task <T> > generator, TimeSpan?timeToRefresh = null, TimeSpan?timeToLive = null) { if (string.IsNullOrWhiteSpace(key)) { this.RaiseException(new ArgumentException($"{nameof(this.GetItem)}=>{nameof(key)} Cannot Be NullOrWhiteSpace")); return(null); } if (generator == null) { this.RaiseException(new ArgumentException($"{nameof(this.GetItem)}=>{nameof(generator)} Cannot Be Null")); return(null); } var foundIndex = 0; LitterBoxItem <T> result = null; for (var i = 0; i < this._caches.Length; i++) { var cache = this._caches[i]; result = await cache.GetItem <T>(key).ConfigureAwait(false); if (result != null) { foundIndex = i; result.Key = key; result.CacheType = cache.GetType(); // if the item is stale; refresh this cache only at this time if (result.IsStale()) { cache.SetItemFireAndForget(key, generator, timeToRefresh, timeToLive); } break; } } int?toLive = null; int?toRefresh = null; if (timeToLive != null) { toLive = (int)((TimeSpan)timeToLive).TotalSeconds; } if (timeToRefresh != null) { toRefresh = (int)((TimeSpan)timeToRefresh).TotalSeconds; } if (result == null) { foundIndex = this._caches.Length; try { result = new LitterBoxItem <T> { Value = await generator().ConfigureAwait(false), TimeToLive = toLive, TimeToRefresh = toRefresh }; } catch (Exception ex) { this.RaiseException(ex); } } if (result != null) { for (var i = 0; i < foundIndex; i++) { var cache = this._caches[i]; // if the result was stale and was also found in a cache // refresh all caches from until the cache it was found in if (result.IsStale() && result.CacheType != null) { cache.SetItemFireAndForget(key, generator, timeToRefresh, timeToLive); } else { // else we can just save the item into the cache and not regenerate cache.SetItemFireAndForget(key, result); } } } return(result); }