Example #1
0
        private void SetupPeerAppendLogJobs(IEnumerable <Peer> peers)
        {
            foreach (var w in _workers.GetWorkers(Queues.PeerAppendLog))
            {
                w.Start();
            }

            _workers.GetWorkers(Queues.ProcessCommandQueue).Single().Start();

            foreach (var p in peers)
            {
                var localP = p;
                var q      = Queues.PeerAppendLog + localP.Address;
                TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] setting up peer append log for queue {q}");
                var todo = PeerAppendLog(localP);

                _workers.Enqueue(q,
                                 new Job(todo,
                                         TheTrace.LogPolicy(_meAsAPeer.ShortName).WaitAndRetryAsync(3, (i) => TimeSpan.FromMilliseconds(i * i * 50)),
                                         TimeSpan.FromMilliseconds(30)));
            }

            // Applying commands received from the clients
            Func <CancellationToken, Task> pcq = ProcessCommandsQueue;

            _workers.Enqueue(Queues.ProcessCommandQueue,
                             new Job(pcq,
                                     TheTrace.LogPolicy(_meAsAPeer.ShortName).RetryForeverAsync(),
                                     _settings.ElectionTimeoutMin.Multiply(0.2)));
        }
Example #2
0
        public void CanAdd2And2EvenIfItFails()
        {
            var maths = new Worker("maths");
            var ran   = false;
            var timesForExceptions = 4;
            var job = new Job <int>(c =>
            {
                if (timesForExceptions-- > 0)
                {
                    throw new ApplicationException();
                }
                ran = true;
                return(Task.FromResult(2 + 2));
            },
                                    TheTrace.LogPolicy("this").RetryForeverAsync()
                                    );

            maths.Start();
            maths.Enqueue(job);
            while (!job.IsFinished)
            {
                Thread.Sleep(100);
            }
            Assert.True(ran);
        }
Example #3
0
        private async Task Candidacy(CancellationToken c)
        {
            while (_role == Role.Candidate)
            {
                var forMe     = 1; // vote for yourself
                var againstMe = 0;

                var peers     = _peerManager.GetPeers().ToArray();
                var concensus = (peers.Length / 2) + 1;
                var proxies   = peers.Select(x => _peerManager.GetProxy(x.Address));
                var retry     = TheTrace.LogPolicy(_meAsAPeer.ShortName).WaitAndRetryAsync(3, (i) => TimeSpan.FromMilliseconds(i * i * 30));
                var policy    = Policy.TimeoutAsync(_settings.CandidacyTimeout).WrapAsync(retry);
                var request   = new RequestVoteRequest()
                {
                    CandidateId  = State.Id,
                    CurrentTerm  = State.CurrentTerm,
                    LastLogIndex = _logPersister.LastIndex,
                    LastLogTerm  = _logPersister.LastEntryTerm
                };

                var all = await Task.WhenAll(proxies.Select(p => policy.ExecuteAndCaptureAsync(() => p.RequestVoteAsync(request))));

                var maxTerm = 0L;
                foreach (var r in all)
                {
                    if (r.Outcome == OutcomeType.Successful)
                    {
                        if (r.Result.CurrentTerm > maxTerm)
                        {
                            maxTerm = r.Result.CurrentTerm;
                        }

                        if (r.Result != null && r.Result.VoteGranted)
                        {
                            forMe++;
                        }
                        else
                        {
                            againstMe++;
                        }
                    }
                }

                if (againstMe >= concensus)
                {
                    TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Result of the candidacy for term {State.CurrentTerm}. I got rejected with {againstMe} votes :/");
                    BecomeFollower(maxTerm);
                }
                else if (forMe >= concensus)
                {
                    TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Result of the candidacy for term {State.CurrentTerm}. I got elected with {forMe} votes! :)");
                    BecomeLeader();
                }
                else
                {
                    TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Result of the candidacy for term {State.CurrentTerm}. Non-conclusive with {forMe} for me and {againstMe} against me.");
                }
            }
        }
