Beispiel #1
0
        public void TestGetTwoOnlyTwoQuick()
        {
            var x = new ConsistentHash();

            x.Add("abcdefg");
            x.Add("hijklmn");

            Quick.Check <string>(s =>
            {
                var(a, b) = x.GetTwo(s);

                if (a == b)
                {
                    return(false);
                }

                if (a != "abcdefg" && a != "hijklmn")
                {
                    return(false);
                }

                if (b != "abcdefg" && b != "hijklmn")
                {
                    return(false);
                }

                return(true);
            });
        }
Beispiel #2
0
        /// <summary>
        /// 选择一个地址。
        /// </summary>
        /// <param name="context">地址选择上下文。</param>
        /// <returns>地址模型。</returns>
        protected override async Task <AddressModel> SelectAsync(AddressSelectContext context)
        {
            var key          = GetCacheKey(context.Descriptor);
            var addressEntry = _concurrent.GetOrAdd(key, k =>
            {
                var len  = context.Address.Count();
                len      = len > 1 && len < 10 ? len * 10 : len;
                var hash = new ConsistentHash <AddressModel>(_hashAlgorithm, len);
                foreach (var address in context.Address)
                {
                    hash.Add(address, address.ToString());
                }
                return(hash);
            });
            AddressModel addressModel;
            var          IsHealth = false;

            do
            {
                addressModel = addressEntry.GetItemNode(context.Item);
                IsHealth     = await _healthCheckService.IsHealth(addressModel);

                if (!IsHealth)
                {
                    addressEntry.Remove(addressModel.ToString());
                    _unHealths.Add(new ValueTuple <string, AddressModel>(key, addressModel));
                    _healthCheckService.Changed += ItemNode_Changed;
                }
            } while (!IsHealth);
            return(addressModel);
        }
        public void AddBulkPerformance()
        {
            var results = "";

            foreach (HashProvider provider in Enum.GetValues(typeof(HashProvider)))
            {
                long     itemCount = 0;
                DateTime startTime = DateTime.Now;
                var      c         = new ConsistentHash <HashableString>(provider);

                while ((startTime + TestLength) > DateTime.Now)
                {
                    var item = new HashableString(Guid.NewGuid().ToString());
                    item.ComputeHash(provider);
                    c.Add(item, false, 0);
                    itemCount++;
                }

                c.UpdateKeyArray();

                results += GetResultString(provider, itemCount);
            }

            Assert.Pass(results);
        }
Beispiel #4
0
 public KafkaEventBus(
     IServiceProvider serviceProvider,
     IKafkaEventBusContainer eventBusContainer,
     string topic, int lBCount = 1)
 {
     if (string.IsNullOrEmpty(topic))
     {
         throw new ArgumentNullException(nameof(topic));
     }
     if (lBCount < 1)
     {
         throw new ArgumentOutOfRangeException($"{nameof(lBCount)} must be greater than 1");
     }
     ServiceProvider       = serviceProvider;
     observerUnitContainer = serviceProvider.GetService <IObserverUnitContainer>();
     Container             = eventBusContainer;
     Topic   = topic;
     LBCount = lBCount;
     Topics  = new List <string>();
     if (LBCount == 1)
     {
         Topics.Add(Topic);
     }
     else
     {
         for (int i = 0; i < LBCount; i++)
         {
             Topics.Add($"{Topic }_{ i.ToString()}");
         }
     }
     _CHash = new ConsistentHash(Topics, lBCount * 10);
 }
        public void TestDistribution()
        {
            var i1             = new Mock <ITestInterface>();
            var i2             = new Mock <ITestInterface>();
            var consistentHash = new ConsistentHash <ITestInterface>(new[] { i1.Object, i2.Object });

            var iterations = 10000;
            var hash       = new SHA256Managed();
            var random     = new RNGCryptoServiceProvider();

            for (var i = 0; i < iterations; i++)
            {
                var bytes = hash.ComputeHash(BitConverter.GetBytes(i));

                var randomBytes = new byte[10];
                random.GetBytes(randomBytes);

                var            key = Convert.ToBase64String(bytes.Concat(randomBytes).ToArray());
                ITestInterface node;
                if (!consistentHash.TryGetNode(key, out node))
                {
                    Assert.Fail("Wat, didn't return node?");
                }

                node.Balls();
            }

            i1.Verify(a => a.Balls(), Times.AtLeast(4000));
            i2.Verify(a => a.Balls(), Times.AtLeast(4000));
        }
        /// <summary>
        /// TBD
        /// </summary>
        /// <param name="pubSubMediator">TBD</param>
        /// <param name="settings">TBD</param>
        /// <exception cref="ArgumentException">TBD</exception>
        public ClusterReceptionist(IActorRef pubSubMediator, ClusterReceptionistSettings settings)
        {
            _log            = Context.GetLogger();
            _pubSubMediator = pubSubMediator;
            _settings       = settings;
            _cluster        = Cluster.Get(Context.System);

            if (!(_settings.Role == null || _cluster.SelfRoles.Contains(_settings.Role)))
            {
                throw new ArgumentException($"This cluster member [{_cluster.SelfAddress}] does not have a role [{_settings.Role}]");
            }

            _nodes = ImmutableSortedSet <Address> .Empty.WithComparer(RingOrdering.Instance);

            _virtualNodesFactor = 10;
            _consistentHash     = ConsistentHash.Create(_nodes, _virtualNodesFactor);

            _clientInteractions = ImmutableDictionary <IActorRef, DeadlineFailureDetector> .Empty;
            _clientsPublished   = ImmutableHashSet <IActorRef> .Empty;
            _subscribers        = ImmutableList <IActorRef> .Empty;
            _checkDeadlinesTask = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(
                _settings.FailureDetectionInterval,
                _settings.FailureDetectionInterval,
                Self,
                CheckDeadlines.Instance,
                Self);
        }
