public void Handle(InitRectify message)
            {
                _node.LogMessage(message);

                // Notify the successor to update it's predecessor with this node's info
                var opId    = _node.Config.CorrelationFactory.GetNextCorrelation();
                var handler = _node.CreateAwaitAllResponsesHandler();

                handler
                .PerformAction(() =>
                {
                    _node.Log($"Sending Rectify to {_node.Successor} Id:{opId}");
                    var msg = new Rectify(_node.Identity, _node.Successor, opId)
                    {
                        Predecessor = _node.Identity
                    };
                    _commMgr.Send(msg);
                })
                .AndAwait(opId, (RectifyReply reply) =>
                {
                    _node.LogMessage(reply);
                    _node.Log($"{_node.Identity} Joined Network");
                })
                .Run(opId);
            }
            private void JoinToSeed(NodeInfo seedNodeInfo)
            {
                _node.Predecessor = _node.Identity;
                _node.FingerTable.Init();

                var opId      = _node.Config.CorrelationFactory.GetNextCorrelation();
                var startTime = _node.Clock.Now;

                var responseHandler = _node.CreateAwaitAllResponsesHandler();

                responseHandler
                .PerformAction(() =>
                {
                    var msg = new JoinNetwork(_node.Identity, seedNodeInfo, opId)
                    {
                        RoutingTable = _node.FingerTable.Entries,
                    };

                    _node.Log($"Sending {msg.TypeName()} To {seedNodeInfo} Id:{opId}");
                    _commMgr.Send(msg);
                })
                .AndAwait(opId, (JoinNetworkReply reply) =>
                {
                    _node.Log($"{reply.TypeName()} From {reply.From} Id:{reply.CorrelationId}");
                    _node.Log($"Join took {(_node.Clock.Now - startTime).Milliseconds} ms");
                    _node.Log($"Assigning successor {reply.SuccessorTable[0].SuccessorIdentity}");

                    _node.SuccessorTable.Copy(reply.SuccessorTable);
                    //_node.FingerTable.Copy(reply.RoutingTable);

                    // This node has "joined" but is not in an ideal state as it is not part of the ring network yet.
                })
                .ContinueWith(() =>
                {
                    _commMgr.SendInternal(new InitStabilize());
                })
                .Run(opId);
            }
            private void Stabilize(NodeInfo successorInfo)
            {
                // Send a stabilize message to my successor to learn it's predecessor
                //   Stabilize might need to be retried as other nodes will be joining at the same location as it is a seed node.

                var opId           = _node.Config.CorrelationFactory.GetNextCorrelation();
                var messageHandler = _node.CreateAwaitAllResponsesHandler();

                messageHandler
                .PerformAction(() =>
                {
                    var msg = new Stabilize(_node.Identity, successorInfo, opId);
                    _node.Log($"Sending {msg.TypeName()} To:{msg.To} Id:{opId}");
                    _commMgr.Send(msg);
                })
                .AndAwait(opId, (StabilizeReply reply) =>
                {
                    var thisHash        = _node.Identity.RoutingHash;
                    var predecessorHash = reply.Predecessor.RoutingHash;
                    var successorHash   = successorInfo.RoutingHash;

                    // We need to check the order of the predecessor or the successor before adopting a value
                    // as nodes are racing to join the seed node - our last query may be out of date, but not by much
                    if (thisHash.IsBetween(predecessorHash, successorHash))
                    {
                        // No ordering change. We can adopt the predecessor values and tell the successor to rectify
                        StabilizeFromSuccessor(reply);
                    }
                    else if (predecessorHash.IsBetween(thisHash, successorHash))
                    {
                        // another node beat this one to join to the successor, changing the ordering...
                        StabilizeFromPredecessor(reply);
                    }
                })
                .Run(opId);
            }
            public void Handle(Notify message)
            {
                _node.Log($"{message.TypeName()} From:{message.From}");
                _commMgr.SendAck(message, message.CorrelationId);

                // Let the predecessor know to join to the specified successor.
                // And adjust successor table accordingly.
                var entries = _node.SuccessorTable.Entries;

                for (int i = 1; i < entries.Length; ++i)
                {
                    entries[i] = entries[i - 1];
                }
                var successor = message.NewSuccessor;

                entries[0] = new RoutingTableEntry(successor.RoutingHash, successor);

                _node.Log($"Changed successor to {successor}");
                _commMgr.Send(new NotifyReply(_node.Identity, message.From, message.CorrelationId));
            }