Example #4
0
        private async Task HeartBeatSend(CancellationToken c)
        {
            if (_role != Role.Leader)
            {
                return;
            }

            if (_lastHeartbeatSent.Since() < _settings.ElectionTimeoutMin.Multiply(0.2))
            {
                return;
            }

            var currentTerm = State.CurrentTerm; // create a var. Could change during the method leading to confusing logs.

            var req = new AppendEntriesRequest()
            {
                CurrentTerm       = currentTerm,
                Entries           = new byte[0][],
                LeaderCommitIndex = _volatileState.CommitIndex,
                LeaderId          = State.Id,
                PreviousLogIndex  = long.MaxValue,
                PreviousLogTerm   = long.MaxValue
            };

            var peers   = _peerManager.GetPeers().ToArray();
            var proxies = peers.Select(x => _peerManager.GetProxy(x.Address));
            var retry   = TheTrace.LogPolicy(_meAsAPeer.ShortName).RetryForeverAsync();
            var policy  = Policy.TimeoutAsync(_settings.ElectionTimeoutMin.Multiply(0.2)).WrapAsync(retry);
            var all     = await Task.WhenAll(proxies.Select(p => policy.ExecuteAndCaptureAsync(() => p.AppendEntriesAsync(req))));

            var maxTerm = currentTerm;

            foreach (var r in all)
            {
                if (r.Outcome == OutcomeType.Successful)
                {
                    if (!r.Result.IsSuccess)
                    {
                        TheTrace.TraceWarning($"[{_meAsAPeer.ShortName}] Got this reason for unsuccessful AppendEntriesAsync from a peer: {r.Result.Reason}");
                    }

                    // NOTE: We do NOT change leadership if they send higher term, since they could be candidates whom will not become leaders
                    // we actually do not need to do anything with the result other than logging it
                    if (r.Result.CurrentTerm > maxTerm)
                    {
                        maxTerm = r.Result.CurrentTerm;
                    }
                }
            }

            if (maxTerm > State.CurrentTerm)
            {
                TheTrace.TraceWarning($"[{_meAsAPeer.ShortName}] Revolution brewing. Terms as high as {maxTerm} (vs my {currentTerm}) were seen.");
            }

            _lastHeartbeatSent.Set();
        }
Example #5
0
        private void SetupPool()
        {
            var names = new List <string>();

            foreach (var p in _peerManager.GetPeers())
            {
                names.Add(Queues.PeerAppendLog + p.Address);
            }

            names.Add(Queues.LogCommit);
            names.Add(Queues.HeartBeatReceiveAndCandidacy);
            names.Add(Queues.HeartBeatSend);
            names.Add(Queues.ProcessCommandQueue);
            names.Add(Queues.CreateSnapshot);

            _workers = new WorkerPool(names.ToArray());
            _workers.Start();

            // LogCommit
            Func <CancellationToken, Task> logCommit = LogCommit;

            _workers.Enqueue(Queues.LogCommit,
                             new Job(logCommit,
                                     TheTrace.LogPolicy(_meAsAPeer.ShortName).RetryForeverAsync(),
                                     _settings.ElectionTimeoutMin.Multiply(0.2)));

            // receiving heartbeat
            Func <CancellationToken, Task> hbr = HeartBeatReceive;

            _workers.Enqueue(Queues.HeartBeatReceiveAndCandidacy,
                             new Job(hbr,
                                     TheTrace.LogPolicy(_meAsAPeer.ShortName).RetryForeverAsync(),
                                     _settings.ElectionTimeoutMin.Multiply(0.2)));

            // sending heartbeat
            Func <CancellationToken, Task> hbs = HeartBeatSend;

            _workers.Enqueue(Queues.HeartBeatSend,
                             new Job(hbs,
                                     TheTrace.LogPolicy(_meAsAPeer.ShortName).RetryForeverAsync(),
                                     _settings.ElectionTimeoutMin.Multiply(0.2)));

            // Applying commands received from the clients
            Func <CancellationToken, Task> cs = CreateSnapshot;

            _workers.Enqueue(Queues.CreateSnapshot,
                             new Job(cs,
                                     TheTrace.LogPolicy(_meAsAPeer.ShortName).WaitAndRetryAsync(2, (i) => TimeSpan.FromMilliseconds(i * i * 50)),
                                     _settings.ElectionTimeoutMin.Multiply(0.2)));

            TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Setup finished.");
        }