Beispiel #7
0
        public void Init(IRabbitMQClient client)
        {
            var isOnce = QueueCount == 1;

            if (isOnce)
            {
                models.Add(Queue, client.PullModel().GetAwaiter().GetResult());
            }
            else
            {
                nodeList = new List <string>();
                for (int i = 0; i < QueueCount; i++)
                {
                    var queue = $"{ Queue}_{i}";
                    nodeList.Add(queue);
                    if (!models.ContainsKey(queue))
                    {
                        models.Add(queue, client.PullModel().GetAwaiter().GetResult());
                    }
                }
                _CHash = new ConsistentHash(nodeList, QueueCount * 10);
            }
            //申明exchange
            client.ExchangeDeclare(Exchange).Wait();
            Client = client;
        }
        public MemcachedSharpClientCluster(IEnumerable <string> endPoints, MemcachedOptions options = null)
        {
            _timer   = new Timer(state => AsyncContext.Run(() => AwakenDeadServers()), null, 10000, 10000);
            _clients = endPoints.Select(a => new MemcachedSharpClient(a, options)).ToList();

            _nodes = new ConsistentHash <MemcachedSharpClient>(_clients);
        }
Beispiel #9
0
        public void TestSet()
        {
            var x = new ConsistentHash();

            x.Add("abc");
            x.Add("def");
            x.Add("ghi");

            {
                x.Set(new[] { "jkl", "mno" });

                Assert.AreEqual(2, x.Count);

                var(a, b) = x.GetTwo("qwerqwerwqer");
                Assert.IsTrue(a == "jkl" || a == "mno");
                Assert.IsTrue(b == "jkl" || b == "mno");
                Assert.AreNotEqual(a, b);
            }

            {
                x.Set(new[] { "pqr", "mno" });

                Assert.AreEqual(2, x.Count);

                var(a, b) = x.GetTwo("qwerqwerwqer");
                Assert.IsTrue(a == "pqr" || a == "mno");
                Assert.IsTrue(b == "pqr" || b == "mno");
                Assert.AreNotEqual(a, b);
            }
        }
        public void ConsistantHash_Smoke_Test()
        {
            var hash = new ConsistentHash <int>();

            hash.AddNode(15);
            hash.AddNode(25);
            hash.AddNode(172);

            for (int i = 200; i < 300; i++)
            {
                hash.AddNode(i);
            }

            hash.RemoveNode(15);
            hash.RemoveNode(172);
            hash.RemoveNode(25);

            var rnd = new Random();

            for (int i = 0; i < 1000; i++)
            {
                Assert.AreNotEqual(15, hash.GetNode(rnd.Next().ToString()));
                Assert.AreNotEqual(25, hash.GetNode(rnd.Next().ToString()));
                Assert.AreNotEqual(172, hash.GetNode(rnd.Next().ToString()));

                var t = hash.GetNode(rnd.Next().ToString());
                Assert.IsTrue(t >= 200 && t < 300);
            }
        }
        public void WeightedAddRemove()
        {
            int rounds    = 1000;
            var c         = new ConsistentHash <HashableString>(HashProvider.SHA384);
            var firstItem = new HashableString(Guid.NewGuid().ToString());

            firstItem.ComputeHash(c.Provider, null);

            c.Add(firstItem, true, rounds);
            Assert.True(c.ContainsNode(firstItem.ComputedHash));

            var rehashed = firstItem.ComputedHash.Rehash();

            for (int i = 1; i < rounds; i++)
            {
                rehashed = firstItem.ComputedHash.Rehash();
                Assert.True(c.ContainsNode(rehashed));
            }

            c.Remove(firstItem.ComputedHash, true);
            Assert.False(c.ContainsNode(firstItem.ComputedHash));
            Assert.True(c.NodeCount == 0);

            rehashed = firstItem.ComputedHash.Rehash();
            for (int i = 1; i < rounds; i++)
            {
                rehashed = firstItem.ComputedHash.Rehash();
                Assert.False(c.ContainsNode(rehashed));
            }
        }
        public void AddRange()
        {
            var c      = new ConsistentHash <HashableString>(HashProvider.SHA384);
            int rounds = 1000;

            var items = new List <HashableString>();

            for (int i = 0; i < rounds; i++)
            {
                var item = new HashableString(Guid.NewGuid().ToString());
                item.ComputeHash(c.Provider, null);
                items.Add(item);
            }

            c.AddRange(items, true, 0);

            foreach (var item in items)
            {
                Assert.True(c.ContainsNode(item.ComputedHash));
            }

            Assert.True(c.NodeCount == items.Count);

            foreach (var item in items)
            {
                c.Remove(item);
                Assert.False(c.ContainsNode(item.ComputedHash));
            }

            Assert.True(c.NodeCount == 0);
        }
