/// <summary> /// Picks a <see cref="Routee" /> to receive the <paramref name="message" />. /// </summary> /// <param name="message">The message that is being routed</param> /// <param name="routees">A collection of routees to choose from when receiving the <paramref name="message" />.</param> /// <returns>A <see cref="Routee" /> that receives the <paramref name="message" />.</returns> 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; } }