/// <summary>
        /// Depart from the Chord ring and stop maintenance.
        /// </summary>
        public void Depart()
        {
            // first, stop maintenance so nothing gets broken
            StopMaintenance();

            try
            {
                // courteously introduce departing node's successor and predecessor
                // ...successor, meet predecessor; predecessor, meet successor
                ChordInstance instance = ChordServer.GetInstance(this.Successor);
                instance.Predecessor = this.Predecessor;

                instance           = ChordServer.GetInstance(this.Predecessor);
                instance.Successor = this.Successor;
            }
            catch (Exception e)
            {
                ChordServer.Log(LogLevel.Error, "Navigation", "Error on Depart ({0}).", e.Message);
            }
            finally
            {
                // set local state in such a way as to force it out of the Chord ring
                this.Successor   = ChordServer.LocalNode;
                this.Predecessor = ChordServer.LocalNode;
                this.FingerTable = new ChordFingerTable(ChordServer.LocalNode);
                for (int i = 0; i < this.SuccessorCache.Length; i++)
                {
                    this.SuccessorCache[i] = ChordServer.LocalNode;
                }
            }
        }
        /// <summary>
        /// Maintenance task to stabilize the local node's predecessor as per the Chord paper.
        /// </summary>
        /// <param name="sender">The backgroundworker thread that this task is running on.</param>
        /// <param name="ea">Args (ignored)</param>
        private void StabilizePredecessors(object sender, DoWorkEventArgs ea)
        {
            BackgroundWorker me = (BackgroundWorker)sender;

            while (!me.CancellationPending)
            {
                if (this.Predecessor != null)
                {
                    try
                    {
                        // validate predecessor (in case of error, predecessor becomes null
                        // and is fixed by stabilizesuccessors and notify.
                        ChordInstance instance = ChordServer.GetInstance(this.Predecessor);
                        if (!ChordServer.IsInstanceValid(instance))
                        {
                            this.Predecessor = null;
                        }
                    }
                    catch (Exception e)
                    {
                        ChordServer.Log(LogLevel.Error, "StabilizePredecessors", "StabilizePredecessors error: {0}", e.Message);
                        this.Predecessor = null;
                    }
                }

                // TODO: make this configurable either via config file or passed in via arguments.
                Thread.Sleep(5000);
            }
        }
        /// <summary>
        /// Maintenance task to ensure that the local node has valid successor node.  Roughly equivalent
        /// to what is called out in the Chord paper.
        /// </summary>
        /// <param name="sender">The worker thread the task is running on.</param>
        /// <param name="ea">Args (ignored here).</param>
        private void StabilizeSuccessors(object sender, DoWorkEventArgs ea)
        {
            BackgroundWorker me = (BackgroundWorker)sender;

            while (!me.CancellationPending)
            {
                try
                {
                    // check in successor and if it's bad, replace it with
                    // the next live entry in the successor cache
                    ChordNode succPredNode = ChordServer.GetPredecessor(this.Successor);
                    if (succPredNode != null)
                    {
                        if (ChordServer.IsIDInRange(succPredNode.ID, this.ID, this.Successor.ID))
                        {
                            this.Successor = succPredNode;
                        }

                        // ignoring return because bad node will be detected on next invocation
                        ChordServer.CallNotify(this.Successor, ChordServer.LocalNode);
                        GetSuccessorCache(this.Successor);
                    }
                    else
                    {
                        bool successorCacheHelped = false;
                        foreach (ChordNode entry in this.m_SuccessorCache)
                        {
                            ChordInstance instance = ChordServer.GetInstance(entry);
                            if (ChordServer.IsInstanceValid(instance))
                            {
                                this.Successor = entry;
                                ChordServer.CallNotify(this.Successor, ChordServer.LocalNode);
                                GetSuccessorCache(this.Successor);
                                successorCacheHelped = true;
                                break;
                            }
                        }

                        // if we get here, then we got no help and have no other recourse than to re-join using the initial seed...
                        if (!successorCacheHelped)
                        {
                            ChordServer.Log(LogLevel.Error, "StabilizeSuccessors", "Ring consistency error, Re-Joining Chord ring.");
                            Join(this.m_SeedNode, this.Host, this.Port);
                            return;
                        }
                    }
                }
                catch (Exception e)
                {
                    ChordServer.Log(LogLevel.Error, "Maintenance", "Error occured during StabilizeSuccessors ({0})", e.Message);
                }

                // TODO: this could be tweaked and/or made configurable elsewhere or passed in as arguments
                Thread.Sleep(5000);
            }
        }
        /// <summary>
        /// Maintenance task to perform ring consistency checking and re-joining to keep a Chord
        /// ring stable under extreme churn and in cases of ring damage.
        /// </summary>
        /// <param name="sender">The calling backgroundworker.</param>
        /// <param name="ea">Args (ignored for this task).</param>
        private void ReJoin(object sender, DoWorkEventArgs ea)
        {
            BackgroundWorker me = (BackgroundWorker)sender;

            while (!me.CancellationPending)
            {
                try
                {
                    // if this is the first iteration, then the core logic
                    // is skipped, as the first iteration generally occurs
                    // right after node Join - allowing a short buffer for
                    // routing structures to stabilize improves the utility
                    // of the ReJoin facility.
                    if (this.m_HasReJoinRun)
                    {
                        // first find the successor for the seed node
                        if (this.m_SeedNode != null)
                        {
                            ChordNode seedSuccessor = FindSuccessor(this.m_SeedNode.ID);

                            // if the successor is not equal to the seed node, something is fishy
                            if (seedSuccessor.ID != this.m_SeedNode.ID)
                            {
                                // if the seed node is still active, re-join the ring to the seed node
                                ChordInstance instance = ChordServer.GetInstance(this.m_SeedNode);
                                if (ChordServer.IsInstanceValid(instance))
                                {
                                    ChordServer.Log(LogLevel.Error, "ReJoin", "Unable to contact initial seed node {0}.  Re-Joining...", this.m_SeedNode);
                                    Join(this.m_SeedNode, this.Host, this.Port);
                                }

                                // otherwise, in the future, there will be a cache of seed nodes to check/join from...
                                // as it may be the case that the seed node simply has disconnected from the network.
                            }
                        }
                    }
                    else
                    {
                        // subsequent iterations will go through the core logic
                        this.m_HasReJoinRun = true;
                    }
                }
                catch (Exception e)
                {
                    ChordServer.Log(LogLevel.Error, "Maintenance", "Error occured during ReJoin ({0})", e.Message);
                }

                // TODO: the delay between iterations, here, is configurable, and
                // ideally should be retrieved from configuration or otherwise passed in...
                Thread.Sleep(30000);
            }
        }
