/// <summary> /// Sets the cache instance for the client. /// </summary> /// <param name="cache">The cache instance to set.</param> /// <param name="cachePrefix">The cache prefix for this instance.</param> /// <param name="auto_invalidate">if set to <c>true</c> the cached elements will be auto-invalidated by /// this instance when modified in ringmaster (i.e. with watchers). Otherwise, the instances invalidation is /// responsibility of the component providing the cache object)</param> /// <param name="allowReplacement">if set to <c>true</c> the caller is indicating that a cache replacement is allowed. /// Otherwise, if there is already a cached object different from the provided one, this method will have no effect.</param> /// <returns>the cache object that will be used from now on (e.g if allowReplacement was false, and there was /// already another cache object set, the return value is such previous cache object)</returns> public async Task <IRingMasterClientCache> SetCacheInstance(IRingMasterClientCache cache, string cachePrefix, bool auto_invalidate, bool allowReplacement) { if (this.baseHandler == null) { return(null); } lock (this.cacheLockobj) { if (this.cacheInflight != this.cache) { return(this.cache); } if (this.cache == cache) { return(cache); } if (this.cache == null || allowReplacement) { this.cachePrefix = cachePrefix; this.cacheInflight = cache; } } bool set = await this.CacheStart(cache, auto_invalidate); if (!set) { return(null); } return(cache); }
public Task <IRingMasterClientCache> SetCacheInstance(IRingMasterClientCache cache, string cachePrefix, bool auto_invalidate, bool allowReplacement) { RingMasterCachedRequestHandler cachedHandler = null; // we first make sure this.requestHandler is set to a cached handler, but avoiding race conditions on the set. while (true) { IRingMasterRequestHandler currentH = this.requestHandler; cachedHandler = currentH as RingMasterCachedRequestHandler; if (cachedHandler != null) { break; } cachedHandler = new RingMasterCachedRequestHandler(currentH); if (Interlocked.CompareExchange(ref this.requestHandler, cachedHandler, currentH) == currentH) { break; } else { cachedHandler.Abandon(); cachedHandler.Dispose(); cachedHandler = null; } } // then, we call setcacheinstance on the cached handler. Task <IRingMasterClientCache> set = cachedHandler.SetCacheInstance(cache, cachePrefix, auto_invalidate, allowReplacement); return(set); }
/// <summary> /// Starts the cache registration /// </summary> /// <param name="cache">The cache.</param> /// <param name="auto_invalidate">if true, the cache gets invalidated from change notifications from RM service automatically</param> /// <returns>a task with the completion of the action, indicating true if the cache was properly set. False otherwise.</returns> private async Task <bool> CacheStart(IRingMasterClientCache cache, bool auto_invalidate) { Task <bool> registration = null; lock (this.cacheLockobj) { if (this.cacheInflight != cache) { return(false); } this.cache = null; this.cache = cache; if (auto_invalidate && cache != null) { string cachePrefix = this.cachePrefix; string path = string.IsNullOrEmpty(cachePrefix) ? "/" : cachePrefix; registration = this.RegisterOnAnySubPathChange(this.Timeout, path, false, false, (RingMasterException.Code rc, WatchedEvent onChange) => { bool reconnectNeeded = cache.NotifyWatcherEvent(rc, onChange, cachePrefix); if (!reconnectNeeded) { return; } if (this.onBulkWatcherRemoved != null) { this.onBulkWatcherRemoved(); return; } if (this.cache != cache) { return; } Task.Delay(1000).ContinueWith(t => { return(this.CacheStart(cache, auto_invalidate)); }); }); } } if (registration != null) { return(await registration); } return(true); }
/// <summary> /// Processes the request in regards to the given cache object. /// </summary> /// <param name="cache">The cache object to use.</param> /// <param name="req">The request to process.</param> /// <returns><c>a response</c> if the processing was completed, <c>null</c> otherwise.</returns> private async Task <RequestResponse> ProcessCacheRequest(IRingMasterClientCache cache, IRingMasterRequest req) { if (cache != null && req != null) { RequestResponse cachedResponse = this.GetCachedResponse(cache, req); if (cachedResponse != null) { return(cachedResponse); } switch (req.RequestType) { case RingMasterRequestType.GetData: { RequestResponse resp = await this.baseHandler.Request(req); string prefix = this.cachePrefix; if (prefix != null) { cache.SetInfo(prefix, req.Path, CachedKind.NodeData | CachedKind.NodeStats, new PrefixedClientCache.DataEntry() { Data = (byte[])resp.Content, Stat = resp.Stat }); } return(resp); } case RingMasterRequestType.GetChildren: { RequestResponse resp = await this.baseHandler.Request(req); string prefix = this.cachePrefix; if (prefix != null) { RequestGetChildren gchil = req as RequestGetChildren; if (string.IsNullOrEmpty(gchil.RetrievalCondition)) { cache.SetInfo(prefix, req.Path, CachedKind.NodeChildren | CachedKind.NodeStats, new PrefixedClientCache.DataEntry() { Children = (IReadOnlyList <string>)resp.Content, Stat = resp.Stat }); } } return(resp); } case RingMasterRequestType.GetAcl: { RequestResponse resp = await this.baseHandler.Request(req); string prefix = this.cachePrefix; if (prefix != null) { cache.SetInfo(prefix, req.Path, CachedKind.NodeAcls | CachedKind.NodeStats, new PrefixedClientCache.DataEntry() { Acls = (IReadOnlyList <Acl>)resp.Content, Stat = resp.Stat }); } return(resp); } case RingMasterRequestType.Exists: { RequestResponse resp = await this.baseHandler.Request(req); string prefix = this.cachePrefix; if (prefix != null) { cache.SetInfo(prefix, req.Path, CachedKind.NodeStats, new PrefixedClientCache.DataEntry() { Stat = resp.Stat }); } return(resp); } } } return(await this.baseHandler.Request(req)); }
/// <summary> /// Gets the response from the cache. /// </summary> /// <param name="cache">The cache to use.</param> /// <param name="req">The request provided.</param> /// <returns>the response if cached, or null if not found</returns> private RequestResponse GetCachedResponse(IRingMasterClientCache cache, IRingMasterRequest req) { if (cache == null || req == null) { return(null); } switch (req.RequestType) { case RingMasterRequestType.GetData: { IRingMasterClientCacheDataEntry data; if (this.cache.TryGetInfo(this.cachePrefix, req.Path, CachedKind.NodeData | CachedKind.NodeStats, out data)) { RequestResponse response = new RequestResponse() { CallId = 0, ResponsePath = req.Path, ResultCode = (int)RingMasterException.Code.Ok, Content = data.Data, Stat = data.Stat, }; return(response); } break; } case RingMasterRequestType.GetChildren: { IRingMasterClientCacheDataEntry data; if (!string.IsNullOrEmpty(((RequestGetChildren)req).RetrievalCondition)) { break; } if (this.cache.TryGetInfo(this.cachePrefix, req.Path, CachedKind.NodeChildren | CachedKind.NodeStats, out data)) { RequestResponse response = new RequestResponse() { CallId = 0, ResponsePath = req.Path, ResultCode = (int)RingMasterException.Code.Ok, Content = data.Children, Stat = data.Stat, }; return(response); } break; } case RingMasterRequestType.GetAcl: { IRingMasterClientCacheDataEntry data; if (this.cache.TryGetInfo(this.cachePrefix, req.Path, CachedKind.NodeAcls | CachedKind.NodeStats, out data)) { RequestResponse response = new RequestResponse() { CallId = 0, ResponsePath = req.Path, ResultCode = (int)RingMasterException.Code.Ok, Content = data.Acls, Stat = data.Stat, }; return(response); } break; } case RingMasterRequestType.Exists: { IRingMasterClientCacheDataEntry data; if (this.cache.TryGetInfo(this.cachePrefix, req.Path, CachedKind.NodeStats, out data)) { RequestResponse response = new RequestResponse() { CallId = 0, ResponsePath = req.Path, ResultCode = (int)RingMasterException.Code.Ok, Content = data.Stat, Stat = data.Stat, }; return(response); } break; } default: { this.CacheInvalidate(cache, req); break; } } return(null); }
private void CacheInvalidate(IRingMasterClientCache cache, IRingMasterRequest req) { if (cache == null || req == null) { return; } switch (req.RequestType) { case RingMasterRequestType.GetData: case RingMasterRequestType.GetChildren: case RingMasterRequestType.GetAcl: case RingMasterRequestType.Exists: return; case RingMasterRequestType.Create: { cache.Invalidate(this.cachePrefix, req.Path); cache.Invalidate(this.cachePrefix, PrefixedClientCache.GetParent(req.Path)); break; } case RingMasterRequestType.Delete: { RequestDelete delete = req as RequestDelete; if (delete.IsCascade) { cache.Wipe(this.cachePrefix); } else { cache.Invalidate(this.cachePrefix, req.Path); cache.Invalidate(this.cachePrefix, PrefixedClientCache.GetParent(req.Path)); } break; } case RingMasterRequestType.Move: { RequestMove move = req as RequestMove; cache.Invalidate(this.cachePrefix, move.Path); cache.Invalidate(this.cachePrefix, PrefixedClientCache.GetParent(move.Path)); cache.Invalidate(this.cachePrefix, PrefixedClientCache.GetParent(move.PathDst)); break; } default: { cache.Invalidate(this.cachePrefix, req.Path); AbstractRingMasterCompoundRequest list = req as AbstractRingMasterCompoundRequest; if (list != null && list.Requests != null) { foreach (IRingMasterRequest child in list.Requests) { this.CacheInvalidate(cache, child); } } break; } } }