예제 #1
0
        static IEtcdWatchStatus MonitorEtcd(IEtcdWatchStatus status)
        {
            IEtcdClient client = new EtcdClient();
            status = client.Watch(status, null, (a, b) =>
            {
                Console.WriteLine("{0}-{1}", b.Node.Key, b.Node.Value);
                return true;
            },
            (a,b) =>
            {
                if (b.PrevErrorsCount%5 == 0){
                    Console.WriteLine("Another 5 errors:" + b.Exception.Message);
                }

                if (a.AllFailed && a.Clients.Select(c => c.LastError.PrevErrorsCount).All(num => num > 15))
                {
                    Console.WriteLine("Fatal Error in etcd cluster, Reinitializing...");
                    a.AbortAll(); //implicit not running
                    MonitorEtcd(status);
                }

                return true;
            },true);

            return status;
        }
예제 #2
0
        /// <summary>
        ///     Sets up a watch on a keyspace and will call the callback when triggered
        /// </summary>
        /// <param name="externalStatus">previous status to update</param>
        /// <param name="key">key</param>
        /// <param name="followUp">callback</param>
        /// <param name="onError">optional error handler</param>
        /// <param name="recursive">watch subkeys?</param>
        /// <param name="timeout">How long will we watch?</param>
        /// <param name="waitIndex">Index to wait from</param>
        public IEtcdWatchStatus Watch(IEtcdWatchStatus externalStatus, string key, Func<IEtcdWatchStatus, EtcdResponse, bool> followUp, Func<IEtcdWatchStatus, EtcdErrorResult, bool> onError = null, bool recursive = false, int? timeout = null, int? waitIndex = null)
        {
            if (followUp == null)
                throw new ArgumentNullException("followUp");

            //if (key == null)
            //    throw new ArgumentNullException("key");

            if(externalStatus == null )
                externalStatus = new EtcdWatchStatus();
            else if(!(externalStatus is EtcdWatchStatus))
                throw new ArgumentException("you cant implement externalStatus yourself, but should obtain it from previous call to Watch");

            EtcdWatchStatus status = (EtcdWatchStatus)externalStatus;

            status.Cleanup();

            //handle for forced close, if such

            //default error handler
            if (onError == null)
            {
                onError = (a,b) =>
                {
                    //rest a while
                    Thread.Sleep(50);
                    return true;
                };
            }

            Action<IRestResponse<EtcdResponse>, RestRequestAsyncHandle> callback = null;
            callback = (b,c) =>
            {

                if (!status.Running)
                    return;

                var baseUrl = getResponseBaseUri(b.Request, c);

                //some weird fix for rest response
                if (String.IsNullOrWhiteSpace(b.Server))
                    b.Server = baseUrl.ToString();

                var clientContext = status.Clients.First(a => a.Server == baseUrl);

                if (checkForError(b))
                {
                    clientContext.State = ClientState.Failed;
                    if(clientContext.LastError==null)
                        clientContext.LastError = new EtcdErrorResult();

                    //check for client shutdown or general watch abort
                    if (status.LockedCheck(()=>onError(status,clientContext.LastError.Update(constructException(b)))) && status.Running)
                    {
                        //we are still alive, try reconnect
                        clientContext.CurrentRequest = _clients
                                .First(a => a.BaseUrl == baseUrl)
                                .GetAsync<EtcdResponse>(prepareWaitOnKeyRequest(baseUrl, key, recursive, timeout, waitIndex), (r, h) => callback(r, h));
                    }
                    else
                    {
                        clientContext.State = ClientState.Down;
                    }
                    return;
                }

                clientContext.State = ClientState.Succeeded;

                //avoid duplicated results from cluster and try to stop gracefully, if required

                if (!status.Running) //check we are already aborted
                    return;

                lock (status)
                {
                    if (!status.Running)
                        return;

                    if (!status.LastResults.ContainsKey(b.Data.Node.Key) || status.LastResults[b.Data.Node.Key] != b.Data.Node.ModifiedIndex)
                    {
                        status.LastResults[b.Data.Node.Key] = b.Data.Node.ModifiedIndex;
                        if (!followUp(status, processRestResponse(b)))
                        {
                            status.Running = false;
                            status.AbortAll();
                            return;
                        }
                    }
                }

                //now wait for changes...
                //see SetWaitable call

                clientContext.CurrentRequest = _clients.First(a => a.BaseUrl == clientContext.Server)
                        .GetAsync<EtcdResponse>(b.Request.SetWaitable(), (r, h) => callback(r, h));
            };

            //all first requests are should return current value immediately
            _clients.ForEach(a =>
            {
                ClientWatchContext context;
                status.Clients.Add(context = new ClientWatchContext(a.BaseUrl) {State = ClientState.InProgress});

                context.CurrentRequest = a.GetAsync<EtcdResponse>(
                    prepareWaitOnKeyRequest(a.BaseUrl,key, recursive, timeout, waitIndex).SetWaitable(),(r, h) => callback(r, h));
            });

            return status;
        }