Beispiel #13
0
        /// <summary>
        /// 初始化设置哈希节点容器
        /// </summary>
        private void InitSettingHashStorage()
        {
            foreach (var dataContext in DataContextPool)
            {
                CacheTargetType targetType;
                if (!Enum.TryParse(dataContext.Key, true, out targetType))
                {
                    continue;
                }

                var hash = new ConsistentHash <ConsistentHashNode>(Ioc.Create <IHashAlgorithm>());
                hash.Add(new ConsistentHashNode()
                {
                    Type     = targetType,
                    Host     = dataContext.Value.Host,
                    Port     = dataContext.Value.Port.ToString(),
                    UserName = dataContext.Value.UserName,
                    Password = dataContext.Value.Password,
                    MaxSize  = dataContext.Value.MaxSize.ToString(),
                    MinSize  = dataContext.Value.MinSize.ToString(),
                    Db       = dataContext.Value.Db.ToString()
                });
                DicHash.GetOrAdd(targetType.ToString(), hash);
            }
        }
Beispiel #14
0
 public KafkaEventBus(
     IObserverUnitContainer observerUnitContainer,
     IKafkaEventBusContainer eventBusContainer,
     string topic,
     int lBCount    = 1,
     bool reenqueue = true)
 {
     if (string.IsNullOrEmpty(topic))
     {
         throw new ArgumentNullException(nameof(topic));
     }
     if (lBCount < 1)
     {
         throw new ArgumentOutOfRangeException($"{nameof(lBCount)} must be greater than 1");
     }
     this.observerUnitContainer = observerUnitContainer;
     Container = eventBusContainer;
     Topic     = topic;
     LBCount   = lBCount;
     Reenqueue = reenqueue;
     Topics    = new List <string>();
     if (LBCount == 1)
     {
         Topics.Add(Topic);
     }
     else
     {
         for (int i = 0; i < LBCount; i++)
         {
             Topics.Add($"{Topic }_{ i.ToString()}");
         }
     }
     _CHash = new ConsistentHash(Topics, lBCount * 10);
 }
