Repository: ignite Updated Branches: refs/heads/master 879fffa65 -> 86372dee2
http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/ScanQueryTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/ScanQueryTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/ScanQueryTest.cs new file mode 100644 index 0000000..bc1e8ee --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/ScanQueryTest.cs @@ -0,0 +1,241 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Tests.Client.Cache +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using Apache.Ignite.Core.Cache; + using Apache.Ignite.Core.Cache.Query; + using Apache.Ignite.Core.Common; + using Apache.Ignite.Core.Configuration; + using NUnit.Framework; + + /// <summary> + /// Tests scan queries. + /// </summary> + public class ScanQueryTest : ClientTestBase + { + /// <summary> + /// Initializes a new instance of the <see cref="ScanQueryTest"/> class. + /// </summary> + public ScanQueryTest() : base(2) + { + // No-op. + } + + /** <inheritdoc /> */ + protected override IgniteConfiguration GetIgniteConfiguration() + { + var cfg = base.GetIgniteConfiguration(); + + cfg.SqlConnectorConfiguration = new SqlConnectorConfiguration + { + MaxOpenCursorsPerConnection = 3 + }; + + return cfg; + } + + /// <summary> + /// Tests scan query without filter. + /// </summary> + [Test] + [SuppressMessage("ReSharper", "ReturnValueOfPureMethodIsNotUsed")] + public void TestNoFilter() + { + var cache = GetPersonCache(); + + Action<IEnumerable<ICacheEntry<int, Person>>> checkResults = e => + { + Assert.AreEqual(cache.Select(x => x.Value.Name).OrderBy(x => x).ToArray(), + e.Select(x => x.Value.Name).OrderBy(x => x).ToArray()); + }; + + using (var client = GetClient()) + { + var clientCache = client.GetCache<int, Person>(CacheName); + + var query = new ScanQuery<int, Person>(); + + // GetAll. + var cursor = clientCache.Query(query); + checkResults(cursor.GetAll()); + + // Can't iterate or call GetAll again. + Assert.Throws<InvalidOperationException>(() => cursor.ToArray()); + Assert.Throws<InvalidOperationException>(() => cursor.GetAll()); + + // Iterator. + using (cursor = clientCache.Query(query)) + { + checkResults(cursor.ToArray()); + + // Can't iterate or call GetAll again. + Assert.Throws<InvalidOperationException>(() => cursor.ToArray()); + Assert.Throws<InvalidOperationException>(() => cursor.GetAll()); + } + + // Partial iterator. + using (cursor = clientCache.Query(query)) + { + var item = cursor.First(); + Assert.AreEqual(item.Key.ToString(), item.Value.Name); + } + + // Local. + query.Local = true; + var localRes = clientCache.Query(query).ToList(); + Assert.Less(localRes.Count, cache.GetSize()); + } + } + + /// <summary> + /// Tests scan query with .NET filter. + /// </summary> + [Test] + public void TestWithFilter() + { + GetPersonCache(); + + using (var client = GetClient()) + { + var clientCache = client.GetCache<int, Person>(CacheName); + + // One result. + var single = clientCache.Query(new ScanQuery<int, Person>(new PersonFilter(x => x.Id == 3))).Single(); + Assert.AreEqual(3, single.Key); + + // Multiple results. + var res = clientCache.Query(new ScanQuery<int, Person>(new PersonFilter(x => x.Name.Length == 1))) + .ToList(); + Assert.AreEqual(9, res.Count); + + // No results. + res = clientCache.Query(new ScanQuery<int, Person>(new PersonFilter(x => x == null))).ToList(); + Assert.AreEqual(0, res.Count); + } + } + + /// <summary> + /// Tests multiple cursors with the same client. + /// </summary> + [Test] + [SuppressMessage("ReSharper", "GenericEnumeratorNotDisposed")] + public void TestMultipleCursors() + { + var cache = GetPersonCache(); + + using (var client = GetClient()) + { + var clientCache = client.GetCache<int, Person>(CacheName); + + var qry = new ScanQuery<int, Person>(); + + var cur1 = clientCache.Query(qry).GetEnumerator(); + var cur2 = clientCache.Query(qry).GetEnumerator(); + var cur3 = clientCache.Query(qry).GetEnumerator(); + + // MaxCursors = 3 + var ex = Assert.Throws<IgniteException>(() => clientCache.Query(qry)); + Assert.AreEqual("Too many open cursors", ex.Message.Substring(0, 21)); + + var count = 0; + + while (cur1.MoveNext()) + { + count++; + + Assert.IsTrue(cur2.MoveNext()); + Assert.IsTrue(cur3.MoveNext()); + + Assert.AreEqual(cur1.Current.Key, cur2.Current.Key); + Assert.AreEqual(cur1.Current.Key, cur3.Current.Key); + } + + Assert.AreEqual(cache.GetSize(), count); + + // Old cursors were auto-closed on last page, we can open new cursors now. + var c1 = clientCache.Query(qry); + var c2 = clientCache.Query(qry); + var c3 = clientCache.Query(qry); + + Assert.Throws<IgniteException>(() => clientCache.Query(qry)); + + // Close one of the cursors. + c1.Dispose(); + c1 = clientCache.Query(qry); + Assert.Throws<IgniteException>(() => clientCache.Query(qry)); + + // Close cursor via GetAll. + c1.GetAll(); + c1 = clientCache.Query(qry); + Assert.Throws<IgniteException>(() => clientCache.Query(qry)); + + c1.Dispose(); + c2.Dispose(); + c3.Dispose(); + } + } + + /// <summary> + /// Gets the string cache. + /// </summary> + private static ICache<int, Person> GetPersonCache() + { + var cache = GetCache<Person>(); + + cache.RemoveAll(); + cache.PutAll(Enumerable.Range(1, 10000).ToDictionary(x => x, x => new Person + { + Id = x, + Name = x.ToString() + })); + + return cache; + } + + /// <summary> + /// Person filter. + /// </summary> + private class PersonFilter : ICacheEntryFilter<int, Person> + { + /** Filter predicate. */ + private readonly Func<Person, bool> _filter; + + /// <summary> + /// Initializes a new instance of the <see cref="PersonFilter"/> class. + /// </summary> + /// <param name="filter">The filter.</param> + public PersonFilter(Func<Person, bool> filter) + { + Debug.Assert(filter != null); + + _filter = filter; + } + + /** <inheritdoc /> */ + public bool Invoke(ICacheEntry<int, Person> entry) + { + return _filter(entry.Value); + } + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/CacheTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/CacheTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/CacheTest.cs deleted file mode 100644 index 3b0daa1..0000000 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/CacheTest.cs +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Apache.Ignite.Core.Tests.Client -{ - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Linq; - using System.Threading; - using Apache.Ignite.Core.Binary; - using Apache.Ignite.Core.Cache; - using Apache.Ignite.Core.Client; - using NUnit.Framework; - - /// <summary> - /// Thin client cache test. - /// </summary> - public sealed class CacheTest - { - /** Cache name. */ - private const string CacheName = "cache"; - - /// <summary> - /// Fixture tear down. - /// </summary> - [TestFixtureSetUp] - public void FixtureSetUp() - { - Ignition.Start(TestUtils.GetTestConfiguration()); - } - - /// <summary> - /// Fixture tear down. - /// </summary> - [TestFixtureTearDown] - public void FixtureTearDown() - { - Ignition.StopAll(true); - } - - /// <summary> - /// Tests the cache put / get with primitive data types. - /// </summary> - [Test] - public void TestPutGetPrimitives() - { - using (var client = GetClient()) - { - GetCache<string>().Put(1, "foo"); - - var clientCache = client.GetCache<int?, string>(CacheName); - - clientCache.Put(2, "bar"); - clientCache[3] = "baz"; - - // Existing key. - Assert.AreEqual("foo", clientCache.Get(1)); - Assert.AreEqual("foo", clientCache[1]); - Assert.AreEqual("bar", clientCache[2]); - Assert.AreEqual("baz", clientCache[3]); - - // Missing key. - Assert.Throws<KeyNotFoundException>(() => clientCache.Get(-1)); - - // Null key. - Assert.Throws<ArgumentNullException>(() => clientCache.Get(null)); - } - } - - /// <summary> - /// Tests the cache put / get with user data types. - /// </summary> - [Test] - public void TestPutGetUserObjects([Values(true, false)] bool compactFooter) - { - var cfg = GetClientConfiguration(); - - cfg.BinaryConfiguration = new BinaryConfiguration - { - CompactFooter = compactFooter - }; - - using (var client = Ignition.StartClient(cfg)) - { - var person = new Person {Id = 100, Name = "foo"}; - var person2 = new Person2 {Id = 200, Name = "bar"}; - - var serverCache = GetCache<Person>(); - var clientCache = client.GetCache<int?, Person>(CacheName); - - Assert.AreEqual(CacheName, clientCache.Name); - - // Put through server cache. - serverCache.Put(1, person); - - // Put through client cache. - clientCache.Put(2, person2); - clientCache[3] = person2; - - // Read from client cache. - Assert.AreEqual("foo", clientCache.Get(1).Name); - Assert.AreEqual(100, clientCache[1].Id); - Assert.AreEqual(200, clientCache[2].Id); - Assert.AreEqual(200, clientCache[3].Id); - - // Read from server cache. - Assert.AreEqual("foo", serverCache.Get(1).Name); - Assert.AreEqual(100, serverCache[1].Id); - Assert.AreEqual(200, serverCache[2].Id); - Assert.AreEqual(200, serverCache[3].Id); - - // Null key or value. - Assert.Throws<ArgumentNullException>(() => clientCache.Put(10, null)); - Assert.Throws<ArgumentNullException>(() => clientCache.Put(null, person)); - } - } - - /// <summary> - /// Tests client get in multiple threads with a single client. - /// </summary> - [Test] - [Category(TestUtils.CategoryIntensive)] - public void TestGetMultithreadedSingleClient() - { - GetCache<string>().Put(1, "foo"); - - using (var client = GetClient()) - { - var clientCache = client.GetCache<int, string>(CacheName); - - TestUtils.RunMultiThreaded(() => Assert.AreEqual("foo", clientCache.Get(1)), - Environment.ProcessorCount, 5); - } - } - - /// <summary> - /// Tests client get in multiple threads with multiple clients. - /// </summary> - [Test] - [Category(TestUtils.CategoryIntensive)] - public void TestGetMultithreadedMultiClient() - { - GetCache<string>().Put(1, "foo"); - - // One client per thread. - var clients = new ConcurrentDictionary<int, IIgniteClient>(); - - TestUtils.RunMultiThreaded(() => - { - var client = clients.GetOrAdd(Thread.CurrentThread.ManagedThreadId, _ => GetClient()); - - var clientCache = client.GetCache<int, string>(CacheName); - - Assert.AreEqual("foo", clientCache.Get(1)); - }, - Environment.ProcessorCount, 5); - - clients.ToList().ForEach(x => x.Value.Dispose()); - } - - /// <summary> - /// Gets the cache. - /// </summary> - private static ICache<int, T> GetCache<T>() - { - return Ignition.GetIgnite().GetOrCreateCache<int, T>(CacheName); - } - - /// <summary> - /// Gets the client. - /// </summary> - private static IIgniteClient GetClient() - { - return Ignition.StartClient(GetClientConfiguration()); - } - - /// <summary> - /// Gets the client configuration. - /// </summary> - private static IgniteClientConfiguration GetClientConfiguration() - { - return new IgniteClientConfiguration {Host = "127.0.0.1"}; - } - } -} http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/CacheTestNoMeta.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/CacheTestNoMeta.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/CacheTestNoMeta.cs deleted file mode 100644 index a011583..0000000 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/CacheTestNoMeta.cs +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Apache.Ignite.Core.Tests.Client -{ - using System.Collections.Generic; - using System.Linq; - using System.Net; - using Apache.Ignite.Core.Binary; - using Apache.Ignite.Core.Cache.Configuration; - using Apache.Ignite.Core.Cache.Query; - using Apache.Ignite.Core.Client; - using Apache.Ignite.Core.Impl.Binary; - using Apache.Ignite.Core.Impl.Binary.Metadata; - using NUnit.Framework; - - /// <summary> - /// Client cache test without metadata (no-op binary processor). - /// </summary> - public class CacheTestNoMeta - { - /** Cache name. */ - private const string CacheName = "cache"; - - /// <summary> - /// Fixture tear down. - /// </summary> - [TestFixtureSetUp] - public void FixtureSetUp() - { - Ignition.Start(TestUtils.GetTestConfiguration()); - } - - /// <summary> - /// Fixture tear down. - /// </summary> - [TestFixtureTearDown] - public void FixtureTearDown() - { - Ignition.StopAll(true); - } - - /// <summary> - /// Tests the cache put / get with user data types. - /// </summary> - [Test] - public void TestPutGetUserObjects() - { - var cfg = new IgniteClientConfiguration - { - BinaryProcessor = new NoopBinaryProcessor(), - BinaryConfiguration = new BinaryConfiguration - { - CompactFooter = false - }, - Host = IPAddress.Loopback.ToString() - }; - - using (var client = Ignition.StartClient(cfg)) - { - var serverCache = Ignition.GetIgnite().GetOrCreateCache<int?, Person>( - new CacheConfiguration(CacheName, new QueryEntity - { - KeyType = typeof(int), - ValueType = typeof(Person), - Fields = new[] - { - new QueryField("id", typeof(int)), - new QueryField("name", typeof(string)) - } - })); - - var clientCache = client.GetCache<int?, Person>(CacheName); - - // Put through client cache. - clientCache.Put(1, new Person { Id = 100, Name = "foo" }); - clientCache[2] = new Person { Id = 200, Name = "bar" }; - - // Read from client cache. - Assert.AreEqual("foo", clientCache.Get(1).Name); - Assert.AreEqual(100, clientCache[1].Id); - Assert.AreEqual(200, clientCache[2].Id); - - // Read from server cache. - Assert.AreEqual("foo", serverCache.Get(1).Name); - Assert.AreEqual(100, serverCache[1].Id); - Assert.AreEqual(200, serverCache[2].Id); - - // SQL from server cache. - var sqlRes = serverCache.Query(new SqlQuery(typeof(Person), "where id = 100")).GetAll().Single(); - Assert.AreEqual(1, sqlRes.Key); - Assert.AreEqual(100, sqlRes.Value.Id); - Assert.AreEqual("foo", sqlRes.Value.Name); - } - } - - /// <summary> - /// No-op binary processor (does not send meta to cluster). - /// </summary> - private class NoopBinaryProcessor : IBinaryProcessor - { - /** <inheritdoc /> */ - public BinaryType GetBinaryType(int typeId) - { - return null; - } - - /** <inheritdoc /> */ - public List<IBinaryType> GetBinaryTypes() - { - return null; - } - - /** <inheritdoc /> */ - public int[] GetSchema(int typeId, int schemaId) - { - return null; - } - - /** <inheritdoc /> */ - public void PutBinaryTypes(ICollection<BinaryType> types) - { - // No-op. - } - - /** <inheritdoc /> */ - public bool RegisterType(int id, string typeName) - { - return false; - } - - /** <inheritdoc /> */ - public BinaryType RegisterEnum(string typeName, IEnumerable<KeyValuePair<string, int>> values) - { - return null; - } - - /** <inheritdoc /> */ - public string GetTypeName(int id) - { - return null; - } - } - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientTestBase.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientTestBase.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientTestBase.cs new file mode 100644 index 0000000..81e1418 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientTestBase.cs @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Tests.Client +{ + using System.Net; + using Apache.Ignite.Core.Cache; + using Apache.Ignite.Core.Client; + using NUnit.Framework; + + /// <summary> + /// Base class for client tests. + /// </summary> + public class ClientTestBase + { + /** Cache name. */ + protected const string CacheName = "cache"; + + /** Grid count. */ + private readonly int _gridCount = 1; + + /// <summary> + /// Initializes a new instance of the <see cref="ClientTestBase"/> class. + /// </summary> + public ClientTestBase() + { + // No-op. + } + + /// <summary> + /// Initializes a new instance of the <see cref="ClientTestBase"/> class. + /// </summary> + public ClientTestBase(int gridCount) + { + _gridCount = gridCount; + } + + /// <summary> + /// Fixture tear down. + /// </summary> + [TestFixtureSetUp] + public void FixtureSetUp() + { + var cfg = GetIgniteConfiguration(); + Ignition.Start(cfg); + + cfg.AutoGenerateIgniteInstanceName = true; + + for (var i = 1; i < _gridCount; i++) + { + Ignition.Start(cfg); + } + } + + /// <summary> + /// Fixture tear down. + /// </summary> + [TestFixtureTearDown] + public void FixtureTearDown() + { + Ignition.StopAll(true); + } + + /// <summary> + /// Gets the cache. + /// </summary> + protected static ICache<int, T> GetCache<T>() + { + return Ignition.GetIgnite().GetOrCreateCache<int, T>(CacheName); + } + + /// <summary> + /// Gets the client. + /// </summary> + protected IIgniteClient GetClient() + { + return Ignition.StartClient(GetClientConfiguration()); + } + + /// <summary> + /// Gets the client configuration. + /// </summary> + protected IgniteClientConfiguration GetClientConfiguration() + { + return new IgniteClientConfiguration + { + Host = IPAddress.Loopback.ToString() + }; + } + + /// <summary> + /// Gets the Ignite configuration. + /// </summary> + protected virtual IgniteConfiguration GetIgniteConfiguration() + { + return TestUtils.GetTestConfiguration(); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Person.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Person.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Person.cs deleted file mode 100644 index 7f0309f..0000000 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Person.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Apache.Ignite.Core.Tests.Client -{ - /// <summary> - /// Test person. - /// </summary> - public class Person - { - /// <summary> - /// Gets or sets the identifier. - /// </summary> - public int Id { get; set; } - - /// <summary> - /// Gets or sets the name. - /// </summary> - public string Name { get; set; } - - /// <summary> - /// Gets or sets the parent. - /// </summary> - public Person Parent { get;set; } - } - - /// <summary> - /// Test person 2. - /// </summary> - public class Person2 : Person - { - // No-op. - } -} http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/RawSocketTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/RawSocketTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/RawSocketTest.cs index 9e2200f..48bd3dd 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/RawSocketTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/RawSocketTest.cs @@ -81,6 +81,9 @@ namespace Apache.Ignite.Core.Tests.Client var requestId = reader.ReadLong(); Assert.AreEqual(1, requestId); + var success = reader.ReadBoolean(); + Assert.IsTrue(success); + var res = reader.ReadObject<string>(); Assert.AreEqual(cache[1], res); } http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj index 615ce90..ed34675 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj @@ -103,6 +103,8 @@ <Compile Include="Common\ExceptionFactory.cs" /> <Compile Include="Configuration\Package-Info.cs" /> <Compile Include="Impl\Binary\BinaryTypeId.cs" /> + <Compile Include="Impl\Client\Cache\CacheFlags.cs" /> + <Compile Include="Impl\Client\Cache\Query\ClientQueryCursor.cs" /> <Compile Include="Impl\Cache\Query\PlatformQueryQursorBase.cs" /> <Compile Include="Impl\Binary\BinaryProcessorClient.cs" /> <Compile Include="Impl\Binary\IBinaryProcessor.cs" /> http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/ScanQuery.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/ScanQuery.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/ScanQuery.cs index 2c9cf35..bb9852d 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/ScanQuery.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/ScanQuery.cs @@ -52,7 +52,7 @@ namespace Apache.Ignite.Core.Cache.Query writer.WriteInt(PageSize); writer.WriteBoolean(Partition.HasValue); - + if (Partition.HasValue) writer.WriteInt(Partition.Value); http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core/Client/Cache/ICacheClient.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Client/Cache/ICacheClient.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Client/Cache/ICacheClient.cs index b8bd6fe..edd411c 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Client/Cache/ICacheClient.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Client/Cache/ICacheClient.cs @@ -18,6 +18,8 @@ namespace Apache.Ignite.Core.Client.Cache { using System.Collections.Generic; + using Apache.Ignite.Core.Cache; + using Apache.Ignite.Core.Cache.Query; /// <summary> /// Client cache API. See <see cref="IIgniteClient.GetCache{K, V}"/>. @@ -56,5 +58,12 @@ namespace Apache.Ignite.Core.Client.Cache /// <returns>Cache value with the specified key.</returns> /// <exception cref="KeyNotFoundException">If the key is not present in the cache.</exception> TV this[TK key] { get; set; } + + /// <summary> + /// Executes a Scan query. + /// </summary> + /// <param name="scanQuery">Scan query.</param> + /// <returns>Query cursor.</returns> + IQueryCursor<ICacheEntry<TK, TV>> Query(ScanQuery<TK, TV> scanQuery); } } http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/QueryCursorBase.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/QueryCursorBase.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/QueryCursorBase.cs index 418bb24..8f00a2e 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/QueryCursorBase.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/QueryCursorBase.cs @@ -54,35 +54,45 @@ namespace Apache.Ignite.Core.Impl.Cache.Query /** Disposed flag. */ private volatile bool _disposed; + /** Whether next batch is available. */ + private bool _hasNext = true; + /// <summary> /// Constructor. /// </summary> /// <param name="marsh">Marshaller.</param> /// <param name="keepBinary">Keep binary flag.</param> - protected QueryCursorBase(Marshaller marsh, bool keepBinary) + /// <param name="initialBatchStream">Optional stream with initial batch.</param> + protected QueryCursorBase(Marshaller marsh, bool keepBinary, IBinaryStream initialBatchStream = null) { Debug.Assert(marsh != null); _keepBinary = keepBinary; _marsh = marsh; + + if (initialBatchStream != null) + { + _batch = ConvertGetBatch(initialBatchStream); + } } /** <inheritdoc /> */ public IList<T> GetAll() { - ThrowIfDisposed(); + if (_getAllCalled) + throw new InvalidOperationException("Failed to get all entries because GetAll() " + + "method has already been called."); if (_iterCalled) - throw new InvalidOperationException("Failed to get all entries because GetEnumerator() " + - "method has already been called."); + throw new InvalidOperationException("Failed to get all entries because GetEnumerator() " + + "method has already been called."); - if (_getAllCalled) - throw new InvalidOperationException("Failed to get all entries because GetAll() " + - "method has already been called."); + ThrowIfDisposed(); var res = GetAllInternal(); _getAllCalled = true; + _hasNext = false; return res; } @@ -92,20 +102,20 @@ namespace Apache.Ignite.Core.Impl.Cache.Query /** <inheritdoc /> */ public IEnumerator<T> GetEnumerator() { - ThrowIfDisposed(); - - if (_iterCalled) + if (_getAllCalled) { throw new InvalidOperationException("Failed to get enumerator entries because " + - "GetEnumerator() method has already been called."); + "GetAll() method has already been called."); } - if (_getAllCalled) + if (_iterCalled) { throw new InvalidOperationException("Failed to get enumerator entries because " + - "GetAll() method has already been called."); + "GetEnumerator() method has already been called."); } + ThrowIfDisposed(); + InitIterator(); _iterCalled = true; @@ -196,7 +206,7 @@ namespace Apache.Ignite.Core.Impl.Cache.Query /// </summary> private void RequestBatch() { - _batch = GetBatch(); + _batch = _hasNext ? GetBatch() : null; _batchPos = 0; } @@ -237,12 +247,19 @@ namespace Apache.Ignite.Core.Impl.Cache.Query var size = reader.ReadInt(); if (size == 0) + { + _hasNext = false; return null; + } var res = new T[size]; for (var i = 0; i < size; i++) + { res[i] = Read(reader); + } + + _hasNext = stream.ReadBool(); return res; } @@ -253,9 +270,14 @@ namespace Apache.Ignite.Core.Impl.Cache.Query lock (this) { if (_disposed) + { return; + } - Dispose(true); + if (_hasNext) + { + Dispose(true); + } GC.SuppressFinalize(this); http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/CacheClient.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/CacheClient.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/CacheClient.cs index affa815..974bab3 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/CacheClient.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/CacheClient.cs @@ -21,11 +21,15 @@ namespace Apache.Ignite.Core.Impl.Client.Cache using System.Collections.Generic; using System.Diagnostics; using System.IO; + using Apache.Ignite.Core.Cache; + using Apache.Ignite.Core.Cache.Query; using Apache.Ignite.Core.Client; using Apache.Ignite.Core.Client.Cache; using Apache.Ignite.Core.Impl.Binary; using Apache.Ignite.Core.Impl.Binary.IO; + using Apache.Ignite.Core.Impl.Cache; using Apache.Ignite.Core.Impl.Client; + using Apache.Ignite.Core.Impl.Client.Cache.Query; using Apache.Ignite.Core.Impl.Common; using BinaryWriter = Apache.Ignite.Core.Impl.Binary.BinaryWriter; @@ -34,6 +38,9 @@ namespace Apache.Ignite.Core.Impl.Client.Cache /// </summary> internal class CacheClient<TK, TV> : ICacheClient<TK, TV> { + /** Scan query filter platform code: .NET filter. */ + private const byte FilterPlatformDotnet = 2; + /** Cache name. */ private readonly string _name; @@ -46,6 +53,9 @@ namespace Apache.Ignite.Core.Impl.Client.Cache /** Marshaller. */ private readonly Marshaller _marsh; + /** Keep binary flag. */ + private bool _keepBinary = false; + /// <summary> /// Initializes a new instance of the <see cref="CacheClient{TK, TV}" /> class. /// </summary> @@ -102,6 +112,17 @@ namespace Apache.Ignite.Core.Impl.Client.Cache }); } + /** <inheritDoc /> */ + public IQueryCursor<ICacheEntry<TK, TV>> Query(ScanQuery<TK, TV> scanQuery) + { + IgniteArgumentCheck.NotNull(scanQuery, "query"); + + // Filter is a binary object for all platforms. + // For .NET it is a CacheEntryFilterHolder with a predefined id (BinaryTypeId.CacheEntryPredicateHolder). + return DoOutInOp(ClientOp.QueryScan, w => WriteScanQuery(w, scanQuery), + s => new ClientQueryCursor<TK, TV>(_ignite, s.ReadLong(), _keepBinary, s)); + } + /// <summary> /// Does the out in op. /// </summary> @@ -150,6 +171,34 @@ namespace Apache.Ignite.Core.Impl.Client.Cache } /// <summary> + /// Writes the scan query. + /// </summary> + private void WriteScanQuery(BinaryWriter writer, ScanQuery<TK, TV> qry) + { + Debug.Assert(qry != null); + + if (qry.Filter == null) + { + writer.WriteByte(BinaryUtils.HdrNull); + } + else + { + var holder = new CacheEntryFilterHolder(qry.Filter, (key, val) => qry.Filter.Invoke( + new CacheEntry<TK, TV>((TK)key, (TV)val)), writer.Marshaller, _keepBinary); + + writer.WriteObject(holder); + + writer.WriteByte(FilterPlatformDotnet); + } + + writer.WriteInt(qry.PageSize); + + writer.WriteInt(qry.Partition ?? -1); + + writer.WriteBoolean(qry.Local); + } + + /// <summary> /// Gets the key not found exception. /// </summary> private static KeyNotFoundException GetKeyNotFoundException() http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/CacheFlags.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/CacheFlags.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/CacheFlags.cs new file mode 100644 index 0000000..e24d952 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/CacheFlags.cs @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Impl.Client.Cache +{ + using System; + + /// <summary> + /// Cache operation flags. + /// </summary> + [Flags] + internal enum CacheFlags : byte + { + /// <summary> + /// No flags. + /// </summary> + None = 0x00, + + /// <summary> + /// Keep binary. + /// </summary> + KeepBinary = 0x01 + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/Query/ClientQueryCursor.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/Query/ClientQueryCursor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/Query/ClientQueryCursor.cs new file mode 100644 index 0000000..898bbbe --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/Query/ClientQueryCursor.cs @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Impl.Client.Cache.Query +{ + using System.Collections.Generic; + using System.Linq; + using Apache.Ignite.Core.Cache; + using Apache.Ignite.Core.Impl.Binary; + using Apache.Ignite.Core.Impl.Binary.IO; + using Apache.Ignite.Core.Impl.Cache; + using Apache.Ignite.Core.Impl.Cache.Query; + using Apache.Ignite.Core.Impl.Client; + + /// <summary> + /// Client query cursor. + /// </summary> + internal class ClientQueryCursor<TK, TV> : QueryCursorBase<ICacheEntry<TK, TV>> + { + /** Ignite. */ + private readonly IgniteClient _ignite; + + /** Cursor ID. */ + private readonly long _cursorId; + + /// <summary> + /// Initializes a new instance of the <see cref="ClientQueryCursor{TK, TV}" /> class. + /// </summary> + /// <param name="ignite">The ignite.</param> + /// <param name="cursorId">The cursor identifier.</param> + /// <param name="keepBinary">Keep binary flag.</param> + /// <param name="initialBatchStream">Optional stream with initial batch.</param> + public ClientQueryCursor(IgniteClient ignite, long cursorId, bool keepBinary, + IBinaryStream initialBatchStream) + : base(ignite.Marshaller, keepBinary, initialBatchStream) + { + _ignite = ignite; + _cursorId = cursorId; + } + + /** <inheritdoc /> */ + protected override void InitIterator() + { + // No-op. + } + + /** <inheritdoc /> */ + protected override IList<ICacheEntry<TK, TV>> GetAllInternal() + { + return this.ToArray(); + } + + /** <inheritdoc /> */ + protected override ICacheEntry<TK, TV> Read(BinaryReader reader) + { + return new CacheEntry<TK, TV>(reader.ReadObject<TK>(), reader.ReadObject<TV>()); + } + + /** <inheritdoc /> */ + protected override ICacheEntry<TK, TV>[] GetBatch() + { + return _ignite.Socket.DoOutInOp(ClientOp.QueryScanCursorGetPage, + w => w.WriteLong(_cursorId), + s => ConvertGetBatch(s)); + } + + /** <inheritdoc /> */ + protected override void Dispose(bool disposing) + { + try + { + _ignite.Socket.DoOutInOp<object>(ClientOp.ResourceClose, w => w.WriteLong(_cursorId), null); + } + finally + { + base.Dispose(disposing); + } + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientOp.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientOp.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientOp.cs index 79d7c9e..c39b68f 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientOp.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientOp.cs @@ -27,6 +27,9 @@ namespace Apache.Ignite.Core.Impl.Client BinaryTypeGet = 3, CachePut = 4, BinaryTypeNamePut = 5, - BinaryTypePut = 6 + BinaryTypePut = 6, + QueryScan = 7, + QueryScanCursorGetPage = 8, + ResourceClose = 9 } } http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientSocket.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientSocket.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientSocket.cs index 0204ef8..1a245a1 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientSocket.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientSocket.cs @@ -88,6 +88,12 @@ namespace Apache.Ignite.Core.Impl.Client var resRequestId = stream.ReadLong(); Debug.Assert(requestId == resRequestId); + if (!stream.ReadBool()) + { + // Error. + throw new IgniteException(BinaryUtils.Marshaller.StartUnmarshal(stream).ReadString()); + } + if (readFunc != null) { return readFunc(stream); @@ -159,7 +165,11 @@ namespace Apache.Ignite.Core.Impl.Client buf = new byte[size]; received = sock.Receive(buf); - Debug.Assert(received == buf.Length); + + while (received < size) + { + received += sock.Receive(buf, received, size - received, SocketFlags.None); + } return buf; } http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/IgniteClient.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/IgniteClient.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/IgniteClient.cs index 29b8a2c..d05355a 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/IgniteClient.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/IgniteClient.cs @@ -144,9 +144,16 @@ namespace Apache.Ignite.Core.Impl.Client /// <summary> /// Gets the client not supported exception. /// </summary> - private static NotSupportedException GetClientNotSupportedException() + public static NotSupportedException GetClientNotSupportedException(string info = null) { - return new NotSupportedException("Operation is not supported in thin client mode."); + var msg = "Operation is not supported in thin client mode."; + + if (info != null) + { + msg += " " + info; + } + + return new NotSupportedException(msg); } } }