/// <summary>This helps us leave the Get early if we either have no results or /// our remaining results will not reach a majority due to too many nodes /// missing data. This closes the clients returns queue.</summary> /// <param name="adgs">The AsDhtGetState to qualify for leaving early</param> protected void GetLeaveEarly(AsDhtGetState adgs) { int left = adgs.queueMapping.Count; // Maybe we can leave early bool got_all_values = true; foreach (DictionaryEntry de in adgs.results) { int val = ((Hashtable)de.Value).Count; if (val < MAJORITY && ((val + left) >= MAJORITY)) { got_all_values = false; break; } } // If we got to leave early, we must clean up if (got_all_values) { if (Dht.DhtLog.Enabled) { ProtocolLog.Write(Dht.DhtLog, String.Format( "GetLeaveEarly found:left:total = {0}:{1}:{2}", adgs.results.Count, left, DEGREE)); } adgs.returns.Close(); adgs.GotToLeaveEarly = true; } }
/// <summary>Restores any of the Dht results that don't return all their /// values. We only get here at the end of a Dht return operation.</summary> /// <remarks>This analyzes the holes and fills them in individually. This only /// fills holes where there was a positive result (MAJORITY of results /// received).</remarks> /// <param name="adgs">The AsDhtGetState to analyze for follow up.</param> protected void GetFollowUp(AsDhtGetState adgs) { foreach (DictionaryEntry de in adgs.results) { if (de.Value == null || de.Key == null) { continue; } Hashtable res = (Hashtable)de.Value; if (res.Count < MAJORITY || res.Count == DEGREE) { if (res.Count < MAJORITY) { if (Dht.DhtLog.Enabled) { ProtocolLog.Write(Dht.DhtLog, String.Format( "Failed get count:total = {0}:{1}", res.Count, DEGREE)); } } res.Clear(); continue; } MemBlock value = (MemBlock)de.Key; int ttl = (int)adgs.ttls[value] / res.Count; if (Dht.DhtLog.Enabled) { ProtocolLog.Write(Dht.DhtLog, String.Format( "Doing follow up put count:total = {0}:{1}", res.Count, DEGREE)); } for (int i = 0; i < DEGREE; i++) { if (!res.Contains(i)) { MemBlock key = adgs.brunet_address_for_key[i]; Channel queue = new Channel(); Address target = new AHAddress(key); AHSender s = new AHGreedySender(Node, target); try { _rpc.Invoke(s, queue, "dht.Put", key, value, ttl, false); } catch (Exception) {} } } res.Clear(); } adgs.ttls.Clear(); adgs.results.Clear(); }
/// <remarks>This starts the get process by sending dht.Get to all the remote /// end points that contain the key we're looking up. The next step is /// is when the results are placed in the channel and GetEnqueueHandler is /// called or GetCloseHandler is called. This means the get needs to be /// stateful, that information is stored in the _adgs_table.</remarks> public void AsyncGet(MemBlock key, Channel returns) { if (!_online) { throw new DhtException("The Node is (going) offline, DHT is offline."); } // create a GetState and map in our table map its queues to it // so when we get a GetHandler we know which state to load AsDhtGetState adgs = new AsDhtGetState(returns); Channel[] q = new Channel[DEGREE]; lock (_adgs_table.SyncRoot) { for (int k = 0; k < DEGREE; k++) { Channel queue = new Channel(1); _adgs_table[queue] = adgs; q[k] = queue; } } // Setting up our Channels for (int k = 0; k < DEGREE; k++) { Channel queue = q[k]; queue.EnqueueEvent += this.GetEnqueueHandler; queue.CloseEvent += this.GetCloseHandler; adgs.queueMapping[queue] = k; } // Sending off the request! adgs.brunet_address_for_key = MapToRing(key); for (int k = 0; k < DEGREE; k++) { Address target = new AHAddress(adgs.brunet_address_for_key[k]); AHSender s = new AHGreedySender(Node, target); // 1024 is in there for backwards compatibility _rpc.Invoke(s, q[k], "dht.Get", adgs.brunet_address_for_key[k], 1024, null); } }
/// <summary>This is called by the Get callbacks when all the results for a /// get have come in. This looks at the results, finds holes, and does a /// follow up put to place the data back into the dht via GetFollowUp. /// </summary> /// <param name="o">The channel representing a specific get.</param> /// <param name="args">Unused.</param> protected void GetCloseHandler(object o, EventArgs args) { Channel queue = (Channel)o; queue.EnqueueEvent -= this.GetEnqueueHandler; queue.CloseEvent -= this.GetCloseHandler; // Looking up state AsDhtGetState adgs = (AsDhtGetState)_adgs_table[queue]; if (adgs == null) { return; } int count = 0; lock (adgs.SyncRoot) { adgs.queueMapping.Remove(queue); count = adgs.queueMapping.Count; } lock (_adgs_table.SyncRoot) { _adgs_table.Remove(queue); } if (count == 0) { adgs.returns.Close(); GetFollowUp(adgs); } else if (count < MAJORITY && !adgs.GotToLeaveEarly) { lock (adgs.SyncRoot) { if (!adgs.GotToLeaveEarly) { GetLeaveEarly(adgs); } } } }
/// <summary>This is called as a result of a successful retrieval of data /// from a remote end point and performs follow up gets for remaining values /// </summary> /// <remarks>This adds the results to the entry in the _adgs_table. Once a /// value has been received by a majority of nodes, it is enqueued into the /// requestors returns channel. If not all results were retrieved follow up /// gets are performed, this is determined by looking at the state of the /// token, a non-null token implies there are remaining results.</remarks> /// <param name="o">The channel used to store the results.</param> /// <param name="args">Unused.</param> public void GetEnqueueHandler(Object o, EventArgs args) { Channel queue = (Channel)o; // Looking up state AsDhtGetState adgs = (AsDhtGetState)_adgs_table[queue]; if (adgs == null) { return; } int idx = (int)adgs.queueMapping[queue]; // Test to see if we got any results and place them into results if necessary ISender sendto = null; MemBlock token = null; try { RpcResult rpc_reply = (RpcResult)queue.Dequeue(); ArrayList result = (ArrayList)rpc_reply.Result; //Result may be corrupted if (result == null) { throw new Exception("Invalid result"); } ArrayList values = (ArrayList)result[0]; int remaining = (int)result[1]; if (remaining > 0) { token = MemBlock.Reference((byte[])result[2]); sendto = rpc_reply.ResultSender; } // Going through the return values and adding them to our // results, if a majority of our servers say a data exists // we say it is a valid data and return it to the caller foreach (Hashtable ht in values) { MemBlock mbVal = MemBlock.Reference((byte[])ht["value"]); int count = 1; Hashtable res = null; lock (adgs.SyncRoot) { res = (Hashtable)adgs.results[mbVal]; if (res == null) { res = new Hashtable(); adgs.results[mbVal] = res; adgs.ttls[mbVal] = ht["ttl"]; } else { adgs.ttls[mbVal] = (int)adgs.ttls[mbVal] + (int)ht["ttl"]; } res[idx] = true; count = ((ICollection)adgs.results[mbVal]).Count; } if (count == MAJORITY) { ht["ttl"] = (int)adgs.ttls[mbVal] / MAJORITY; adgs.returns.Enqueue(ht); } } } catch (Exception) { sendto = null; token = null; } // We were notified that more results were available! Let's go get them! if (token != null && sendto != null) { Channel new_queue = new Channel(1); lock (adgs.SyncRoot) { adgs.queueMapping[new_queue] = idx; } lock (_adgs_table.SyncRoot) { _adgs_table[new_queue] = adgs; } new_queue.EnqueueEvent += this.GetEnqueueHandler; new_queue.CloseEvent += this.GetCloseHandler; try { _rpc.Invoke(sendto, new_queue, "dht.Get", adgs.brunet_address_for_key[idx], token); } catch (Exception) { lock (adgs.SyncRoot) { adgs.queueMapping.Remove(new_queue); } lock (_adgs_table.SyncRoot) { _adgs_table.Remove(new_queue); } new_queue.EnqueueEvent -= this.GetEnqueueHandler; new_queue.CloseEvent -= this.GetCloseHandler; } } }
/// <summary>Restores any of the Dht results that don't return all their /// values. We only get here at the end of a Dht return operation.</summary> /// <remarks>This analyzes the holes and fills them in individually. This only /// fills holes where there was a positive result (MAJORITY of results /// received).</remarks> /// <param name="adgs">The AsDhtGetState to analyze for follow up.</param> protected void GetFollowUp(AsDhtGetState adgs) { foreach (DictionaryEntry de in adgs.results) { if(de.Value == null || de.Key == null) { continue; } Hashtable res = (Hashtable) de.Value; if(res.Count < MAJORITY || res.Count == DEGREE) { if(res.Count < MAJORITY) { if(Dht.DhtLog.Enabled) { ProtocolLog.Write(Dht.DhtLog, String.Format( "Failed get count:total = {0}:{1}", res.Count, DEGREE)); } } res.Clear(); continue; } MemBlock value = (MemBlock) de.Key; int ttl = (int) adgs.ttls[value] / res.Count; if(Dht.DhtLog.Enabled) { ProtocolLog.Write(Dht.DhtLog, String.Format( "Doing follow up put count:total = {0}:{1}", res.Count, DEGREE)); } for(int i = 0; i < DEGREE; i++) { if(!res.Contains(i)) { MemBlock key = adgs.brunet_address_for_key[i]; Channel queue = new Channel(); Address target = new AHAddress(key); AHSender s = new AHSender(Node, target, AHPacket.AHOptions.Greedy); try { _rpc.Invoke(s, queue, "dht.Put", key, value, ttl, false); } catch(Exception) {} } } res.Clear(); } adgs.ttls.Clear(); adgs.results.Clear(); }
/// <summary>This helps us leave the Get early if we either have no results or /// our remaining results will not reach a majority due to too many nodes /// missing data. This closes the clients returns queue.</summary> /// <param name="adgs">The AsDhtGetState to qualify for leaving early</param> protected void GetLeaveEarly(AsDhtGetState adgs) { int left = adgs.queueMapping.Count; // Maybe we can leave early bool got_all_values = true; foreach (DictionaryEntry de in adgs.results) { int val = ((Hashtable) de.Value).Count; if(val < MAJORITY && ((val + left) >= MAJORITY)) { got_all_values = false; break; } } // If we got to leave early, we must clean up if(got_all_values) { if(Dht.DhtLog.Enabled) { ProtocolLog.Write(Dht.DhtLog, String.Format( "GetLeaveEarly found:left:total = {0}:{1}:{2}", adgs.results.Count, left, DEGREE)); } adgs.returns.Close(); adgs.GotToLeaveEarly = true; } }
/// <remarks>This starts the get process by sending dht.Get to all the remote /// end points that contain the key we're looking up. The next step is /// is when the results are placed in the channel and GetEnqueueHandler is /// called or GetCloseHandler is called. This means the get needs to be /// stateful, that information is stored in the _adgs_table.</remarks> public void AsyncGet(MemBlock key, Channel returns) { if(!_online) { throw new DhtException("The Node is (going) offline, DHT is offline."); } // create a GetState and map in our table map its queues to it // so when we get a GetHandler we know which state to load AsDhtGetState adgs = new AsDhtGetState(returns); Channel[] q = new Channel[DEGREE]; lock(_adgs_table.SyncRoot) { for (int k = 0; k < DEGREE; k++) { Channel queue = new Channel(1); _adgs_table[queue] = adgs; q[k] = queue; } } // Setting up our Channels for (int k = 0; k < DEGREE; k++) { Channel queue = q[k]; queue.EnqueueEvent += this.GetEnqueueHandler; queue.CloseEvent += this.GetCloseHandler; adgs.queueMapping[queue] = k; } // Sending off the request! adgs.brunet_address_for_key = MapToRing(key); for (int k = 0; k < DEGREE; k++) { Address target = new AHAddress(adgs.brunet_address_for_key[k]); AHSender s = new AHSender(Node, target, AHPacket.AHOptions.Greedy); // 1024 is in there for backwards compatibility _rpc.Invoke(s, q[k], "dht.Get", adgs.brunet_address_for_key[k], 1024, null); } }