Example #6
0
        public void CanAdd2And2()
        {
            var maths = new Worker("maths");
            var ran   = false;

            var job = new Job <int>(c => Task.FromResult(2 + 2),
                                    TheTrace.LogPolicy("this").RetryForeverAsync(), (n) => { ran = true; });

            maths.Start();
            maths.Enqueue(job);
            Thread.Sleep(100);
            Assert.True(ran);
        }
Example #7
0
        private Func <CancellationToken, Task> PeerAppendLog(Peer peer)
        {
            return((CancellationToken c) =>
            {
                long nextIndex;
                long matchIndex;

                var hasMatch = _volatileLeaderState.TryGetMatchIndex(peer.Id, out matchIndex);
                var hasNext = _volatileLeaderState.TryGetNextIndex(peer.Id, out nextIndex);
                var myLastIndex = _logPersister.LastIndex;

                if (!hasMatch)
                {
                    TheTrace.TraceWarning($"[{_meAsAPeer.ShortName}] Could not find peer with id {peer.Id} and address {peer.Address} in matchIndex dic.");
                    return Task.CompletedTask;
                }

                if (!hasNext)
                {
                    TheTrace.TraceWarning($"[{_meAsAPeer.ShortName}] Could not find peer with id {peer.Id} and address {peer.Address} in nextIndex dic.");
                    return Task.CompletedTask;
                }

                if (nextIndex > myLastIndex)
                {
                    TheTrace.TraceVerbose($"[{_meAsAPeer.ShortName}] PeerAppendLog - Nothing to do for peer {peer.ShortName} since myIndex is {myLastIndex} and nextIndex is {nextIndex}.");
                    return Task.CompletedTask; // nothing to do
                }

                var count = (int)Math.Min(_settings.MaxNumberLogEntriesToAskToBeAppended, myLastIndex + 1 - nextIndex);
                var proxy = _peerManager.GetProxy(peer.Address);
                var retry = TheTrace.LogPolicy(_meAsAPeer.ShortName).WaitAndRetryAsync(2, (i) => TimeSpan.FromMilliseconds(20));
                var policy = Policy.TimeoutAsync(_settings.CandidacyTimeout).WrapAsync(retry); // TODO: create its own timeout

                if (nextIndex >= _logPersister.LogOffset)
                {
                    TheTrace.TraceVerbose($"[{_meAsAPeer.ShortName}] Intending to do SendLog for peer {peer.Address} with nextIndex {nextIndex} and count {count}.");
                    return SendLogs(proxy, policy, peer, nextIndex, matchIndex, count);
                }
                else
                {
                    TheTrace.TraceVerbose($"[{_meAsAPeer.ShortName}] Intending to do SendSnapshot for peer {peer.Address}.");
                    return SendSnapshot(proxy, policy, peer, nextIndex, matchIndex);
                }
            });
        }
Example #8
0
        public async Task CanAdd2And2ThousandTimes()
        {
            var maths = new Worker("maths");
            var ran   = 0;
            var job   = new Job <int>(c =>
            {
                ran++;
                return(Task.FromResult(2 + 2));
            },
                                      TheTrace.LogPolicy("this")
                                      .RetryForeverAsync()
                                      );

            maths.Start();
            for (int i = 0; i < 1000; i++)
            {
                maths.Enqueue(job);
            }

            Thread.Sleep(10000);
            Assert.Equal(1000, ran);
        }