Example #5
0
 /// <summary>
 /// Safely checks whether a ChordInstance is valid by ensuring the port and successor values are valid.
 /// </summary>
 /// <param name="instance">The ChordInstance to validity-check.</param>
 /// <returns>TRUE if valid; FALSE otherwise.</returns>
 public static bool IsInstanceValid(ChordInstance instance)
 {
     try
     {
         if (instance.Port > 0 && instance.Successor != null)
         {
             return(true);
         }
         else
         {
             return(false);
         }
     }
     catch (Exception e)
     {
         Log(LogLevel.Debug, "Incoming instance was not valid: ({0}).", e.ToString());  // TODO; better logging
         return(false);
     }
 }
Example #6
0
        /// <summary>
        /// Get a (local or remote) ChordInstance given a ChordNode.
        /// </summary>
        /// <param name="node">The ChordNode specifying the node to get an instance of.</param>
        /// <returns>A ChordInstance from the specified node, or null if an error is encountered.</returns>
        public static ChordInstance GetInstance(ChordNode node)
        {
            if (node == null)
            {
                ChordServer.Log(LogLevel.Error, "Navigation", "Invalid Node ({0}).", "Null Argument.");
                return(null);
            }

            try
            {
                ChordInstance retInstance = (ChordInstance)Activator.GetObject(typeof(ChordInstance), string.Format("tcp://{0}:{1}/chord", node.Host, node.PortNumber));
                return(retInstance);
            }
            catch (Exception e)
            {
                // perhaps instead we should just pass on the error?
                ChordServer.Log(LogLevel.Error, "Navigation", "Unable to activate remote server {0}:{1} ({2}).", node.Host, node.PortNumber, e.Message);
                return(null);
            }
        }
        /// <summary>
        /// Returns the closest successor preceding id.
        /// </summary>
        /// <param name="id">The id for which the closest finger should be found</param>
        /// <returns>The successor node of the closest finger to id in the current node's finger table</returns>
        private ChordNode FindClosestPrecedingFinger(UInt64 id)
        {
            // iterate downward through the finger table looking for the right finger in the right range. if the finger is
            // in the range but not valid, keep moving. if the entire finger table is checked without success, check the successor
            // cache - if that fails, return the local node as the closest preceding finger.
            for (int i = this.FingerTable.Length - 1; i >= 0; i--)
            {
                // if the finger is more closely between the local node and id and that finger corresponds to a valid node, return the finger
                if (this.FingerTable.Successors[i] != null && this.FingerTable.Successors[i] != ChordServer.LocalNode)
                {
                    if (ChordServer.FingerInRange(this.FingerTable.Successors[i].ID, this.ID, id))
                    {
                        ChordInstance instance = ChordServer.GetInstance(this.FingerTable.Successors[i]);
                        if (ChordServer.IsInstanceValid(instance))
                        {
                            return(this.FingerTable.Successors[i]);
                        }
                    }
                }
            }

            // at this point, not even the successor is any good so go through the successor cache and run the same test
            for (int i = 0; i < this.SuccessorCache.Length; i++)
            {
                if (this.SuccessorCache[i] != null && this.SuccessorCache[i] != ChordServer.LocalNode)
                {
                    if (ChordServer.FingerInRange(this.SuccessorCache[i].ID, this.ID, id))
                    {
                        ChordInstance instance = ChordServer.GetInstance(this.SuccessorCache[i]);
                        if (ChordServer.IsInstanceValid(instance))
                        {
                            return(this.SuccessorCache[i]);
                        }
                    }
                }
            }

            // otherwise, if there is nothing closer, the local node is the closest preceding finger
            return(ChordServer.LocalNode);
        }
