diff --git a/src/Enyim.Caching/FnvHash.cs b/src/Enyim.Caching/FnvHash.cs
index 0481d327..2cb79926 100755
--- a/src/Enyim.Caching/FnvHash.cs
+++ b/src/Enyim.Caching/FnvHash.cs
@@ -23,6 +23,7 @@ public class FNV64 : System.Security.Cryptography.HashAlgorithm, IUIntHashAlgori
public FNV64()
{
//base.HashSize = 64;
+ Initialize();
}
///
@@ -109,6 +110,7 @@ public class FNV1 : HashAlgorithm, IUIntHashAlgorithm
///
public FNV1()
{
+ Initialize();
}
///
diff --git a/src/Enyim.Caching/Memcached/Locators/DefaultNodeLocator.cs b/src/Enyim.Caching/Memcached/Locators/DefaultNodeLocator.cs
index cc477a12..6fd95d04 100644
--- a/src/Enyim.Caching/Memcached/Locators/DefaultNodeLocator.cs
+++ b/src/Enyim.Caching/Memcached/Locators/DefaultNodeLocator.cs
@@ -172,7 +172,7 @@ private static uint[] GenerateKeys(IMemcachedNode node, int numberOfKeys)
for (int i = 0; i < numberOfKeys; i++)
{
- byte[] data = fnv.ComputeHash(Encoding.UTF8.GetBytes(String.Concat(address, "-", i)));
+ byte[] data = fnv.ComputeHash(Encoding.UTF8.GetBytes(String.Concat(i, "-", address)));
for (int h = 0; h < PartCount; h++)
{
diff --git a/test/Enyim.Caching.Tests/DefaultNodeLocatorTests.cs b/test/Enyim.Caching.Tests/DefaultNodeLocatorTests.cs
new file mode 100644
index 00000000..2a990261
--- /dev/null
+++ b/test/Enyim.Caching.Tests/DefaultNodeLocatorTests.cs
@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Net;
+using Enyim.Caching.Memcached;
+using Enyim.Caching.Memcached.Results;
+using Xunit;
+
+namespace Enyim.Caching.Tests
+{
+ public class DefaultNodeLocatorTest
+ {
+ [Fact]
+ public void FNV1a()
+ {
+ var fnv = new Enyim.FNV1a();
+
+ // FNV1a test vectors:
+ // http://www.isthe.com/chongo/src/fnv/test_fnv.c
+ var testVectors = new List>
+ {
+ new Tuple("",0x811c9dc5U),
+ new Tuple("a",0xe40c292cU),
+ new Tuple("b",0xe70c2de5U),
+ new Tuple("c",0xe60c2c52U),
+ new Tuple("d",0xe10c2473U),
+ new Tuple("e",0xe00c22e0U),
+ new Tuple("f",0xe30c2799U),
+ new Tuple("fo",0x6222e842U),
+ new Tuple("foo",0xa9f37ed7U),
+ new Tuple("foob",0x3f5076efU),
+ };
+
+ foreach (var testVector in testVectors)
+ {
+ byte[] data = fnv.ComputeHash(Encoding.ASCII.GetBytes(testVector.Item1));
+ uint value = BitConverter.ToUInt32(data, 0);
+ Assert.Equal(value, testVector.Item2);
+ }
+ }
+
+ [Fact]
+ public void TestLocator()
+ {
+ String[] servers = new[]
+ {
+ "10.0.1.1:11211",
+ "10.0.1.2:11211",
+ "10.0.1.3:11211",
+ "10.0.1.4:11211",
+ "10.0.1.5:11211",
+ "10.0.1.6:11211",
+ "10.0.1.7:11211",
+ "10.0.1.8:11211",
+ };
+ int[] serverCount = new int[servers.Length];
+
+ var nodes = servers.
+ Select(s => new MockNode(new IPEndPoint(IPAddress.Parse(s.Substring(0, s.IndexOf(":"))), 11211))).
+ Cast().
+ ToList();
+
+ IMemcachedNodeLocator locator = new DefaultNodeLocator();
+ locator.Initialize(nodes.ToList());
+
+ var keyCheckCount = 1000000;
+ var expectedKeysPerServer = keyCheckCount / nodes.Count;
+
+ var random = new Random();
+ for (int i = 0; i < keyCheckCount; i++)
+ {
+ var node = locator.Locate(random.NextDouble().ToString());
+ for (int j = 0; j < nodes.Count; j++)
+ {
+ if (nodes[j] == node)
+ {
+ serverCount[j]++;
+ break;
+ }
+ }
+ }
+
+ double maxVariation = 0;
+ for (int i = 0; i < serverCount.Length; i++)
+ {
+ var keysThisServer = serverCount[i];
+ var variation = (double)Math.Abs(keysThisServer - expectedKeysPerServer) / expectedKeysPerServer;
+ maxVariation = Math.Max(maxVariation, variation);
+ Console.WriteLine("Expected about {0} keys per server; got {1} for server {2}; variation: {3:0.0%}", expectedKeysPerServer, keysThisServer, i, variation);
+ }
+ Assert.InRange(maxVariation, 0, 0.20); // variation expected to be less than 20%
+ }
+ }
+
+ class MockNode : IMemcachedNode
+ {
+ public MockNode(IPEndPoint endpoint)
+ {
+ this.EndPoint = endpoint;
+ }
+
+ public EndPoint EndPoint { get; private set; }
+
+ public bool IsAlive => true;
+
+ public event Action Failed;
+
+ public void Dispose()
+ {
+ throw new NotImplementedException();
+ }
+
+ public IOperationResult Execute(IOperation op)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task ExecuteAsync(IOperation op)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task ExecuteAsync(IOperation op, Action next)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool Ping()
+ {
+ throw new NotImplementedException();
+ }
+ }
+}