/// <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);
        }
Ejemplo n.º 2
0
        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;
            }
            }
        }