/// <summary> /// Removes a node from the hash ring. /// /// Note that <see cref="ConsistentHash{T}"/> is immutable and /// this operation returns a new instance. /// </summary> public static ConsistentHash <T> operator -(ConsistentHash <T> hash, T node) { var nodeHash = ConsistentHash.HashFor(node.ToString()); return(new ConsistentHash <T>(hash._nodes.CopyAndRemove(Enumerable.Range(1, hash._virtualNodesFactor).Select(r => new KeyValuePair <int, T>(ConsistentHash.ConcatenateNodeHash(nodeHash, r), node))), hash._virtualNodesFactor)); }
/// <summary> /// Get the node responsible for the data key. /// Can only be used if nodes exist in the node ring. /// Otherwise throws <see cref="ArgumentException"/>. /// </summary> public T NodeFor(string key) { if (IsEmpty) { throw new InvalidOperationException(string.Format("Can't get node for [{0}] from an empty node ring", key)); } return(NodeRing[Idx(Array.BinarySearch(NodeHashRing, ConsistentHash.HashFor(key)))]); }
/// <summary> /// Get the node responsible for the data key. /// Can only be used if nodes exist in the node ring. /// </summary> /// <exception cref="InvalidOperationException"> /// This exception is thrown if the node ring is empty. /// </exception> public T NodeFor(byte[] key) { if (IsEmpty) { throw new InvalidOperationException($"Can't get node for [{key}] from an empty node ring"); } return(NodeRing[Idx(Array.BinarySearch(NodeHashRing, ConsistentHash.HashFor(key)))]); }
public override Routee Select(object message, Routee[] routees) { if (message == null || routees == null || routees.Length == 0) { return(Routee.NoRoutee); } Func <ConsistentHash <ConsistentRoutee> > updateConsistentHash = () => { // update consistentHash when routees are changed // changes to routees are rare when no changes this is a quick operation var oldConsistHashTuple = _consistentHashRef.Value; var oldRoutees = oldConsistHashTuple.Item1; var oldConsistentHash = oldConsistHashTuple.Item2; if (oldRoutees == null || !routees.SequenceEqual(oldRoutees)) { // when other instance, same content, no need to re-hash, but try to set routees var consistentHash = routees == oldRoutees ? oldConsistentHash : ConsistentHash.Create(routees.Select(x => new ConsistentRoutee(x, _selfAddress)), _vnodes); //ignore, don't update, in case of CAS failure _consistentHashRef.CompareAndSet(oldConsistHashTuple, Tuple.Create(routees, consistentHash)); return(consistentHash); } return(oldConsistentHash); }; Func <object, Routee> target = hashData => { try { var currentConsistentHash = updateConsistentHash(); if (currentConsistentHash.IsEmpty) { return(Routee.NoRoutee); } else { if (hashData is byte[]) { return(currentConsistentHash.NodeFor(hashData as byte[]).Routee); } if (hashData is string) { return(currentConsistentHash.NodeFor(hashData as string).Routee); } return (currentConsistentHash.NodeFor( _system.Serialization.FindSerializerFor(hashData).ToBinary(hashData)).Routee); } } catch (Exception ex) { //serialization failed _log.Value.Warning("Couldn't route message with consistent hash key [{0}] due to [{1}]", hashData, ex.Message); return(Routee.NoRoutee); } }; if (_hashMapping(message) != null) { return(target(ConsistentHash.ToBytesOrObject(_hashMapping(message)))); } else if (message is IConsistentHashable) { var hashable = (IConsistentHashable)message; return(target(ConsistentHash.ToBytesOrObject(hashable.ConsistentHashKey))); } else { _log.Value.Warning( "Message [{0}] must be handled by hashMapping, or implement [{1}] or be wrapped in [{2}]", message.GetType().Name, typeof(IConsistentHashable).Name, typeof(ConsistentHashableEnvelope).Name); return(Routee.NoRoutee); } }