/// <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); } } }
/// <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); }