Beispiel #15
0
        public void ConsistentHashingGroup_must_use_same_hash_ring_independent_of_local_and_remote_nodes()
        {
            var a1         = new Address("akka.tcp", "RemoteConsistentHashingRouterSpec-1", "client1", 2552);
            var a2         = new Address("akka.tcp", "RemoteConsistentHashingRouterSpec-1", "client2", 2552);
            var localActor = Sys.ActorOf(Props.Empty, "a");

            var s1     = new ActorRefRoutee(localActor);
            var s2     = new ActorSelectionRoutee(Sys.ActorSelection("akka.tcp://RemoteConsistentHashingRouterSpec-1@client2:2552/user/a"));
            var nodes1 = new List <ConsistentRoutee>(new[] { new ConsistentRoutee(s1, a1), new ConsistentRoutee(s2, a1) });

            var s4 = new ActorSelectionRoutee(Sys.ActorSelection("akka.tcp://RemoteConsistentHashingRouterSpec-1@client1:2552/user/a"));
            var s5 = new ActorRefRoutee(localActor);

            var nodes2 = new List <ConsistentRoutee>(new[] { new ConsistentRoutee(s5, a2), new ConsistentRoutee(s4, a2) });

            var consistentHash1 = ConsistentHash.Create(nodes1, 10);
            var consistentHash2 = ConsistentHash.Create(nodes2, 10);
            var keys            = new List <string>(new[] { "A", "B", "C", "D", "E", "F", "G" });

            var result1 = keys
                          .Select(k => consistentHash1.NodeFor(k))
                          .Select(routee => routee.ToString())
                          .ToArray();

            var result2 = keys
                          .Select(k => consistentHash2.NodeFor(k))
                          .Select(routee => routee.ToString())
                          .ToArray();

            result1
            .ShouldOnlyContainInOrder(result2);
        }
Beispiel #16
0
        public void TestGetMultipleRemove()
        {
            var x = new ConsistentHash();

            x.Add("abcdefg");
            x.Add("hijklmn");
            x.Add("opqrstu");

            foreach (var gmtest in new[]
            {
                // new[] { in, out }
                new[] { "ggg", "abcdefg" },
                new[] { "hhh", "opqrstu" },
                new[] { "iiiii", "hijklmn" }
            })
            {
                Assert.AreEqual(gmtest[1], x.Get(gmtest[0]));
            }

            x.Remove("hijklmn");

            foreach (var gmtest in new[]
            {
                // new[] { in, out }
                new[] { "ggg", "abcdefg" },
                new[] { "hhh", "opqrstu" },
                new[] { "iiiii", "opqrstu" }
            })
            {
                Assert.AreEqual(gmtest[1], x.Get(gmtest[0]));
            }
        }
Beispiel #17
0
        public void TestRemoveNonExisting()
        {
            var x = new ConsistentHash();

            x.Add("abcdefg");
            x.Remove("abcdefghijk");
            Assert.AreEqual(20, x.Circle.Count);
        }
Beispiel #18
0
        private static void Dump(string[] users, ConsistentHash c)
        {
            foreach (var user in users)
            {
                Console.WriteLine(user + "=>" + c.Get(user));
            }

            Console.WriteLine();
        }
        public void AddOne()
        {
            var chash = new ConsistentHash<string>();

              chash.Add("server1");

              Assert.That(chash.GetNext("key1"), Is.EqualTo("server1"));
              Assert.That(chash.GetNext("key2"), Is.EqualTo("server1"));
              Assert.That(chash.GetNext("key3"), Is.EqualTo("server1"));
        }
Beispiel #20
0
 public void Init()
 {
     for (int i = 0; i < QueueCount; i++)
     {
         nodeList.Add(Queue + "_" + i);
     }
     _CHash = new ConsistentHash(nodeList, QueueCount * 10);
     //申明exchange
     RabbitMQClient.ExchangeDeclare(this.Exchange).Wait();
 }
Beispiel #21
0
        public void TestRemove()
        {
            var x = new ConsistentHash();

            x.Add("abcdefg");
            x.Remove("abcdefg");

            Assert.AreEqual(0, x.Circle.Count);
            Assert.AreEqual(0, x.SortedHashes.Length);
        }
Beispiel #22
0
        public void TestCompare()
        {
            var x = new ConsistentHash(new byte[2] {
                0xff, 0xfe
            });
            var y = new ConsistentHash(new byte[2] {
                0xff, 0x00
            });

            Assert.True(x > y);
        }
Beispiel #23
0
        public void TestToStringFromString()
        {
            var x = new ConsistentHash(new byte[2] {
                0xff, 0xfe
            });

            Console.WriteLine(x.ToString());
//            Assert.That(x.ToString(), Is.EqualTo("fffe"));
            var y = ConsistentHash.New("fffe");
//            Assert.True(x == y);
        }
		public ShardedRedisClientManager(params ShardedConnectionPool[] connectionPools)
		{
			if (connectionPools == null) throw new ArgumentNullException("connection pools can not be null.");

			List<KeyValuePair<ShardedConnectionPool, int>> pools = new List<KeyValuePair<ShardedConnectionPool, int>>();
			foreach (var connectionPool in connectionPools)
			{
				pools.Add(new KeyValuePair<ShardedConnectionPool, int>(connectionPool, connectionPool.weight));
			}
			_consistentHash = new ConsistentHash<ShardedConnectionPool>(pools);
		}