Example #8
0
        /// <summary>
        /// Calls AddKey remotely.
        /// </summary>
        /// <param name="remoteNode">The remote node on which to call AddKey.</param>
        /// <param name="value">The string value to add.</param>
        /// <param name="retryCount">The number of retries to attempt.</param>
        public static void CallAddKey(ChordNode remoteNode, string value, int retryCount)
        {
            ChordInstance instance = ChordServer.GetInstance(remoteNode);

            try
            {
                instance.AddKey(value);
            }
            catch (System.Exception ex)
            {
                ChordServer.Log(LogLevel.Debug, "Remote Invoker", "CallAddKey error: {0}", ex.Message);

                if (retryCount > 0)
                {
                    CallAddKey(remoteNode, value, --retryCount);
                }
                else
                {
                    ChordServer.Log(LogLevel.Debug, "Remote Invoker", "CallAddKey failed - error: {0}", ex.Message);
                }
            }
        }
Example #9
0
        /// <summary>
        /// Gets the remote Successor property, given a custom retry count.
        /// </summary>
        /// <param name="remoteNode">The remote node from which to access the property.</param>
        /// <param name="retryCount">The number of times to retry the operation in case of error.</param>
        /// <returns>The remote successor, or NULL in case of error.</returns>
        public static ChordNode GetSuccessor(ChordNode remoteNode, int retryCount)
        {
            ChordInstance instance = ChordServer.GetInstance(remoteNode);

            try
            {
                return(instance.Successor);
            }
            catch (System.Exception ex)
            {
                ChordServer.Log(LogLevel.Debug, "Remote Accessor", "GetSuccessor error: {0}", ex.Message);

                if (retryCount > 0)
                {
                    return(GetSuccessor(remoteNode, --retryCount));
                }
                else
                {
                    return(null);
                }
            }
        }
Example #10
0
        /// <summary>
        /// Calls FindKey remotely.
        /// </summary>
        /// <param name="remoteNode">The remote node on which to call FindKey.</param>
        /// <param name="key">The key to look up.</param>
        /// <param name="retryCount">The number of retries to attempt.</param>
        /// <returns>The value corresponding to the key, or empty string if not found.</returns>
        public static string CallFindKey(ChordNode remoteNode, ulong key, int retryCount)
        {
            ChordInstance instance = ChordServer.GetInstance(remoteNode);

            try
            {
                return(instance.FindKey(key));
            }
            catch (System.Exception ex)
            {
                ChordServer.Log(LogLevel.Debug, "Remote Invoker", "CallFindKey error: {0}", ex.Message);

                if (retryCount > 0)
                {
                    return(CallFindKey(remoteNode, key, --retryCount));
                }
                else
                {
                    ChordServer.Log(LogLevel.Debug, "Remote Invoker", "CallFindKey failed - error: {0}", ex.Message);
                    return(string.Empty);
                }
            }
        }
Example #11
0
        /// <summary>
        /// Calls FindSuccessor() remotely, using a default retry value of three.
        /// </summary>
        /// <param name="remoteNode">The remote node on which to call FindSuccessor().</param>
        /// <param name="id">The ID to look up.</param>
        /// <param name="retryCount">The number of times to retry the operation in case of error.</param>
        /// <param name="hopCountIn">The known hopcount prior to calling FindSuccessor on this node.</param>
        /// <param name="hopCountOut">The total hopcount of this operation (either returned upwards, or reported for hopcount efficiency validation).</param>
        /// <returns>The Successor of ID, or NULL in case of error.</returns>
        public static ChordNode CallFindSuccessor(ChordNode remoteNode, UInt64 id, int retryCount, int hopCountIn, out int hopCountOut)
        {
            ChordInstance instance = ChordServer.GetInstance(remoteNode);

            try
            {
                return(instance.FindSuccessor(id, hopCountIn, out hopCountOut));
            }
            catch (System.Exception ex)
            {
                ChordServer.Log(LogLevel.Debug, "Remote Invoker", "CallFindSuccessor error: {0}", ex.Message);

                if (retryCount > 0)
                {
                    return(CallFindSuccessor(remoteNode, id, --retryCount, hopCountIn, out hopCountOut));
                }
                else
                {
                    hopCountOut = hopCountIn;
                    return(null);
                }
            }
        }
