diff --git a/SimpleStorage/Client/SimpleStorageClient.cs b/SimpleStorage/Client/SimpleStorageClient.cs index e69d4ed..4798e84 100644 --- a/SimpleStorage/Client/SimpleStorageClient.cs +++ b/SimpleStorage/Client/SimpleStorageClient.cs @@ -6,34 +6,39 @@ namespace Client { - public class SimpleStorageClient : ISimpleStorageClient - { - private readonly IEnumerable endpoints; + public class SimpleStorageClient : ISimpleStorageClient + { + private readonly IEnumerable endpoints; - public SimpleStorageClient(params string[] endpoints) - { - if (endpoints == null || !endpoints.Any()) - throw new ArgumentException("Empty endpoints!", "endpoints"); - this.endpoints = endpoints; - } + public SimpleStorageClient(params string[] endpoints) + { + if (endpoints == null || !endpoints.Any()) + throw new ArgumentException("Empty endpoints!", "endpoints"); + this.endpoints = endpoints; + } - public void Put(string id, Value value) - { - var putUri = endpoints.First() + "api/values/" + id; - using (var client = new HttpClient()) - using (var response = client.PutAsJsonAsync(putUri, value).Result) - response.EnsureSuccessStatusCode(); - } + public void Put(string id, Value value) + { + var putUri = endpoints.ElementAt(getShardNumber(id, endpoints.Count())) + "api/values/" + id; + using (var client = new HttpClient()) + using (var response = client.PutAsJsonAsync(putUri, value).Result) + response.EnsureSuccessStatusCode(); + } - public Value Get(string id) - { - var requestUri = endpoints.First() + "api/values/" + id; - using (var client = new HttpClient()) - using (var response = client.GetAsync(requestUri).Result) - { - response.EnsureSuccessStatusCode(); - return response.Content.ReadAsAsync().Result; - } - } - } + public Value Get(string id) + { + var requestUri = endpoints.ElementAt(getShardNumber(id, endpoints.Count())) + "api/values/" + id; + using (var client = new HttpClient()) + using (var response = client.GetAsync(requestUri).Result) { + response.EnsureSuccessStatusCode(); + return response.Content.ReadAsAsync().Result; + } + } + + static int getShardNumber(string id, int count) + { + return (Math.Abs(id.GetHashCode()) % count); + } + + } } \ No newline at end of file diff --git a/SimpleStorage/SimpleStorage.Tests/FuctionalTestBase.cs b/SimpleStorage/SimpleStorage.Tests/FuctionalTestBase.cs index d6145e6..20238a9 100644 --- a/SimpleStorage/SimpleStorage.Tests/FuctionalTestBase.cs +++ b/SimpleStorage/SimpleStorage.Tests/FuctionalTestBase.cs @@ -25,4 +25,4 @@ public virtual void SetUp() container.Configure(c => c.For().Use(storage)); } } -} \ No newline at end of file +} diff --git a/SimpleStorage/SimpleStorage.Tests/Sharding/Task1Tests.cs b/SimpleStorage/SimpleStorage.Tests/Sharding/Task1Tests.cs new file mode 100644 index 0000000..7b53cb1 --- /dev/null +++ b/SimpleStorage/SimpleStorage.Tests/Sharding/Task1Tests.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using Client; +using Domain; +using NUnit.Framework; + +namespace SimpleStorage.Tests.Sharding +{ + [TestFixture] + public class Task1Tests + { + private static readonly string endpoint1 = string.Format ("http://127.0.0.1:{0}/", 16000); + private static readonly string endpoint2 = string.Format ("http://127.0.0.1:{0}/", 16001); + private static readonly string endpoint3 = string.Format ("http://127.0.0.1:{0}/", 16002); + private readonly string[] endpoints = { endpoint1, endpoint2, endpoint3 }; + private SimpleStorageClient client; + + [SetUp] + public void SetUp () + { + client = new SimpleStorageClient (endpoints); + + using (var httpClient = new HttpClient ()) + foreach (var endpoint in endpoints) { + using (var response = + httpClient.PostAsync (endpoint + "api/admin/removeAllData", new ByteArrayContent (new byte[0])) + .Result) + Assert.That (response.StatusCode, Is.EqualTo (HttpStatusCode.NoContent)); + } + } + + [Test] + public void Sharding_EachShard_ShouldNotContainAllData () + { + + + Assert.That (GetAll (endpoint1).ToArray (), Has.Length.LessThan (100)); + Assert.That (GetAll (endpoint2).ToArray (), Has.Length.LessThan (100)); + Assert.That (GetAll (endpoint3).ToArray (), Has.Length.LessThan (100)); + } + + [Test] + public void Sharding_AllShards_ShouldContainSomeData () + { + for (var i = 0; i < 100; i++) + client.Put (Guid.NewGuid ().ToString (), new Value { Content = "content" }); + + Assert.That (GetAll (endpoint1).ToArray (), Has.Length.GreaterThan (0)); + Assert.That (GetAll (endpoint2).ToArray (), Has.Length.GreaterThan (0)); + Assert.That (GetAll (endpoint3).ToArray (), Has.Length.GreaterThan (0)); + } + + [Test] + public void Sharding_Always_ShouldSaveAllData () + { + var items = new List> (); + for (var i = 0; i < 100; i++) { + var id = Guid.NewGuid ().ToString (); + var value = new Value { Content = "content" }; + items.Add (new KeyValuePair (id, value)); + client.Put (id, value); + } + + foreach (var item in items) { + var actual = client.Get (item.Key); + Assert.That (actual.Content, Is.EqualTo (item.Value.Content)); + Assert.That (actual.IsDeleted, Is.EqualTo (item.Value.IsDeleted)); + Assert.That (actual.Revision, Is.EqualTo (item.Value.Revision)); + } + } + + private IEnumerable GetAll (string endpoint) + { + var requestUri = endpoint + "api/admin/getAllLocalData"; + using (var httpClient = new HttpClient ()) + using (var response = httpClient.GetAsync (requestUri).Result) { + response.EnsureSuccessStatusCode (); + return response.Content.ReadAsAsync> ().Result; + } + } + } +} \ No newline at end of file diff --git a/SimpleStorage/SimpleStorage.Tests/Sharding/Task2Tests.cs b/SimpleStorage/SimpleStorage.Tests/Sharding/Task2Tests.cs new file mode 100644 index 0000000..0acb2c6 --- /dev/null +++ b/SimpleStorage/SimpleStorage.Tests/Sharding/Task2Tests.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using Client; +using Domain; +using NUnit.Framework; + +namespace SimpleStorage.Tests.Sharding +{ + [TestFixture] + public class Task2Tests + { + private static readonly string endpoint1 = string.Format("http://127.0.0.1:{0}/", 16000); + private static readonly string endpoint2 = string.Format("http://127.0.0.1:{0}/", 16001); + private static readonly string endpoint3 = string.Format("http://127.0.0.1:{0}/", 16002); + + [SetUp] + public void SetUp() + { + using (var httpClient = new HttpClient()) + foreach (var endpoint in new[] { endpoint1, endpoint2, endpoint3 }) + { + using (var response = + httpClient.PostAsync(endpoint + "api/admin/removeAllData", new ByteArrayContent(new byte[0])) + .Result) + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent)); + } + } + + [Test] + public void Sharding_AllShards_ShouldContainSomeData() + { + var client = new SimpleStorageClient(endpoint1); + for (var i = 0; i < 100; i++) + client.Put(Guid.NewGuid().ToString(), new Value { Content = "content" }); + + Assert.That(GetAll(endpoint1).ToArray(), Has.Length.GreaterThan(0)); + Assert.That(GetAll(endpoint2).ToArray(), Has.Length.GreaterThan(0)); + Assert.That(GetAll(endpoint3).ToArray(), Has.Length.GreaterThan(0)); + } + + [Test] + public void Sharding_Always_ShouldReadAllData() + { + var client = new SimpleStorageClient(endpoint1); + var items = new List>(); + for (var i = 0; i < 100; i++) + { + var id = Guid.NewGuid().ToString(); + var value = new Value { Content = "content" }; + items.Add(new KeyValuePair(id, value)); + client.Put(id, value); + } + + foreach (var item in items) + foreach (var endpoint in new[] { endpoint1, endpoint2, endpoint3 }) + { + var actual = new SimpleStorageClient(endpoint).Get(item.Key); + Assert.That(actual.Content, Is.EqualTo(item.Value.Content)); + Assert.That(actual.IsDeleted, Is.EqualTo(item.Value.IsDeleted)); + Assert.That(actual.Revision, Is.EqualTo(item.Value.Revision)); + } + } + + private IEnumerable GetAll(string endpoint) + { + var requestUri = endpoint + "api/admin/getAllLocalData"; + using (var httpClient = new HttpClient()) + using (var response = httpClient.GetAsync(requestUri).Result) + { + response.EnsureSuccessStatusCode(); + return response.Content.ReadAsAsync>().Result; + } + } + } +} \ No newline at end of file diff --git a/SimpleStorage/SimpleStorage.Tests/Sharding/Task3Tests.cs b/SimpleStorage/SimpleStorage.Tests/Sharding/Task3Tests.cs new file mode 100644 index 0000000..4319985 --- /dev/null +++ b/SimpleStorage/SimpleStorage.Tests/Sharding/Task3Tests.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using Client; +using Domain; +using NUnit.Framework; + +namespace SimpleStorage.Tests.Sharding +{ + [TestFixture] + public class Task3Tests + { + private static readonly string endpoint1 = string.Format("http://127.0.0.1:{0}/", 16000); + private static readonly string endpoint2 = string.Format("http://127.0.0.1:{0}/", 16001); + private static readonly string endpoint3 = string.Format("http://127.0.0.1:{0}/", 16002); + private readonly string[] endpoints = {endpoint1, endpoint2, endpoint3}; + private SimpleStorageClient client; + + [SetUp] + public void SetUp() + { + client = new SimpleStorageClient(endpoints); + + using (var httpClient = new HttpClient()) + foreach (var endpoint in endpoints) + { + using (var response = + httpClient.PostAsync(endpoint + "api/admin/removeAllData", new ByteArrayContent(new byte[0])) + .Result) + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent)); + } + } + + [Test] + public void Sharding_EachShard_ShouldNotContainAllData() + { + for (var i = 0; i < 100; i++) + client.Put(Guid.NewGuid().ToString(), new Value {Content = "content"}); + + Assert.That(GetAll(endpoint1).ToArray(), Has.Length.LessThan(100)); + Assert.That(GetAll(endpoint2).ToArray(), Has.Length.LessThan(100)); + Assert.That(GetAll(endpoint3).ToArray(), Has.Length.LessThan(100)); + } + + [Test] + public void Sharding_AllShards_ShouldContainSomeData() + { + for (var i = 0; i < 100; i++) + client.Put(Guid.NewGuid().ToString(), new Value {Content = "content"}); + + Assert.That(GetAll(endpoint1).ToArray(), Has.Length.GreaterThan(0)); + Assert.That(GetAll(endpoint2).ToArray(), Has.Length.GreaterThan(0)); + Assert.That(GetAll(endpoint3).ToArray(), Has.Length.GreaterThan(0)); + } + + [Test] + public void Sharding_Always_ShouldSaveAllData() + { + var items = new List>(); + for (var i = 0; i < 100; i++) + { + var id = Guid.NewGuid().ToString(); + var value = new Value {Content = "content"}; + items.Add(new KeyValuePair(id, value)); + client.Put(id, value); + } + + foreach (var item in items) + { + var actual = client.Get(item.Key); + Assert.That(actual.Content, Is.EqualTo(item.Value.Content)); + Assert.That(actual.IsDeleted, Is.EqualTo(item.Value.IsDeleted)); + Assert.That(actual.Revision, Is.EqualTo(item.Value.Revision)); + } + } + + private IEnumerable GetAll(string endpoint) + { + var requestUri = endpoint + "api/admin/getAllLocalData"; + using (var httpClient = new HttpClient()) + using (var response = httpClient.GetAsync(requestUri).Result) + { + response.EnsureSuccessStatusCode(); + return response.Content.ReadAsAsync>().Result; + } + } + } +} \ No newline at end of file diff --git a/SimpleStorage/SimpleStorage.Tests/ShardingTests.cs b/SimpleStorage/SimpleStorage.Tests/ShardingTests.cs index 382b62a..ceef97d 100644 --- a/SimpleStorage/SimpleStorage.Tests/ShardingTests.cs +++ b/SimpleStorage/SimpleStorage.Tests/ShardingTests.cs @@ -9,7 +9,6 @@ namespace SimpleStorage.Tests { [TestFixture] - [Ignore] public class ShardingTests { private const int port1 = 15000; diff --git a/SimpleStorage/packages/repositories.config b/SimpleStorage/packages/repositories.config index 91e8083..adeccb7 100644 --- a/SimpleStorage/packages/repositories.config +++ b/SimpleStorage/packages/repositories.config @@ -1,9 +1,15 @@ - - - - - - - - - + + + + + + + + + + + + + + + \ No newline at end of file