Beispiel #25
0
        public static RoutingTableEntry[] CreateEntries(int entryCount, ConsistentHash nodeHash)
        {
            var entries = RoutingTable.CreateEntries(entryCount);

            for (int i = 0; i < entryCount; ++i)
            {
                var finger = nodeHash + nodeHash.One() << i;
                entries[i] = new RoutingTableEntry(finger, null);
            }

            return(entries);
        }
Beispiel #26
0
        public void TestGetSingle()
        {
            var x = new ConsistentHash();

            x.Add("abcdefg");

            Quick.Check <string>(s =>
            {
                var y = x.Get(s);
                return(y == "abcdefg");
            });
        }
Beispiel #27
0
        public void Test()
        {
            string[] array = { "AAA", "BBB", "CCC", "DDD" };
            string[] array2 = { array[0], array[1], array[2] };
            ConsistentHash<string> s = new ConsistentHash<string>(array);
            s.Add(array[3]);
            for(int i = 0; i < 100; i++)
            {
                Assert.Contains(s.GetNode(i.ToString()), array);
            }

        }
Beispiel #28
0
        public void TestGetTwoOnlyOneInCircle()
        {
            var x = new ConsistentHash();

            x.Add("abcdefg");

            var(a, b) = x.GetTwo("99999999");
            Assert.AreNotEqual(a, b);

            Assert.AreEqual("abcdefg", a);
            Assert.AreEqual(default(string), b);
        }
        private static void Main(string[] args)
        {
            ConsistentHash<string> chash = new ConsistentHash<string>();

              Console.WriteLine("Adding 10 servers.");

              for (int i = 0; i < 10; i++) {
            chash.Add("server"+i);
              }

              Console.WriteLine("Key distributions:");

              var counts = new SortedDictionary<string, int>();

              for (int i = 0; i < 1000; i++) {
            var key = "key"+i;
            var server = chash.GetNext(key);

            if (!counts.ContainsKey(server)) {
              counts[server] = 0;
            }

            counts[server]++;
              }

              foreach (var server in counts.Keys) {
            Console.WriteLine("There are {0} keys assigned to {1}", counts[server], server);
              }

              Console.WriteLine("Adding 1 more server.");

              chash.Add("serverX");

              Console.WriteLine("New key distributions:");

              counts.Clear();

              for (int i = 0; i < 1000; i++) {
            var key = "key"+i;
            var server = chash.GetNext(key);

            if (!counts.ContainsKey(server)) {
              counts[server] = 0;
            }

            counts[server]++;
              }

              foreach (var server in counts.Keys) {
            Console.WriteLine("There are {0} keys assigned to {1}", counts[server], server);
              }
        }
        public ClusterReceptionist(IActorRef pubSubMediator, ClusterReceptionistSettings settings)
        {
            _pubSubMediator = pubSubMediator;
            _settings       = settings;

            if (!(_settings.Role == null || _cluster.SelfRoles.Contains(_settings.Role)))
            {
                throw new ArgumentException(string.Format("This cluster member [{0}] does not have a role [{1}]", _cluster.SelfAddress, _settings.Role));
            }

            _nodes          = GetNodes();
            _consistentHash = new ConsistentHash <Address>(_nodes, VirtualNodesFactor);
        }
