using System.Linq; using Couchbase; using System; using System.Collections.Generic; using Enyim.Caching.Memcached; namespace Sony.Filtr.DistributedCaching { public class DistributedCacheHandler { const int GroupCount = 50; private readonly string _cachePrefix; private readonly ICouchbaseClient _couchbaseClient; public DistributedCacheHandler(string cachePrefix, ICouchbaseClient couchbaseClient) { _cachePrefix = cachePrefix; _couchbaseClient = couchbaseClient; } public object Get(string key) { var obj = _couchbaseClient.Get(GetKey(key)); return obj; } public IDictionary Get(IEnumerable keys) { var objs = _couchbaseClient.ExecuteGet(keys.Select(GetKey)); return objs.ToDictionary(k => k.Key.Replace(_cachePrefix, string.Empty), v => v.Value.Value); } public List GetList(string key) { try { var obj = _couchbaseClient.Get(GetKey(key)); if (obj is ToLargeObject) { return GetCacheListItems(key, (ToLargeObject)obj, GroupCount); } } catch (Exception) { return null; } return null; } private List GetCacheListItems(string key, ToLargeObject obj, int groupCount) { var itemCount = obj.Count; List items = new List(itemCount); var numGroups = (itemCount / groupCount); var keys = Enumerable.Range(0, numGroups + 1).Select(i => GetToLongKey(key, i)).ToList(); var gets = _couchbaseClient.ExecuteGet(keys); foreach(var localKey in keys) { if (!gets.ContainsKey(localKey)) return null; //If we can't fetch an individual group we can't recreate the entire array so we fail and consider it a miss if (!gets[localKey].HasValue) return null; //If we can't fetch an individual group we can't recreate the entire array so we fail and consider it a miss var localList = gets[localKey].Value as IList; if (localList == null) return null; //If we can't fetch an individual group we can't recreate the entire array so we fail and consider it a miss items.AddRange(localList); } return items; } public void PutList(string key, IList list, DateTime? expiresAt = null) { if (expiresAt.HasValue) _couchbaseClient.Store(StoreMode.Set, GetKey(key), new ToLargeObject(list.Count), expiresAt.Value); else _couchbaseClient.Store(StoreMode.Set, GetKey(key), new ToLargeObject(list.Count)); var defaultGroupCount = GroupCount; PutListItems(key, list, defaultGroupCount, expiresAt); } public void PutSpecial(string key, IList list, DateTime? expiresAt = null, int groupCount = GroupCount) { var cacheItem = new CacheItemTooLargeObject(list.Count) { GroupCount = groupCount, UtcCacheTime = DateTime.UtcNow }; if (expiresAt.HasValue) _couchbaseClient.Store(StoreMode.Set, GetKey(key), cacheItem, expiresAt.Value); else _couchbaseClient.Store(StoreMode.Set, GetKey(key), cacheItem); PutListItems(key, list, groupCount, expiresAt); } public SpecialListCacheItem GetSpecialList(string key) { var obj = _couchbaseClient.Get(GetKey(key)) as CacheItemTooLargeObject; if (obj != null) { var items = GetCacheListItems(key, obj, obj.GroupCount); if (items == null) return null; return new SpecialListCacheItem() { UtcCacheTime = obj.UtcCacheTime, Items = items, }; } return null; } private void PutListItems(string key, IList list, int groupCount, DateTime? expiresAt) { for(int i = 0; i <= (list.Count / groupCount); i++) { var startIndex = i * groupCount; var endIndex = (i + 1) * groupCount; IList tempList = new List(groupCount); for(var j = startIndex; j < endIndex; j++) { if (j < list.Count) tempList.Add(list[j]); } if (expiresAt.HasValue) _couchbaseClient.Store(StoreMode.Set, GetToLongKey(key, i), tempList, expiresAt.Value); else _couchbaseClient.Store(StoreMode.Set, GetToLongKey(key, i), tempList); } } public void Put(string key, object value) { _couchbaseClient.Store(StoreMode.Set, GetKey(key), value); } private string GetToLongKey(string key, int count) { return _cachePrefix + "Range:" + key + "|" + count; } public void Put(string key, object value, DateTime expiresAt) { _couchbaseClient.Store(StoreMode.Set, GetKey(key), value, expiresAt); } public void Remove(string key) { _couchbaseClient.Remove(GetKey(key)); } private string GetKey(string key) { return _cachePrefix + key; } } }