Example #12
0
        /// <summary>
        /// Calls Notify() remotely, using a default retry value of three.
        /// </summary>
        /// <param name="remoteNode">The remote on which to call the method.</param>
        /// <param name="callingNode">The node to inform the remoteNode of.</param>
        /// <param name="retryCount">The number of times to retry the operation in case of error.</param>
        /// <returns>True if succeeded, FALSE otherwise.</returns>
        public static bool CallNotify(ChordNode remoteNode, ChordNode callingNode, int retryCount)
        {
            ChordInstance instance = ChordServer.GetInstance(remoteNode);

            try
            {
                instance.Notify(callingNode);
                return(true);
            }
            catch (System.Exception ex)
            {
                ChordServer.Log(LogLevel.Debug, "Remote Invoker", "CallNotify error: {0}", ex.Message);

                if (retryCount > 0)
                {
                    return(CallNotify(remoteNode, callingNode, --retryCount));
                }
                else
                {
                    return(false);
                }
            }
        }
Example #13
0
        /// <summary>
        /// Join the current ChordInstance to a chord ring given a seed node.
        /// </summary>
        /// <param name="seed">Remote node to connect to that is a member of a Chord ring.  To start a new Chord ring, a null seed may be provided.</param>
        /// <param name="host">The local host name.</param>
        /// <param name="port">The tcp port on which this node will listen for calls from other Chord nodes.</param>
        /// <returns>true if the Join succeeds; false, otherwise.</returns>
        public bool Join(ChordNode seed, string host, int port)
        {
            // LocalNode is established first to ensure proper logging.
            ChordServer.LocalNode = new ChordNode(host, port);

            this.m_HasReJoinRun = false;
            this.m_SeedNode     = seed; // cache the seed node so the ring may re-form itself in case of partition or damage

            // safely establish finger table (startvalues and successors, using LocalNode as the default successor)
            this.FingerTable = new ChordFingerTable(ChordServer.LocalNode);

            // establish successor cache initially with all entries pointing loally
            this.SuccessorCache = new ChordNode[3]; // TODO: make this configurable
            for (int i = 0; i < this.SuccessorCache.Length; i++)
            {
                this.SuccessorCache[i] = ChordServer.LocalNode;
            }

            if (seed != null)   // join an existing chord ring
            {
                ChordServer.Log(LogLevel.Info, "Navigation", "Joining Ring @ {0}:{1}.", seed.Host, seed.PortNumber);

                // first, establish successor via seed node
                ChordInstance instance = ChordServer.GetInstance(seed);
                if (ChordServer.IsInstanceValid(instance))
                {
                    try
                    {
                        this.Successor = instance.FindSuccessor(this.ID);   // NOTE: this conceivably could be replaced with ChordServer.FindSuccessor()

                        // disabled: a clever trick that requires only one remote network call is to
                        //  append the successor's successor cache (minus its last entry) to the local
                        //  successor cache, starting at the second entry in the local successor cache.
                        //  during churn, this can break down, so instead the successor cache is populated
                        //  and maintained lazily by maintenance.
                        //  as the successor cache was initialized with the LocalNode as the default
                        //  instance values, "misses" on successor cache entries are gracefully handled by
                        //  simply being forwarded (via the local node) on to the successor node where
                        //  better information may be found and utilized.
                        //
                        //this.SuccessorCache = ChordServer.GetSuccessorCache(this.Successor);
                    }
                    catch (Exception e)
                    {
                        ChordServer.Log(LogLevel.Error, "Navigation", "Error setting Successor Node ({0}).", e.Message);
                        return(false);
                    }
                }
                else
                {
                    ChordServer.Log(LogLevel.Error, "Navigation", "Invalid Node Seed.");
                    return(false);
                }
            }
            else // start a new ring
            {
                // not much needs to happen - successor is already established as
                // ChordServer.LocalNode,everything else takes place lazily as part of maintenance
                ChordServer.Log(LogLevel.Info, "Navigation", "Starting Ring @ {0}:{1}.", this.Host, this.Port);
            }

            // everything that needs to be populated or kept up-to-date
            // lazily is handled via background maintenance threads running periodically
            StartMaintenance();

            return(true);
        }