Beispiel #31
0
        protected override T DoSelectEqual <T>(List <T> equalItems, string key)
        {
            var itemsHash = equalItems.GetHashCode().ToString();
            ConsistentHash <object> selector;

            if (!_selectors.TryGetValue(itemsHash, out selector))
            {
                selector = new ConsistentHash <object>(equalItems.Select(item => item as object));
                _selectors.TryAdd(itemsHash, selector);
            }

            return((T)selector.GetNode(key));
        }
        public void GetNode(int count)
        {
            var consHash = new ConsistentHash(Enumerable.Range(0, count).Select(o => o.ToString()));
            var nodes    = new List <string>();

            for (int i = 0; i < count; i++)
            {
                nodes.Add(consHash.GetNode(i.ToString()));
            }
            for (int i = 0; i < count; i++)
            {
                Assert.Equal(nodes[i], consHash.GetNode(i.ToString()));
            }
        }
        public NodeInfo FindClosestPrecedingFinger(ConsistentHash toNode)
        {
            //Check finger tables
            for (int i = Entries.Length - 1; i >= 0; --i)
            {
                if (Node.IsIdInDomain(Entries[i].SuccessorIdentity.RoutingHash, Identity.RoutingHash, toNode))
                {
                    return(Entries[i].SuccessorIdentity);
                }
            }
            // Check successors

            return(Identity);
        }
        /// <summary>
        /// 获取结点
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public T GetTarget(string key)
        {
            if (circle.Count == 0)
            {
                return(default(T));
            }

            if (circle.Count == 1)
            {
                return(circle[0]);
            }

            return(circle[ConsistentHash <T> .ModifiedBinarySearch(circle.Keys.ToArray(), hashFunction(key))]);
        }
Beispiel #35
0
        private void btnTest_Click(object sender, EventArgs e)
        {
            List<Server> servers = new List<Server>();
            for (int i = 0; i < 1000; i++)
            {
                servers.Add(new Server(i));
            }

            ConsistentHash<Server> ch = new ConsistentHash<Server>();
            ch.Init(servers);

            int search = 100000;

            DateTime start = DateTime.Now;
            SortedList<int, int> ay1 = new SortedList<int, int>();
            for (int i = 0; i < search; i++)
            {
                int temp = ch.GetNode(i.ToString()).ID;

                ay1[i] = temp;
            }
            TimeSpan ts = DateTime.Now - start;
            MessageBox.Show(search + " each use macro seconds: " + (ts.TotalMilliseconds/search)*1000);

            //ch.Add(new Server(1000));
            ch.Remove(servers[1]);
            SortedList<int, int> ay2 = new SortedList<int, int>();
            for (int i = 0; i < search; i++)
            {
                int temp = ch.GetNode(i.ToString()).ID;

                ay2[i] = temp;
            }

            int diff = 0;
            for (int i = 0; i < search; i++)
            {
                if (ay1[i] != ay2[i])
                {
                    diff++;
                }
            }

            MessageBox.Show("diff: " + diff);
        }
        public void AddRemoveMultiple()
        {
            var chash = new ConsistentHash<string>();

              chash.Add("server1");
              chash.Add("server2");
              chash.Add("server3");

              Assert.Contains(chash.GetNext("key1"), new string[] {"server1", "server2", "server3"});
              Assert.Contains(chash.GetNext("key2"), new string[] {"server1", "server2", "server3"});
              Assert.Contains(chash.GetNext("key3"), new string[] {"server1", "server2", "server3"});

              chash.Add("server4");

              Assert.Contains(chash.GetNext("key1"), new string[] {"server1", "server2", "server3", "server4"});
              Assert.Contains(chash.GetNext("key2"), new string[] {"server1", "server2", "server3", "server4"});
              Assert.Contains(chash.GetNext("key3"), new string[] {"server1", "server2", "server3", "server4"});
              Assert.Contains(chash.GetNext("key4"), new string[] {"server1", "server2", "server3", "server4"});

              chash.Remove("server2");

              Assert.Contains(chash.GetNext("key1"), new string[] {"server1", "server3", "server4"});
              Assert.Contains(chash.GetNext("key2"), new string[] {"server1", "server3", "server4"});
              Assert.Contains(chash.GetNext("key3"), new string[] {"server1", "server3", "server4"});
              Assert.Contains(chash.GetNext("key4"), new string[] {"server1", "server3", "server4"});

              chash.Remove("server1");

              Assert.Contains(chash.GetNext("key1"), new string[] {"server3", "server4"});
              Assert.Contains(chash.GetNext("key2"), new string[] {"server3", "server4"});
              Assert.Contains(chash.GetNext("key3"), new string[] {"server3", "server4"});
              Assert.Contains(chash.GetNext("key4"), new string[] {"server3", "server4"});

              chash.Remove("server3");

              Assert.Contains(chash.GetNext("key1"), new string[] {"server4"});
              Assert.Contains(chash.GetNext("key2"), new string[] {"server4"});
              Assert.Contains(chash.GetNext("key3"), new string[] {"server4"});
              Assert.Contains(chash.GetNext("key4"), new string[] {"server4"});
        }