IGNITE-7109 .NET: Thin client: Async cache operations
authorPavel Tupitsyn <ptupitsyn@apache.org>
Fri, 22 Dec 2017 12:47:49 +0000 (15:47 +0300)
committerPavel Tupitsyn <ptupitsyn@apache.org>
Fri, 22 Dec 2017 12:47:49 +0000 (15:47 +0300)
This closes #3187

23 files changed:
modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCachePutRequest.java
modules/platforms/dotnet/Apache.Ignite.Benchmarks/Apache.Ignite.Benchmarks.csproj
modules/platforms/dotnet/Apache.Ignite.Benchmarks/BenchmarkRunner.cs
modules/platforms/dotnet/Apache.Ignite.Benchmarks/ThinClient/ThinClientGetAsyncBenchmark.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Benchmarks/ThinClient/ThinClientPutAsyncBenchmark.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/CacheTestAsyncWrapper.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheClientAsyncWrapper.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestAsync.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientConnectionTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientTestBase.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/IgniteClientConfigurationTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/Client/IgniteClientConfiguration.xml
modules/platforms/dotnet/Apache.Ignite.Core.Tests/TaskExtensions.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Core/Client/Cache/ICacheClient.cs
modules/platforms/dotnet/Apache.Ignite.Core/Client/IIgniteClient.cs
modules/platforms/dotnet/Apache.Ignite.Core/Client/IgniteClientConfiguration.cs
modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/BinaryHeapStream.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/CacheClient.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientSocket.cs

index b5c2074..e712337 100644 (file)
@@ -69,7 +69,9 @@
     <Compile Include="Result\BenchmarkConsoleResultWriter.cs" />
     <Compile Include="Result\BenchmarkFileResultWriter.cs" />
     <Compile Include="Result\IBenchmarkResultWriter.cs" />
+    <Compile Include="ThinClient\ThinClientGetAsyncBenchmark.cs" />
     <Compile Include="ThinClient\ThinClientGetBenchmark.cs" />
+    <Compile Include="ThinClient\ThinClientPutAsyncBenchmark.cs" />
     <Compile Include="ThinClient\ThinClientPutBenchmark.cs" />
   </ItemGroup>
   <ItemGroup>
index 2571c14..9d86da2 100644 (file)
@@ -21,6 +21,7 @@ namespace Apache.Ignite.Benchmarks
     using System.Diagnostics;
     using System.Text;
     using Apache.Ignite.Benchmarks.Interop;
+    using Apache.Ignite.Benchmarks.ThinClient;
 
     /// <summary>
     /// Benchmark runner.
@@ -35,8 +36,8 @@ namespace Apache.Ignite.Benchmarks
         public static void Main(string[] args)
         {
             args = new[] { 
-                typeof(GetBenchmark).FullName,
-                "-ConfigPath", @"C:\W\incubator-ignite\modules\platforms\dotnet\Apache.Ignite.Benchmarks\Config\benchmark.xml",
+                typeof(ThinClientGetBenchmark).FullName,
+                "-ConfigPath", @"S:\W\incubator-ignite\modules\platforms\dotnet\Apache.Ignite.Benchmarks\Config\benchmark.xml",
                 "-Threads", "1",
                 "-Warmup", "0",
                 "-Duration", "60",
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/ThinClient/ThinClientGetAsyncBenchmark.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/ThinClient/ThinClientGetAsyncBenchmark.cs
new file mode 100644 (file)
index 0000000..a9fca1f
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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.Benchmarks.ThinClient
+{
+    using System.Collections.Generic;
+    using Apache.Ignite.Benchmarks.Interop;
+    using Apache.Ignite.Benchmarks.Model;
+    using Apache.Ignite.Core.Client.Cache;
+
+    /// <summary>
+    /// Cache get async benchmark.
+    /// </summary>
+    internal class ThinClientGetAsyncBenchmark : PlatformBenchmarkBase
+    {
+        /** Cache name. */
+        private const string CacheName = "cache";
+
+        /** Native cache wrapper. */
+        private ICacheClient<int, Employee> _cache;
+
+        /** <inheritDoc /> */
+        protected override void OnStarted()
+        {
+            base.OnStarted();
+
+            _cache = GetClient().GetCache<int, Employee>(CacheName);
+
+            for (int i = 0; i < Emps.Length; i++)
+                _cache.Put(i, Emps[i]);
+        }
+
+        /** <inheritDoc /> */
+        protected override void GetDescriptors(ICollection<BenchmarkOperationDescriptor> descs)
+        {
+            descs.Add(BenchmarkOperationDescriptor.Create("ThinClientGetAsync", GetAsync, 1));
+        }
+
+        /// <summary>
+        /// Cache get.
+        /// </summary>
+        private void GetAsync(BenchmarkState state)
+        {
+            var idx = BenchmarkUtils.GetRandomInt(Dataset);
+
+            _cache.GetAsync(idx).Wait();
+        }
+    }
+}
\ No newline at end of file
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/ThinClient/ThinClientPutAsyncBenchmark.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/ThinClient/ThinClientPutAsyncBenchmark.cs
new file mode 100644 (file)
index 0000000..92a2150
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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.Benchmarks.ThinClient
+{
+    using System.Collections.Generic;
+    using Apache.Ignite.Benchmarks.Interop;
+    using Apache.Ignite.Core.Client.Cache;
+
+    /// <summary>
+    /// Cache put async benchmark.
+    /// </summary>
+    internal class ThinClientPutAsyncBenchmark : PlatformBenchmarkBase
+    {
+        /** Cache name. */
+        private const string CacheName = "cache";
+
+        /** Native cache wrapper. */
+        private ICacheClient<object, object> _cache;
+
+        /** <inheritDoc /> */
+        protected override void OnStarted()
+        {
+            base.OnStarted();
+
+            _cache = GetClient().GetCache<object, object>(CacheName);
+        }
+
+        /** <inheritDoc /> */
+        protected override void GetDescriptors(ICollection<BenchmarkOperationDescriptor> descs)
+        {
+            descs.Add(BenchmarkOperationDescriptor.Create("ThinClientPutAsync", PutAsync, 1));
+        }
+        
+        /// <summary>
+        /// Cache put.
+        /// </summary>
+        private void PutAsync(BenchmarkState state)
+        {
+            int idx = BenchmarkUtils.GetRandomInt(Dataset);
+
+            _cache.PutAsync(idx, Emps[idx]).Wait();
+        }
+    }
+}
\ No newline at end of file
index 5947685..f1ccb06 100644 (file)
@@ -83,6 +83,8 @@
     <Compile Include="..\Apache.Ignite.Core.Tests\Cache\TestReferenceObject.cs" Link="Cache\TestReferenceObject.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Client\Cache\BinaryBuilderTest.cs" Link="ThinClient\Cache\BinaryBuilderTest.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Client\Cache\CacheTest.cs" Link="ThinClient\Cache\CacheTest.cs" />
+    <Compile Include="..\Apache.Ignite.Core.Tests\Client\Cache\CacheTestAsync.cs" Link="ThinClient\Cache\CacheTestAsync.cs" />
+    <Compile Include="..\Apache.Ignite.Core.Tests\Client\Cache\CacheClientAsyncWrapper.cs" Link="ThinClient\Cache\CacheClientAsyncWrapper.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Client\Cache\CacheTestKeepBinary.cs" Link="ThinClient\Cache\CacheTestKeepBinary.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Client\Cache\EmptyObject.cs" Link="ThinClient\Cache\EmptyObject.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Client\Cache\Person.cs" Link="ThinClient\Cache\Person.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Plugin\TestIgnitePluginException.cs" Link="Plugin\TestIgnitePluginException.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Plugin\TestIgnitePluginProvider.cs" Link="Plugin\TestIgnitePluginProvider.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Query\BinarizablePerson.cs" Link="Cache\Query\BinarizablePerson.cs" />
+    <Compile Include="..\Apache.Ignite.Core.Tests\TaskExtensions.cs" Link="Common\TaskExtensions.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\TestUtils.Common.cs" Link="Common\TestUtils.Common.cs" />
   </ItemGroup>
 
index 059a6e0..d3b9fa2 100644 (file)
     <Compile Include="Cache\Store\CacheStoreSessionTestCodeConfig.cs" />
     <Compile Include="Cache\Store\CacheStoreSessionTestSharedFactory.cs" />
     <Compile Include="Client\Cache\BinaryBuilderTest.cs" />
+    <Compile Include="Client\Cache\CacheClientAsyncWrapper.cs" />
     <Compile Include="Client\Cache\CacheTest.cs" />
+    <Compile Include="Client\Cache\CacheTestAsync.cs" />
     <Compile Include="Client\Cache\CacheTestKeepBinary.cs" />
     <Compile Include="Client\Cache\CacheTestNoMeta.cs" />
     <Compile Include="Client\Cache\ClientCacheConfigurationTest.cs" />
     <Compile Include="Plugin\TestIgnitePluginConfiguration.cs" />
     <Compile Include="Plugin\TestIgnitePluginException.cs" />
     <Compile Include="Plugin\TestIgnitePluginProvider.cs" />
+    <Compile Include="TaskExtensions.cs" />
     <Compile Include="TestAppConfig.cs" />
     <Compile Include="Binary\BinaryBuilderSelfTestFullFooter.cs" />
     <Compile Include="Binary\BinaryCompactFooterInteropTest.cs" />
index 0b1af41..c4e50d1 100644 (file)
@@ -98,7 +98,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public void LoadCache(ICacheEntryFilter<TK, TV> p, params object[] args)
         {
-            WaitResult(_cache.LoadCacheAsync(p, args));
+            _cache.LoadCacheAsync(p, args).WaitResult();
         }
 
         /** <inheritDoc /> */
@@ -110,7 +110,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public void LocalLoadCache(ICacheEntryFilter<TK, TV> p, params object[] args)
         {
-            WaitResult(_cache.LocalLoadCacheAsync(p, args));
+            _cache.LocalLoadCacheAsync(p, args).WaitResult();
         }
 
         /** <inheritDoc /> */
@@ -134,7 +134,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public bool ContainsKey(TK key)
         {
-            return GetResult(_cache.ContainsKeyAsync(key));
+            return _cache.ContainsKeyAsync(key).GetResult();
         }
 
         /** <inheritDoc /> */
@@ -146,7 +146,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public bool ContainsKeys(IEnumerable<TK> keys)
         {
-            return GetResult(_cache.ContainsKeysAsync(keys));
+            return _cache.ContainsKeysAsync(keys).GetResult();
         }
 
         /** <inheritDoc /> */
@@ -177,7 +177,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public TV Get(TK key)
         {
-            return GetResult(_cache.GetAsync(key));
+            return _cache.GetAsync(key).GetResult();
         }
 
         /** <inheritDoc /> */
@@ -201,7 +201,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public ICollection<ICacheEntry<TK, TV>> GetAll(IEnumerable<TK> keys)
         {
-            return GetResult(_cache.GetAllAsync(keys));
+            return _cache.GetAllAsync(keys).GetResult();
         }
 
         /** <inheritDoc /> */
@@ -213,7 +213,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public void Put(TK key, TV val)
         {
-            WaitResult(_cache.PutAsync(key, val));
+            _cache.PutAsync(key, val).WaitResult();
         }
 
         /** <inheritDoc /> */
@@ -225,7 +225,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public CacheResult<TV> GetAndPut(TK key, TV val)
         {
-            return GetResult(_cache.GetAndPutAsync(key, val));
+            return _cache.GetAndPutAsync(key, val).GetResult();
         }
 
         /** <inheritDoc /> */
@@ -237,7 +237,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public CacheResult<TV> GetAndReplace(TK key, TV val)
         {
-            return GetResult(_cache.GetAndReplaceAsync(key, val));
+            return _cache.GetAndReplaceAsync(key, val).GetResult();
         }
 
         /** <inheritDoc /> */
@@ -249,7 +249,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public CacheResult<TV> GetAndRemove(TK key)
         {
-            return GetResult(_cache.GetAndRemoveAsync(key));
+            return _cache.GetAndRemoveAsync(key).GetResult();
         }
 
         /** <inheritDoc /> */
@@ -261,7 +261,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public bool PutIfAbsent(TK key, TV val)
         {
-            return GetResult(_cache.PutIfAbsentAsync(key, val));
+            return _cache.PutIfAbsentAsync(key, val).GetResult();
         }
 
         /** <inheritDoc /> */
@@ -273,7 +273,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public CacheResult<TV> GetAndPutIfAbsent(TK key, TV val)
         {
-            return GetResult(_cache.GetAndPutIfAbsentAsync(key, val));
+            return _cache.GetAndPutIfAbsentAsync(key, val).GetResult();
         }
 
         /** <inheritDoc /> */
@@ -285,7 +285,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public bool Replace(TK key, TV val)
         {
-            return GetResult(_cache.ReplaceAsync(key, val));
+            return _cache.ReplaceAsync(key, val).GetResult();
         }
 
         /** <inheritDoc /> */
@@ -297,7 +297,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public bool Replace(TK key, TV oldVal, TV newVal)
         {
-            return GetResult(_cache.ReplaceAsync(key, oldVal, newVal));
+            return _cache.ReplaceAsync(key, oldVal, newVal).GetResult();
         }
 
         /** <inheritDoc /> */
@@ -309,7 +309,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public void PutAll(IEnumerable<KeyValuePair<TK, TV>> vals)
         {
-            WaitResult(_cache.PutAllAsync(vals));
+            _cache.PutAllAsync(vals).WaitResult();
         }
 
         /** <inheritDoc /> */
@@ -327,7 +327,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public void Clear()
         {
-            WaitResult(_cache.ClearAsync());
+            _cache.ClearAsync().WaitResult();
         }
 
         /** <inheritDoc /> */
@@ -339,7 +339,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public void Clear(TK key)
         {
-            WaitResult(_cache.ClearAsync(key));
+            _cache.ClearAsync(key).WaitResult();
         }
 
         /** <inheritDoc /> */
@@ -351,7 +351,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public void ClearAll(IEnumerable<TK> keys)
         {
-            WaitResult(_cache.ClearAllAsync(keys));
+            _cache.ClearAllAsync(keys).WaitResult();
         }
 
         /** <inheritDoc /> */
@@ -375,7 +375,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public bool Remove(TK key)
         {
-            return GetResult(_cache.RemoveAsync(key));
+            return _cache.RemoveAsync(key).GetResult();
         }
 
         /** <inheritDoc /> */
@@ -387,7 +387,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public bool Remove(TK key, TV val)
         {
-            return GetResult(_cache.RemoveAsync(key, val));
+            return _cache.RemoveAsync(key, val).GetResult();
         }
 
         /** <inheritDoc /> */
@@ -399,7 +399,8 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public void RemoveAll(IEnumerable<TK> keys)
         {
-            WaitResult(_cache.RemoveAllAsync(keys));
+            Task task = _cache.RemoveAllAsync(keys);
+            task.WaitResult();
         }
 
         /** <inheritDoc /> */
@@ -411,7 +412,8 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public void RemoveAll()
         {
-            WaitResult(_cache.RemoveAllAsync());
+            Task task = _cache.RemoveAllAsync();
+            task.WaitResult();
         }
 
         /** <inheritDoc /> */
@@ -429,7 +431,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public int GetSize(params CachePeekMode[] modes)
         {
-            return GetResult(_cache.GetSizeAsync(modes));
+            return _cache.GetSizeAsync(modes).GetResult();
         }
 
         /** <inheritDoc /> */
@@ -478,7 +480,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         /** <inheritDoc /> */
         public TRes Invoke<TArg, TRes>(TK key, ICacheEntryProcessor<TK, TV, TArg, TRes> processor, TArg arg)
         {
-            return GetResult(_cache.InvokeAsync(key, processor, arg));
+            return _cache.InvokeAsync(key, processor, arg).GetResult();
         }
 
         /** <inheritDoc /> */
@@ -491,7 +493,7 @@ namespace Apache.Ignite.Core.Tests.Cache
         public ICollection<ICacheEntryProcessorResult<TK, TRes>> InvokeAll<TArg, TRes>(IEnumerable<TK> keys, 
             ICacheEntryProcessor<TK, TV, TArg, TRes> processor, TArg arg)
         {
-            return GetResult(_cache.InvokeAllAsync(keys, processor, arg));
+            return _cache.InvokeAllAsync(keys, processor, arg).GetResult();
         }
 
         /** <inheritDoc /> */
@@ -571,37 +573,6 @@ namespace Apache.Ignite.Core.Tests.Cache
         {
             return GetEnumerator();
         }
-
-        /// <summary>
-        /// Waits the result of a task, unwraps exceptions.
-        /// </summary>
-        /// <param name="task">The task.</param>
-        private static void WaitResult(Task task)
-        {
-            try
-            {
-                task.Wait();
-            }
-            catch (AggregateException ex)
-            {
-                throw ex.InnerException ?? ex;
-            }
-        }
-
-        /// <summary>
-        /// Gets the result of a task, unwraps exceptions.
-        /// </summary>
-        private static T GetResult<T>(Task<T> task)
-        {
-            try
-            {
-                return task.Result;
-            }
-            catch (Exception ex)
-            {
-                throw ex.InnerException ?? ex;
-            }
-        }
     }
 
     /// <summary>
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheClientAsyncWrapper.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheClientAsyncWrapper.cs
new file mode 100644 (file)
index 0000000..1075ddf
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * 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.Threading.Tasks;
+    using Apache.Ignite.Core.Cache;
+    using Apache.Ignite.Core.Cache.Query;
+    using Apache.Ignite.Core.Client.Cache;
+
+    /// <summary>
+    /// Cache client async wrapper.
+    /// </summary>
+    public class CacheClientAsyncWrapper<TK, TV> : ICacheClient<TK, TV>
+    {
+        /** */
+        private readonly ICacheClient<TK, TV> _cache;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="CacheClientAsyncWrapper{TK, TV}"/> class.
+        /// </summary>
+        /// <param name="cache">The cache.</param>
+        public CacheClientAsyncWrapper(ICacheClient<TK, TV> cache)
+        {
+            _cache = cache;
+        }
+
+        /** <inheritDoc /> */
+        public string Name
+        {
+            get { return _cache.Name; }
+        }
+
+        /** <inheritDoc /> */
+        public void Put(TK key, TV val)
+        {
+            _cache.PutAsync(key, val).WaitResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task PutAsync(TK key, TV val)
+        {
+            return _cache.PutAsync(key, val);
+        }
+
+        /** <inheritDoc /> */
+        public TV Get(TK key)
+        {
+            return _cache.GetAsync(key).GetResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task<TV> GetAsync(TK key)
+        {
+            return _cache.GetAsync(key);
+        }
+
+        /** <inheritDoc /> */
+        public bool TryGet(TK key, out TV value)
+        {
+            var res = _cache.TryGetAsync(key).GetResult();
+            value = res.Value;
+            return res.Success;
+        }
+
+        /** <inheritDoc /> */
+        public Task<CacheResult<TV>> TryGetAsync(TK key)
+        {
+            return _cache.TryGetAsync(key);
+        }
+
+        /** <inheritDoc /> */
+        public ICollection<ICacheEntry<TK, TV>> GetAll(IEnumerable<TK> keys)
+        {
+            return _cache.GetAllAsync(keys).GetResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task<ICollection<ICacheEntry<TK, TV>>> GetAllAsync(IEnumerable<TK> keys)
+        {
+            return _cache.GetAllAsync(keys);
+        }
+
+        /** <inheritDoc /> */
+        public TV this[TK key]
+        {
+            get { return _cache[key]; }
+            set { _cache[key] = value; }
+        }
+
+        /** <inheritDoc /> */
+        public bool ContainsKey(TK key)
+        {
+            return _cache.ContainsKeyAsync(key).GetResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task<bool> ContainsKeyAsync(TK key)
+        {
+            return _cache.ContainsKeyAsync(key);
+        }
+
+        /** <inheritDoc /> */
+        public bool ContainsKeys(IEnumerable<TK> keys)
+        {
+            return _cache.ContainsKeysAsync(keys).GetResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task<bool> ContainsKeysAsync(IEnumerable<TK> keys)
+        {
+            return _cache.ContainsKeysAsync(keys);
+        }
+
+        /** <inheritDoc /> */
+        public IQueryCursor<ICacheEntry<TK, TV>> Query(ScanQuery<TK, TV> scanQuery)
+        {
+            return _cache.Query(scanQuery);
+        }
+
+        /** <inheritDoc /> */
+        public IQueryCursor<ICacheEntry<TK, TV>> Query(SqlQuery sqlQuery)
+        {
+            return _cache.Query(sqlQuery);
+        }
+
+        /** <inheritDoc /> */
+        public IFieldsQueryCursor Query(SqlFieldsQuery sqlFieldsQuery)
+        {
+            return _cache.Query(sqlFieldsQuery);
+        }
+
+        /** <inheritDoc /> */
+        public CacheResult<TV> GetAndPut(TK key, TV val)
+        {
+            return _cache.GetAndPutAsync(key, val).GetResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task<CacheResult<TV>> GetAndPutAsync(TK key, TV val)
+        {
+            return _cache.GetAndPutAsync(key, val);
+        }
+
+        /** <inheritDoc /> */
+        public CacheResult<TV> GetAndReplace(TK key, TV val)
+        {
+            return _cache.GetAndReplaceAsync(key, val).GetResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task<CacheResult<TV>> GetAndReplaceAsync(TK key, TV val)
+        {
+            return _cache.GetAndReplaceAsync(key, val);
+        }
+
+        /** <inheritDoc /> */
+        public CacheResult<TV> GetAndRemove(TK key)
+        {
+            return _cache.GetAndRemoveAsync(key).GetResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task<CacheResult<TV>> GetAndRemoveAsync(TK key)
+        {
+            return _cache.GetAndRemoveAsync(key);
+        }
+
+        /** <inheritDoc /> */
+        public bool PutIfAbsent(TK key, TV val)
+        {
+            return _cache.PutIfAbsentAsync(key, val).GetResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task<bool> PutIfAbsentAsync(TK key, TV val)
+        {
+            return _cache.PutIfAbsentAsync(key, val);
+        }
+
+        /** <inheritDoc /> */
+        public CacheResult<TV> GetAndPutIfAbsent(TK key, TV val)
+        {
+            return _cache.GetAndPutIfAbsentAsync(key, val).GetResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task<CacheResult<TV>> GetAndPutIfAbsentAsync(TK key, TV val)
+        {
+            return _cache.GetAndPutIfAbsentAsync(key, val);
+        }
+
+        /** <inheritDoc /> */
+        public bool Replace(TK key, TV val)
+        {
+            return _cache.ReplaceAsync(key, val).GetResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task<bool> ReplaceAsync(TK key, TV val)
+        {
+            return _cache.ReplaceAsync(key, val);
+        }
+
+        /** <inheritDoc /> */
+        public bool Replace(TK key, TV oldVal, TV newVal)
+        {
+            return _cache.ReplaceAsync(key, oldVal, newVal).GetResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task<bool> ReplaceAsync(TK key, TV oldVal, TV newVal)
+        {
+            return _cache.ReplaceAsync(key, oldVal, newVal);
+        }
+
+        /** <inheritDoc /> */
+        public void PutAll(IEnumerable<KeyValuePair<TK, TV>> vals)
+        {
+            _cache.PutAllAsync(vals).WaitResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task PutAllAsync(IEnumerable<KeyValuePair<TK, TV>> vals)
+        {
+            return _cache.PutAllAsync(vals);
+        }
+
+        /** <inheritDoc /> */
+        public void Clear()
+        {
+            _cache.ClearAsync().WaitResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task ClearAsync()
+        {
+            return _cache.ClearAsync();
+        }
+
+        /** <inheritDoc /> */
+        public void Clear(TK key)
+        {
+            _cache.ClearAsync(key).WaitResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task ClearAsync(TK key)
+        {
+            return _cache.ClearAsync(key);
+        }
+
+        /** <inheritDoc /> */
+        public void ClearAll(IEnumerable<TK> keys)
+        {
+            _cache.ClearAllAsync(keys).WaitResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task ClearAllAsync(IEnumerable<TK> keys)
+        {
+            return _cache.ClearAllAsync(keys);
+        }
+
+        /** <inheritDoc /> */
+        public bool Remove(TK key)
+        {
+            return _cache.RemoveAsync(key).GetResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task<bool> RemoveAsync(TK key)
+        {
+            return _cache.RemoveAsync(key);
+        }
+
+        /** <inheritDoc /> */
+        public bool Remove(TK key, TV val)
+        {
+            return _cache.RemoveAsync(key, val).GetResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task<bool> RemoveAsync(TK key, TV val)
+        {
+            return _cache.RemoveAsync(key, val);
+        }
+
+        /** <inheritDoc /> */
+        public void RemoveAll(IEnumerable<TK> keys)
+        {
+            _cache.RemoveAllAsync(keys).WaitResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task RemoveAllAsync(IEnumerable<TK> keys)
+        {
+            return _cache.RemoveAllAsync(keys);
+        }
+
+        /** <inheritDoc /> */
+        public void RemoveAll()
+        {
+            _cache.RemoveAllAsync().WaitResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task RemoveAllAsync()
+        {
+            return _cache.RemoveAllAsync();
+        }
+
+        /** <inheritDoc /> */
+        public long GetSize(params CachePeekMode[] modes)
+        {
+            return _cache.GetSizeAsync(modes).GetResult();
+        }
+
+        /** <inheritDoc /> */
+        public Task<long> GetSizeAsync(params CachePeekMode[] modes)
+        {
+            return _cache.GetSizeAsync(modes);
+        }
+
+        /** <inheritDoc /> */
+        public CacheClientConfiguration GetConfiguration()
+        {
+            return _cache.GetConfiguration();
+        }
+
+        /** <inheritDoc /> */
+        public ICacheClient<TK1, TV1> WithKeepBinary<TK1, TV1>()
+        {
+            return _cache.WithKeepBinary<TK1, TV1>();
+        }
+    }
+}
\ No newline at end of file
index f54efae..4c51d72 100644 (file)
@@ -30,7 +30,7 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
     /// <summary>
     /// Thin client cache test.
     /// </summary>
-    public sealed class CacheTest : ClientTestBase
+    public class CacheTest : ClientTestBase
     {
         /// <summary>
         /// Tests the cache put / get with primitive data types.
@@ -38,32 +38,29 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestPutGetPrimitives()
         {
-            using (var client = GetClient())
-            {
-                GetCache<string>().Put(1, "foo");
+            GetCache<string>().Put(1, "foo");
 
-                var clientCache = client.GetCache<int?, string>(CacheName);
+            var clientCache = GetClientCache<int?, string>();
 
-                clientCache.Put(2, "bar");
-                clientCache[3] = "baz";
+            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]);
+            // 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));
+            // Missing key.
+            Assert.Throws<KeyNotFoundException>(() => clientCache.Get(-1));
 
-                // Null key.
-                Assert.Throws<ArgumentNullException>(() => clientCache.Get(null));
+            // Null key.
+            Assert.Throws<ArgumentNullException>(() => clientCache.Get(null));
 
-                // Null vs 0.
-                var intCache = client.GetCache<int?, int?>(CacheName);
-                intCache.Put(1, 0);
-                Assert.AreEqual(0, intCache.Get(1));
-            }
+            // Null vs 0.
+            var intCache = GetClientCache<int?, int?>();
+            intCache.Put(1, 0);
+            Assert.AreEqual(0, intCache.Get(1));
         }
 
         /// <summary>
@@ -72,14 +69,11 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestPutGetEmptyObject()
         {
-            using (var client = GetClient())
-            {
-                var serverCache = GetCache<EmptyObject>();
-                var clientCache = client.GetCache<int, EmptyObject>(CacheName);
+            var serverCache = GetCache<EmptyObject>();
+            var clientCache = GetClientCache<EmptyObject>();
 
-                serverCache.Put(1, new EmptyObject());
-                Assert.IsNotNull(clientCache.Get(1));
-            }
+            serverCache.Put(1, new EmptyObject());
+            Assert.IsNotNull(clientCache.Get(1));
         }
 
         /// <summary>
@@ -185,40 +179,60 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         }
 
         /// <summary>
+        /// Tests the GetAsync method.
+        /// </summary>
+        [Test]
+        public void TestGetAsync()
+        {
+            var cache = GetClientCache<int>();
+            cache[1] = 1;
+
+            // Existing key.
+            Assert.AreEqual(1, cache.GetAsync(1).Result);
+
+            // Missing key.
+            cache.Remove(1);
+            var aex = Assert.Throws<AggregateException>(() => cache.GetAsync(1).Wait());
+            Assert.IsInstanceOf<KeyNotFoundException>(aex.InnerException);
+
+            // Incorrect data type.
+            GetClientCache<Person>().PutAsync(1, new Person(1)).Wait();
+            aex = Assert.Throws<AggregateException>(() => cache.GetAsync(1).Wait());
+            Assert.IsInstanceOf<InvalidCastException>(aex.InnerException);
+        }
+
+        /// <summary>
         /// Tests the TryGet method.
         /// </summary>
         [Test]
         public void TestTryGet()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int?, int>(CacheName);
+            var cache = GetClientCache<int?, int>();
 
-                cache[1] = 0;
-                cache[2] = 2;
+            cache[1] = 0;
+            cache[2] = 2;
 
-                // Non-existent key.
-                int res;
-                var success = cache.TryGet(0, out res);
+            // Non-existent key.
+            int res;
+            var success = cache.TryGet(0, out res);
 
-                Assert.AreEqual(0, res);
-                Assert.IsFalse(success);
+            Assert.AreEqual(0, res);
+            Assert.IsFalse(success);
 
-                // Key with default value.
-                success = cache.TryGet(1, out res);
+            // Key with default value.
+            success = cache.TryGet(1, out res);
 
-                Assert.AreEqual(0, res);
-                Assert.IsTrue(success);
+            Assert.AreEqual(0, res);
+            Assert.IsTrue(success);
 
-                // Key with custom value.
-                success = cache.TryGet(2, out res);
+            // Key with custom value.
+            success = cache.TryGet(2, out res);
 
-                Assert.AreEqual(2, res);
-                Assert.IsTrue(success);
+            Assert.AreEqual(2, res);
+            Assert.IsTrue(success);
 
-                // Null key.
-                Assert.Throws<ArgumentNullException>(() => cache.TryGet(null, out res));
-            }
+            // Null key.
+            Assert.Throws<ArgumentNullException>(() => cache.TryGet(null, out res));
         }
 
         /// <summary>
@@ -227,30 +241,27 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestGetAll()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int?, int>(CacheName);
+            var cache = GetClientCache<int?, int>();
 
-                cache[1] = 1;
-                cache[2] = 2;
-                cache[3] = 3;
+            cache[1] = 1;
+            cache[2] = 2;
+            cache[3] = 3;
 
-                var res = cache.GetAll(new int?[] {1}).Single();
-                Assert.AreEqual(1, res.Key);
-                Assert.AreEqual(1, res.Value);
+            var res = cache.GetAll(new int?[] {1}).Single();
+            Assert.AreEqual(1, res.Key);
+            Assert.AreEqual(1, res.Value);
 
-                res = cache.GetAll(new int?[] {1, -1}).Single();
-                Assert.AreEqual(1, res.Key);
-                Assert.AreEqual(1, res.Value);
+            res = cache.GetAll(new int?[] {1, -1}).Single();
+            Assert.AreEqual(1, res.Key);
+            Assert.AreEqual(1, res.Value);
 
-                CollectionAssert.AreEquivalent(new[] {1, 2, 3},
-                    cache.GetAll(new int?[] {1, 2, 3}).Select(x => x.Value));
+            CollectionAssert.AreEquivalent(new[] {1, 2, 3},
+                cache.GetAll(new int?[] {1, 2, 3}).Select(x => x.Value));
 
-                Assert.Throws<ArgumentNullException>(() => cache.GetAll(null));
+            Assert.Throws<ArgumentNullException>(() => cache.GetAll(null));
 
-                Assert.Throws<IgniteClientException>(() => cache.GetAll(new int?[] {1, null}));
-                Assert.Throws<IgniteClientException>(() => cache.GetAll(new int?[] {null}));
-            }
+            Assert.Throws<IgniteClientException>(() => cache.GetAll(new int?[] {1, null}));
+            Assert.Throws<IgniteClientException>(() => cache.GetAll(new int?[] {null}));
         }
 
         /// <summary>
@@ -259,27 +270,24 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestGetAndPut()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int?, int?>(CacheName);
+            var cache = GetClientCache<int?, int?>();
 
-                Assert.IsFalse(cache.ContainsKey(1));
+            Assert.IsFalse(cache.ContainsKey(1));
 
-                var res = cache.GetAndPut(1, 1);
-                Assert.IsFalse(res.Success);
-                Assert.IsNull(res.Value);
+            var res = cache.GetAndPut(1, 1);
+            Assert.IsFalse(res.Success);
+            Assert.IsNull(res.Value);
 
-                Assert.IsTrue(cache.ContainsKey(1));
+            Assert.IsTrue(cache.ContainsKey(1));
 
-                res = cache.GetAndPut(1, 2);
-                Assert.IsTrue(res.Success);
-                Assert.AreEqual(1, res.Value);
+            res = cache.GetAndPut(1, 2);
+            Assert.IsTrue(res.Success);
+            Assert.AreEqual(1, res.Value);
 
-                Assert.AreEqual(2, cache[1]);
+            Assert.AreEqual(2, cache[1]);
 
-                Assert.Throws<ArgumentNullException>(() => cache.GetAndPut(1, null));
-                Assert.Throws<ArgumentNullException>(() => cache.GetAndPut(null, 1));
-            }
+            Assert.Throws<ArgumentNullException>(() => cache.GetAndPut(1, null));
+            Assert.Throws<ArgumentNullException>(() => cache.GetAndPut(null, 1));
         }
 
         /// <summary>
@@ -288,28 +296,25 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestGetAndReplace()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int?, int?>(CacheName);
+            var cache = GetClientCache<int?, int?>();
 
-                Assert.IsFalse(cache.ContainsKey(1));
+            Assert.IsFalse(cache.ContainsKey(1));
 
-                var res = cache.GetAndReplace(1, 1);
-                Assert.IsFalse(res.Success);
-                Assert.IsNull(res.Value);
+            var res = cache.GetAndReplace(1, 1);
+            Assert.IsFalse(res.Success);
+            Assert.IsNull(res.Value);
 
-                Assert.IsFalse(cache.ContainsKey(1));
-                cache[1] = 1;
+            Assert.IsFalse(cache.ContainsKey(1));
+            cache[1] = 1;
 
-                res = cache.GetAndReplace(1, 2);
-                Assert.IsTrue(res.Success);
-                Assert.AreEqual(1, res.Value);
+            res = cache.GetAndReplace(1, 2);
+            Assert.IsTrue(res.Success);
+            Assert.AreEqual(1, res.Value);
 
-                Assert.AreEqual(2, cache[1]);
+            Assert.AreEqual(2, cache[1]);
 
-                Assert.Throws<ArgumentNullException>(() => cache.GetAndReplace(1, null));
-                Assert.Throws<ArgumentNullException>(() => cache.GetAndReplace(null, 1));
-            }
+            Assert.Throws<ArgumentNullException>(() => cache.GetAndReplace(1, null));
+            Assert.Throws<ArgumentNullException>(() => cache.GetAndReplace(null, 1));
         }
 
         /// <summary>
@@ -318,27 +323,24 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestGetAndRemove()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int?, int?>(CacheName);
+            var cache = GetClientCache<int?, int?>();
 
-                Assert.IsFalse(cache.ContainsKey(1));
+            Assert.IsFalse(cache.ContainsKey(1));
 
-                var res = cache.GetAndRemove(1);
-                Assert.IsFalse(res.Success);
-                Assert.IsNull(res.Value);
+            var res = cache.GetAndRemove(1);
+            Assert.IsFalse(res.Success);
+            Assert.IsNull(res.Value);
 
-                Assert.IsFalse(cache.ContainsKey(1));
-                cache[1] = 1;
+            Assert.IsFalse(cache.ContainsKey(1));
+            cache[1] = 1;
 
-                res = cache.GetAndRemove(1);
-                Assert.IsTrue(res.Success);
-                Assert.AreEqual(1, res.Value);
+            res = cache.GetAndRemove(1);
+            Assert.IsTrue(res.Success);
+            Assert.AreEqual(1, res.Value);
 
-                Assert.IsFalse(cache.ContainsKey(1));
+            Assert.IsFalse(cache.ContainsKey(1));
 
-                Assert.Throws<ArgumentNullException>(() => cache.GetAndRemove(null));
-            }
+            Assert.Throws<ArgumentNullException>(() => cache.GetAndRemove(null));
         }
 
         /// <summary>
@@ -347,17 +349,14 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestContainsKey()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int?, int>(CacheName);
+            var cache = GetClientCache<int?, int>();
 
-                cache[1] = 1;
+            cache[1] = 1;
 
-                Assert.IsTrue(cache.ContainsKey(1));
-                Assert.IsFalse(cache.ContainsKey(2));
+            Assert.IsTrue(cache.ContainsKey(1));
+            Assert.IsFalse(cache.ContainsKey(2));
 
-                Assert.Throws<ArgumentNullException>(() => cache.ContainsKey(null));
-            }
+            Assert.Throws<ArgumentNullException>(() => cache.ContainsKey(null));
         }
 
         /// <summary>
@@ -366,27 +365,24 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestContainsKeys()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int, int>(CacheName);
+            var cache = GetClientCache<int, int>();
 
-                cache[1] = 1;
-                cache[2] = 2;
-                cache[3] = 3;
+            cache[1] = 1;
+            cache[2] = 2;
+            cache[3] = 3;
 
-                Assert.IsTrue(cache.ContainsKeys(new[] {1}));
-                Assert.IsTrue(cache.ContainsKeys(new[] {1, 2}));
-                Assert.IsTrue(cache.ContainsKeys(new[] {2, 1}));
-                Assert.IsTrue(cache.ContainsKeys(new[] {1, 2, 3}));
-                Assert.IsTrue(cache.ContainsKeys(new[] {1, 3, 2}));
+            Assert.IsTrue(cache.ContainsKeys(new[] {1}));
+            Assert.IsTrue(cache.ContainsKeys(new[] {1, 2}));
+            Assert.IsTrue(cache.ContainsKeys(new[] {2, 1}));
+            Assert.IsTrue(cache.ContainsKeys(new[] {1, 2, 3}));
+            Assert.IsTrue(cache.ContainsKeys(new[] {1, 3, 2}));
 
-                Assert.IsFalse(cache.ContainsKeys(new[] {0}));
-                Assert.IsFalse(cache.ContainsKeys(new[] {0, 1}));
-                Assert.IsFalse(cache.ContainsKeys(new[] {1, 0}));
-                Assert.IsFalse(cache.ContainsKeys(new[] {1, 2, 3, 0}));
+            Assert.IsFalse(cache.ContainsKeys(new[] {0}));
+            Assert.IsFalse(cache.ContainsKeys(new[] {0, 1}));
+            Assert.IsFalse(cache.ContainsKeys(new[] {1, 0}));
+            Assert.IsFalse(cache.ContainsKeys(new[] {1, 2, 3, 0}));
 
-                Assert.Throws<ArgumentNullException>(() => cache.ContainsKeys(null));
-            }
+            Assert.Throws<ArgumentNullException>(() => cache.ContainsKeys(null));
         }
 
         /// <summary>
@@ -395,23 +391,20 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestPutIfAbsent()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int?, int?>(CacheName);
+            var cache = GetClientCache<int?, int?>();
 
-                Assert.IsFalse(cache.ContainsKey(1));
+            Assert.IsFalse(cache.ContainsKey(1));
 
-                var res = cache.PutIfAbsent(1, 1);
-                Assert.IsTrue(res);
-                Assert.AreEqual(1, cache[1]);
+            var res = cache.PutIfAbsent(1, 1);
+            Assert.IsTrue(res);
+            Assert.AreEqual(1, cache[1]);
 
-                res = cache.PutIfAbsent(1, 2);
-                Assert.IsFalse(res);
-                Assert.AreEqual(1, cache[1]);
+            res = cache.PutIfAbsent(1, 2);
+            Assert.IsFalse(res);
+            Assert.AreEqual(1, cache[1]);
 
-                Assert.Throws<ArgumentNullException>(() => cache.PutIfAbsent(null, 1));
-                Assert.Throws<ArgumentNullException>(() => cache.PutIfAbsent(1, null));
-            }
+            Assert.Throws<ArgumentNullException>(() => cache.PutIfAbsent(null, 1));
+            Assert.Throws<ArgumentNullException>(() => cache.PutIfAbsent(1, null));
         }
 
         /// <summary>
@@ -420,25 +413,22 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestGetAndPutIfAbsent()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int?, int?>(CacheName);
+            var cache = GetClientCache<int?, int?>();
 
-                Assert.IsFalse(cache.ContainsKey(1));
+            Assert.IsFalse(cache.ContainsKey(1));
 
-                var res = cache.GetAndPutIfAbsent(1, 1);
-                Assert.IsFalse(res.Success);
-                Assert.IsNull(res.Value);
-                Assert.AreEqual(1, cache[1]);
+            var res = cache.GetAndPutIfAbsent(1, 1);
+            Assert.IsFalse(res.Success);
+            Assert.IsNull(res.Value);
+            Assert.AreEqual(1, cache[1]);
 
-                res = cache.GetAndPutIfAbsent(1, 2);
-                Assert.IsTrue(res.Success);
-                Assert.AreEqual(1, res.Value);
-                Assert.AreEqual(1, cache[1]);
+            res = cache.GetAndPutIfAbsent(1, 2);
+            Assert.IsTrue(res.Success);
+            Assert.AreEqual(1, res.Value);
+            Assert.AreEqual(1, cache[1]);
 
-                Assert.Throws<ArgumentNullException>(() => cache.GetAndPutIfAbsent(null, 1));
-                Assert.Throws<ArgumentNullException>(() => cache.GetAndPutIfAbsent(1, null));
-            }
+            Assert.Throws<ArgumentNullException>(() => cache.GetAndPutIfAbsent(null, 1));
+            Assert.Throws<ArgumentNullException>(() => cache.GetAndPutIfAbsent(1, null));
         }
 
         /// <summary>
@@ -447,25 +437,22 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestReplace()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int?, int?>(CacheName);
+            var cache = GetClientCache<int?, int?>();
 
-                Assert.IsFalse(cache.ContainsKey(1));
+            Assert.IsFalse(cache.ContainsKey(1));
 
-                var res = cache.Replace(1, 1);
-                Assert.IsFalse(res);
-                Assert.IsFalse(cache.ContainsKey(1));
+            var res = cache.Replace(1, 1);
+            Assert.IsFalse(res);
+            Assert.IsFalse(cache.ContainsKey(1));
 
-                cache[1] = 1;
+            cache[1] = 1;
 
-                res = cache.Replace(1, 2);
-                Assert.IsTrue(res);
-                Assert.AreEqual(2, cache[1]);
+            res = cache.Replace(1, 2);
+            Assert.IsTrue(res);
+            Assert.AreEqual(2, cache[1]);
 
-                Assert.Throws<ArgumentNullException>(() => cache.Replace(null, 1));
-                Assert.Throws<ArgumentNullException>(() => cache.Replace(1, null));
-            }
+            Assert.Throws<ArgumentNullException>(() => cache.Replace(null, 1));
+            Assert.Throws<ArgumentNullException>(() => cache.Replace(1, null));
         }
 
         /// <summary>
@@ -474,30 +461,27 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestReplaceIfEquals()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int?, int?>(CacheName);
+            var cache = GetClientCache<int?, int?>();
 
-                Assert.IsFalse(cache.ContainsKey(1));
+            Assert.IsFalse(cache.ContainsKey(1));
 
-                var res = cache.Replace(1, 1, 2);
-                Assert.IsFalse(res);
-                Assert.IsFalse(cache.ContainsKey(1));
+            var res = cache.Replace(1, 1, 2);
+            Assert.IsFalse(res);
+            Assert.IsFalse(cache.ContainsKey(1));
 
-                cache[1] = 1;
+            cache[1] = 1;
 
-                res = cache.Replace(1, -1, 2);
-                Assert.IsFalse(res);
-                Assert.AreEqual(1, cache[1]);
+            res = cache.Replace(1, -1, 2);
+            Assert.IsFalse(res);
+            Assert.AreEqual(1, cache[1]);
 
-                res = cache.Replace(1, 1, 2);
-                Assert.IsTrue(res);
-                Assert.AreEqual(2, cache[1]);
+            res = cache.Replace(1, 1, 2);
+            Assert.IsTrue(res);
+            Assert.AreEqual(2, cache[1]);
 
-                Assert.Throws<ArgumentNullException>(() => cache.Replace(null, 1, 1));
-                Assert.Throws<ArgumentNullException>(() => cache.Replace(1, null, 1));
-                Assert.Throws<ArgumentNullException>(() => cache.Replace(1, 1, null));
-            }
+            Assert.Throws<ArgumentNullException>(() => cache.Replace(null, 1, 1));
+            Assert.Throws<ArgumentNullException>(() => cache.Replace(1, null, 1));
+            Assert.Throws<ArgumentNullException>(() => cache.Replace(1, 1, null));
         }
 
         /// <summary>
@@ -509,7 +493,7 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
             using (var client = GetClient())
             {
                 // Primitives.
-                var cache = client.GetCache<int?, int?>(CacheName);
+                var cache = GetClientCache<int?, int?>();
 
                 cache.PutAll(Enumerable.Range(1, 3).ToDictionary(x => (int?) x, x => (int?) x + 1));
 
@@ -543,6 +527,13 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
                 Assert.AreEqual(res2, res2.Inner.Inner);
                 Assert.IsNotNull(res3.Inner.Inner.Inner);
 
+                // Huge data set.
+                var cache3 = client.GetCache<int, Person>(CacheName);
+                const int count = 30000;
+
+                cache3.PutAll(Enumerable.Range(1, count).ToDictionary(x => x, x => new Person(x)));
+                Assert.AreEqual(count, cache3.GetSize());
+
                 // Nulls.
                 Assert.Throws<ArgumentNullException>(() => cache.PutAll(null));
 
@@ -564,18 +555,15 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestClear()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int?, int?>(CacheName);
+            var cache = GetClientCache<int?, int?>();
 
-                cache[1] = 1;
-                cache[2] = 2;
+            cache[1] = 1;
+            cache[2] = 2;
 
-                cache.Clear();
+            cache.Clear();
 
-                Assert.IsFalse(cache.ContainsKey(1));
-                Assert.IsFalse(cache.ContainsKey(2));
-            }
+            Assert.IsFalse(cache.ContainsKey(1));
+            Assert.IsFalse(cache.ContainsKey(2));
         }
 
         /// <summary>
@@ -584,23 +572,20 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestClearKey()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int?, int?>(CacheName);
+            var cache = GetClientCache<int?, int?>();
 
-                cache[1] = 1;
-                cache[2] = 2;
+            cache[1] = 1;
+            cache[2] = 2;
 
-                cache.Clear(1);
-                Assert.IsFalse(cache.ContainsKey(1));
-                Assert.IsTrue(cache.ContainsKey(2));
+            cache.Clear(1);
+            Assert.IsFalse(cache.ContainsKey(1));
+            Assert.IsTrue(cache.ContainsKey(2));
 
-                cache.Clear(2);
-                Assert.IsFalse(cache.ContainsKey(1));
-                Assert.IsFalse(cache.ContainsKey(2));
+            cache.Clear(2);
+            Assert.IsFalse(cache.ContainsKey(1));
+            Assert.IsFalse(cache.ContainsKey(2));
 
-                Assert.Throws<ArgumentNullException>(() => cache.Clear(null));
-            }
+            Assert.Throws<ArgumentNullException>(() => cache.Clear(null));
         }
 
         /// <summary>
@@ -609,22 +594,19 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestClearAll()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int?, int?>(CacheName);
+            var cache = GetClientCache<int?, int?>();
 
-                cache[1] = 1;
-                cache[2] = 2;
-                cache[3] = 3;
+            cache[1] = 1;
+            cache[2] = 2;
+            cache[3] = 3;
 
-                cache.ClearAll(new int?[] {1, 3});
-                Assert.IsFalse(cache.ContainsKey(1));
-                Assert.IsTrue(cache.ContainsKey(2));
-                Assert.IsFalse(cache.ContainsKey(3));
+            cache.ClearAll(new int?[] {1, 3});
+            Assert.IsFalse(cache.ContainsKey(1));
+            Assert.IsTrue(cache.ContainsKey(2));
+            Assert.IsFalse(cache.ContainsKey(3));
 
-                Assert.Throws<ArgumentNullException>(() => cache.ClearAll(null));
-                Assert.Throws<IgniteClientException>(() => cache.ClearAll(new int?[] {null, 1}));
-            }
+            Assert.Throws<ArgumentNullException>(() => cache.ClearAll(null));
+            Assert.Throws<IgniteClientException>(() => cache.ClearAll(new int?[] {null, 1}));
         }
 
         /// <summary>
@@ -633,28 +615,25 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestRemove()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int?, int?>(CacheName);
+            var cache = GetClientCache<int?, int?>();
 
-                cache[1] = 1;
-                cache[2] = 2;
+            cache[1] = 1;
+            cache[2] = 2;
 
-                var res = cache.Remove(1);
-                Assert.IsTrue(res);
-                Assert.IsFalse(cache.ContainsKey(1));
-                Assert.IsTrue(cache.ContainsKey(2));
+            var res = cache.Remove(1);
+            Assert.IsTrue(res);
+            Assert.IsFalse(cache.ContainsKey(1));
+            Assert.IsTrue(cache.ContainsKey(2));
 
-                res = cache.Remove(2);
-                Assert.IsTrue(res);
-                Assert.IsFalse(cache.ContainsKey(1));
-                Assert.IsFalse(cache.ContainsKey(2));
+            res = cache.Remove(2);
+            Assert.IsTrue(res);
+            Assert.IsFalse(cache.ContainsKey(1));
+            Assert.IsFalse(cache.ContainsKey(2));
 
-                res = cache.Remove(-1);
-                Assert.IsFalse(res);
+            res = cache.Remove(-1);
+            Assert.IsFalse(res);
 
-                Assert.Throws<ArgumentNullException>(() => cache.Remove(null));
-            }
+            Assert.Throws<ArgumentNullException>(() => cache.Remove(null));
         }
 
         /// <summary>
@@ -663,35 +642,32 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestRemoveKeyVal()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int?, int?>(CacheName);
+            var cache = GetClientCache<int?, int?>();
 
-                cache[1] = 1;
-                cache[2] = 2;
+            cache[1] = 1;
+            cache[2] = 2;
 
-                var res = cache.Remove(1, 0);
-                Assert.IsFalse(res);
+            var res = cache.Remove(1, 0);
+            Assert.IsFalse(res);
 
-                res = cache.Remove(0, 0);
-                Assert.IsFalse(res);
+            res = cache.Remove(0, 0);
+            Assert.IsFalse(res);
 
-                res = cache.Remove(1, 1);
-                Assert.IsTrue(res);
-                Assert.IsFalse(cache.ContainsKey(1));
-                Assert.IsTrue(cache.ContainsKey(2));
+            res = cache.Remove(1, 1);
+            Assert.IsTrue(res);
+            Assert.IsFalse(cache.ContainsKey(1));
+            Assert.IsTrue(cache.ContainsKey(2));
 
-                res = cache.Remove(2, 2);
-                Assert.IsTrue(res);
-                Assert.IsFalse(cache.ContainsKey(1));
-                Assert.IsFalse(cache.ContainsKey(2));
+            res = cache.Remove(2, 2);
+            Assert.IsTrue(res);
+            Assert.IsFalse(cache.ContainsKey(1));
+            Assert.IsFalse(cache.ContainsKey(2));
 
-                res = cache.Remove(2, 2);
-                Assert.IsFalse(res);
+            res = cache.Remove(2, 2);
+            Assert.IsFalse(res);
 
-                Assert.Throws<ArgumentNullException>(() => cache.Remove(1, null));
-                Assert.Throws<ArgumentNullException>(() => cache.Remove(null, 1));
-            }
+            Assert.Throws<ArgumentNullException>(() => cache.Remove(1, null));
+            Assert.Throws<ArgumentNullException>(() => cache.Remove(null, 1));
         }
 
         /// <summary>
@@ -700,27 +676,24 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestRemoveReys()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int?, int?>(CacheName);
-                var keys = Enumerable.Range(1, 10).Cast<int?>().ToArray();
+            var cache = GetClientCache<int?, int?>();
+            var keys = Enumerable.Range(1, 10).Cast<int?>().ToArray();
 
-                cache.PutAll(keys.ToDictionary(x => x, x => x));
+            cache.PutAll(keys.ToDictionary(x => x, x => x));
 
-                cache.RemoveAll(keys.Skip(2));
-                CollectionAssert.AreEquivalent(keys.Take(2), cache.GetAll(keys).Select(x => x.Key));
+            cache.RemoveAll(keys.Skip(2));
+            CollectionAssert.AreEquivalent(keys.Take(2), cache.GetAll(keys).Select(x => x.Key));
 
-                cache.RemoveAll(new int?[] {1});
-                Assert.AreEqual(2, cache.GetAll(keys).Single().Value);
+            cache.RemoveAll(new int?[] {1});
+            Assert.AreEqual(2, cache.GetAll(keys).Single().Value);
 
-                cache.RemoveAll(keys);
-                cache.RemoveAll(keys);
+            cache.RemoveAll(keys);
+            cache.RemoveAll(keys);
 
-                Assert.AreEqual(0, cache.GetSize());
+            Assert.AreEqual(0, cache.GetSize());
 
-                Assert.Throws<ArgumentNullException>(() => cache.RemoveAll(null));
-                Assert.Throws<IgniteClientException>(() => cache.RemoveAll(new int?[] {1, null}));
-            }
+            Assert.Throws<ArgumentNullException>(() => cache.RemoveAll(null));
+            Assert.Throws<IgniteClientException>(() => cache.RemoveAll(new int?[] {1, null}));
         }
 
         /// <summary>
@@ -748,38 +721,35 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         [Test]
         public void TestGetSize()
         {
-            using (var client = GetClient())
-            {
-                var cache = client.GetCache<int, int>(CacheName);
-
-                Assert.AreEqual(0, cache.GetSize());
-                Assert.AreEqual(0, cache.GetSize(CachePeekMode.All));
-                Assert.AreEqual(0, cache.GetSize(CachePeekMode.Backup));
-                Assert.AreEqual(0, cache.GetSize(CachePeekMode.Near));
-                Assert.AreEqual(0, cache.GetSize(CachePeekMode.Offheap));
-                Assert.AreEqual(0, cache.GetSize(CachePeekMode.Onheap));
-                Assert.AreEqual(0, cache.GetSize(CachePeekMode.Primary));
-
-                cache[1] = 1;
-                
-                Assert.AreEqual(1, cache.GetSize());
-                Assert.AreEqual(1, cache.GetSize(CachePeekMode.All));
-                Assert.AreEqual(0, cache.GetSize(CachePeekMode.Backup));
-                Assert.AreEqual(0, cache.GetSize(CachePeekMode.Near));
-                Assert.AreEqual(1, cache.GetSize(CachePeekMode.Offheap));
-                Assert.AreEqual(0, cache.GetSize(CachePeekMode.Onheap));
-                Assert.AreEqual(1, cache.GetSize(CachePeekMode.Primary));
-
-                cache.PutAll(Enumerable.Range(1, 100).ToDictionary(x => x, x => x));
-                
-                Assert.AreEqual(100, cache.GetSize());
-                Assert.AreEqual(100, cache.GetSize(CachePeekMode.All));
-                Assert.AreEqual(0, cache.GetSize(CachePeekMode.Backup));
-                Assert.AreEqual(0, cache.GetSize(CachePeekMode.Near));
-                Assert.AreEqual(100, cache.GetSize(CachePeekMode.Offheap));
-                Assert.AreEqual(0, cache.GetSize(CachePeekMode.Onheap));
-                Assert.AreEqual(100, cache.GetSize(CachePeekMode.Primary));
-            }
+            var cache = GetClientCache<int>();
+
+            Assert.AreEqual(0, cache.GetSize());
+            Assert.AreEqual(0, cache.GetSize(CachePeekMode.All));
+            Assert.AreEqual(0, cache.GetSize(CachePeekMode.Backup));
+            Assert.AreEqual(0, cache.GetSize(CachePeekMode.Near));
+            Assert.AreEqual(0, cache.GetSize(CachePeekMode.Offheap));
+            Assert.AreEqual(0, cache.GetSize(CachePeekMode.Onheap));
+            Assert.AreEqual(0, cache.GetSize(CachePeekMode.Primary));
+
+            cache[1] = 1;
+
+            Assert.AreEqual(1, cache.GetSize());
+            Assert.AreEqual(1, cache.GetSize(CachePeekMode.All));
+            Assert.AreEqual(0, cache.GetSize(CachePeekMode.Backup));
+            Assert.AreEqual(0, cache.GetSize(CachePeekMode.Near));
+            Assert.AreEqual(1, cache.GetSize(CachePeekMode.Offheap));
+            Assert.AreEqual(0, cache.GetSize(CachePeekMode.Onheap));
+            Assert.AreEqual(1, cache.GetSize(CachePeekMode.Primary));
+
+            cache.PutAll(Enumerable.Range(1, 100).ToDictionary(x => x, x => x));
+
+            Assert.AreEqual(100, cache.GetSize());
+            Assert.AreEqual(100, cache.GetSize(CachePeekMode.All));
+            Assert.AreEqual(0, cache.GetSize(CachePeekMode.Backup));
+            Assert.AreEqual(0, cache.GetSize(CachePeekMode.Near));
+            Assert.AreEqual(100, cache.GetSize(CachePeekMode.Offheap));
+            Assert.AreEqual(0, cache.GetSize(CachePeekMode.Onheap));
+            Assert.AreEqual(100, cache.GetSize(CachePeekMode.Primary));
         }
 
         /// <summary>
@@ -826,22 +796,81 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
         }
 
         /// <summary>
+        /// Tests interleaved put/get operations.
+        /// </summary>
+        [Test]
+        [Category(TestUtils.CategoryIntensive)]
+        public void TestPutGetAsyncMultithreaded()
+        {
+            const int count = 5000;
+
+            var cache = GetClientCache<string>();
+            var key = 0;
+
+            TestUtils.RunMultiThreaded(() =>
+            {
+                for (var i = 0; i < count; i++)
+                {
+                    // ReSharper disable once AccessToModifiedClosure
+                    var k = Interlocked.Increment(ref key);
+                    var v = k + "_" + Guid.NewGuid();
+
+                    if (k % 3 == 0)
+                    {
+                        cache.Put(k, v);
+                    }
+                    else
+                    {
+                        cache.PutAsync(k, v);
+                    }
+                }
+            }, Environment.ProcessorCount * 2);
+
+            key = 0;
+
+            TestUtils.RunMultiThreaded(() =>
+            {
+                for (var i = 0; i < count; i++)
+                {
+                    var k = Interlocked.Increment(ref key);
+                    var val = k % 3 == 0 ? cache.Get(k) : cache.GetAsync(k).Result;
+
+                    Assert.IsTrue(val.Split('_').First() == k.ToString());
+                }
+            }, Environment.ProcessorCount * 2);
+        }
+
+        /// <summary>
+        /// Tests that long operation completes later than short operation.
+        /// </summary>
+        [Test]
+        public void TestAsyncCompletionOrder()
+        {
+            var cache = GetClientCache<int>();
+
+            var t1 = cache.PutAllAsync(Enumerable.Range(1, 100000).ToDictionary(x => x, x => x));
+            var t2 = cache.PutAsync(-1, -1);
+
+            t2.Wait();
+            Assert.IsFalse(t1.IsCompleted);
+
+            t1.Wait();
+        }
+
+        /// <summary>
         /// Tests the cache exceptions.
         /// </summary>
         [Test]
         public void TestExceptions()
         {
-            using (var client = GetClient())
-            {
-                // Getting the cache instance does not throw.
-                var cache = client.GetCache<int, int>("foobar");
+            // Getting the cache instance does not throw.
+            var cache = GetClientCache<int, int>("foobar");
 
-                // Accessing non-existent cache throws.
-                var ex = Assert.Throws<IgniteClientException>(() => cache.Put(1, 1));
+            // Accessing non-existent cache throws.
+            var ex = Assert.Throws<IgniteClientException>(() => cache.Put(1, 1));
 
-                Assert.AreEqual("Cache doesn't exist: foobar", ex.Message);
-                Assert.AreEqual(ClientStatusCode.CacheDoesNotExist, ex.StatusCode);
-            }
+            Assert.AreEqual("Cache doesn't exist: foobar", ex.Message);
+            Assert.AreEqual(ClientStatusCode.CacheDoesNotExist, ex.StatusCode);
         }
 
         /// <summary>
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestAsync.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestAsync.cs
new file mode 100644 (file)
index 0000000..2081167
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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 Apache.Ignite.Core.Client.Cache;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// Async cache test.
+    /// </summary>
+    [TestFixture]
+    public sealed class CacheTestAsync : CacheTest
+    {
+        /** <inheritdoc /> */
+        protected override ICacheClient<TK, TV> GetClientCache<TK, TV>(string cacheName = CacheName)
+        {
+            return new CacheClientAsyncWrapper<TK, TV>(base.GetClientCache<TK, TV>(cacheName));
+        }
+    }
+}
index 145762a..2b92eb7 100644 (file)
 namespace Apache.Ignite.Core.Tests.Client
 {
     using System;
+    using System.Collections.Generic;
     using System.Linq;
     using System.Net;
     using System.Net.Sockets;
+    using System.Threading;
+    using System.Threading.Tasks;
     using Apache.Ignite.Core.Client;
     using Apache.Ignite.Core.Configuration;
     using NUnit.Framework;
@@ -33,8 +36,8 @@ namespace Apache.Ignite.Core.Tests.Client
         /// <summary>
         /// Fixture tear down.
         /// </summary>
-        [TestFixtureTearDown]
-        public void FixtureTearDown()
+        [TearDown]
+        public void TearDown()
         {
             Ignition.StopAll(true);
         }
@@ -128,7 +131,7 @@ namespace Apache.Ignite.Core.Tests.Client
 
                 Assert.AreEqual(ClientStatusCode.Fail, ex.StatusCode);
 
-                Assert.AreEqual("Client handhsake failed: 'Unsupported version.'. " +
+                Assert.AreEqual("Client handshake failed: 'Unsupported version.'. " +
                                 "Client version: -1.-1.-1. Server version: 1.0.0", ex.Message);
             }
         }
@@ -159,6 +162,116 @@ namespace Apache.Ignite.Core.Tests.Client
         }
 
         /// <summary>
+        /// Tests that we get a proper exception when server disconnects (node shutdown, network issues, etc).
+        /// </summary>
+        [Test]
+        public void TestServerConnectionAborted()
+        {
+            var evt = new ManualResetEventSlim();
+            var ignite = Ignition.Start(TestUtils.GetTestConfiguration());
+
+            var putGetTask = Task.Factory.StartNew(() =>
+            {
+                using (var client = StartClient())
+                {
+                    var cache = client.GetOrCreateCache<int, int>("foo");
+                    evt.Set();
+
+                    for (var i = 0; i < 100000; i++)
+                    {
+                        cache[i] = i;
+                        Assert.AreEqual(i, cache.GetAsync(i).Result);
+                    }
+                }
+            });
+
+            evt.Wait();
+            ignite.Dispose();
+
+            var ex = Assert.Throws<AggregateException>(() => putGetTask.Wait());
+            var baseEx = ex.GetBaseException();
+            var socketEx = baseEx as SocketException;
+
+            if (socketEx != null)
+            {
+                Assert.AreEqual(SocketError.ConnectionAborted, socketEx.SocketErrorCode);
+            }
+            else
+            {
+                Assert.Fail("Unexpected exception: " + ex);
+            }
+        }
+
+        /// <summary>
+        /// Tests the operation timeout.
+        /// </summary>
+        [Test]
+        [Category(TestUtils.CategoryIntensive)]
+        public void TestOperationTimeout()
+        {
+            var data = Enumerable.Range(1, 500000).ToDictionary(x => x, x => x.ToString());
+
+            Ignition.Start(TestUtils.GetTestConfiguration());
+
+            var cfg = GetClientConfiguration();
+            cfg.SocketTimeout = TimeSpan.FromMilliseconds(500);
+            var client = Ignition.StartClient(cfg);
+            var cache = client.CreateCache<int, string>("s");
+            Assert.AreEqual(cfg.SocketTimeout, client.GetConfiguration().SocketTimeout);
+
+            // Async.
+            var task = cache.PutAllAsync(data);
+            Assert.IsFalse(task.IsCompleted);
+            var aex = Assert.Throws<AggregateException>(() => task.Wait());
+            Assert.AreEqual(SocketError.TimedOut, ((SocketException) aex.GetBaseException()).SocketErrorCode);
+
+            // Sync (reconnect for clean state).
+            Ignition.StopAll(true);
+            Ignition.Start(TestUtils.GetTestConfiguration());
+            client = Ignition.StartClient(cfg);
+            cache = client.CreateCache<int, string>("s");
+            var ex = Assert.Throws<SocketException>(() => cache.PutAll(data));
+            Assert.AreEqual(SocketError.TimedOut, ex.SocketErrorCode);
+        }
+
+        /// <summary>
+        /// Tests the client dispose while operations are in progress.
+        /// </summary>
+        [Test]
+        [Category(TestUtils.CategoryIntensive)]
+        public void TestClientDisposeWhileOperationsAreInProgress()
+        {
+            Ignition.Start(TestUtils.GetTestConfiguration());
+
+            var ops = new List<Task>();
+
+            using (var client = StartClient())
+            {
+                var cache = client.GetOrCreateCache<int, int>("foo");
+                for (var i = 0; i < 100000; i++)
+                {
+                    ops.Add(cache.PutAsync(i, i));
+                }
+                ops.First().Wait();
+            }
+
+            var completed = ops.Count(x => x.Status == TaskStatus.RanToCompletion);
+            Assert.Greater(completed, 0, "Some tasks should have completed.");
+
+            var failed = ops.Where(x => x.Status == TaskStatus.Faulted).ToArray();
+            Assert.IsTrue(failed.Any(), "Some tasks should have failed.");
+
+            foreach (var task in failed)
+            {
+                var ex = task.Exception;
+                Assert.IsNotNull(ex);
+                var baseEx = ex.GetBaseException();
+                Assert.IsNotNull((object) (baseEx as SocketException) ?? baseEx as ObjectDisposedException, 
+                    ex.ToString());
+            }
+        }
+
+        /// <summary>
         /// Starts the client.
         /// </summary>
         private static IIgniteClient StartClient()
index 6177f34..78d571b 100644 (file)
@@ -87,7 +87,12 @@ namespace Apache.Ignite.Core.Tests.Client
         [SetUp]
         public virtual void TestSetUp()
         {
-            GetCache<int>().RemoveAll();
+            var cache = GetCache<int>();
+            cache.RemoveAll();
+            cache.Clear();
+            
+            Assert.AreEqual(0, cache.GetSize(CachePeekMode.All));
+            Assert.AreEqual(0, GetClientCache<int>().GetSize(CachePeekMode.All));
         }
 
         /// <summary>
@@ -108,7 +113,15 @@ namespace Apache.Ignite.Core.Tests.Client
         /// </summary>
         protected ICacheClient<int, T> GetClientCache<T>()
         {
-            return Client.GetCache<int, T>(CacheName);
+            return GetClientCache<int, T>();
+        }
+
+        /// <summary>
+        /// Gets the client cache.
+        /// </summary>
+        protected virtual ICacheClient<TK, TV> GetClientCache<TK, TV>(string cacheName = CacheName)
+        {
+            return Client.GetCache<TK, TV>(cacheName ?? CacheName);
         }
 
         /// <summary>
index 0b28cfd..1857520 100644 (file)
@@ -43,6 +43,7 @@ namespace Apache.Ignite.Core.Tests.Client
             Assert.AreEqual(IgniteClientConfiguration.DefaultSocketBufferSize, cfg.SocketReceiveBufferSize);
             Assert.AreEqual(IgniteClientConfiguration.DefaultSocketBufferSize, cfg.SocketSendBufferSize);
             Assert.AreEqual(IgniteClientConfiguration.DefaultTcpNoDelay, cfg.TcpNoDelay);
+            Assert.AreEqual(IgniteClientConfiguration.DefaultSocketTimeout, cfg.SocketTimeout);
         }
 
         /// <summary>
@@ -68,6 +69,7 @@ namespace Apache.Ignite.Core.Tests.Client
                 SocketReceiveBufferSize = 222,
                 SocketSendBufferSize = 333,
                 TcpNoDelay = false,
+                SocketTimeout = TimeSpan.FromSeconds(15),
                 BinaryConfiguration = new BinaryConfiguration
                 {
                     CompactFooter = false,
index 9a19e32..18026b4 100644 (file)
Binary files a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/Client/IgniteClientConfiguration.xml and b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/Client/IgniteClientConfiguration.xml differ
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TaskExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TaskExtensions.cs
new file mode 100644 (file)
index 0000000..6e8489e
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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
+{
+    using System;
+    using System.Threading.Tasks;
+
+    /// <summary>
+    /// Task extensions.
+    /// </summary>
+    public static class TaskExtensions
+    {
+        /// <summary>
+        /// Waits the result of a task, unwraps exceptions.
+        /// </summary>
+        /// <param name="task">The task.</param>
+        public static void WaitResult(this Task task)
+        {
+            try
+            {
+                task.Wait();
+            }
+            catch (AggregateException ex)
+            {
+                throw ex.GetBaseException();
+            }
+        }
+
+        /// <summary>
+        /// Gets the result of a task, unwraps exceptions.
+        /// </summary>
+        public static T GetResult<T>(this Task<T> task)
+        {
+            try
+            {
+                return task.Result;
+            }
+            catch (AggregateException ex)
+            {
+                throw ex.GetBaseException();
+            }
+        }
+    }
+}
index 47b780d..818a7f6 100644 (file)
  * limitations under the License.
  */
 
+// ReSharper disable UnusedParameter.Global
 namespace Apache.Ignite.Core.Client.Cache
 {
     using System;
     using System.Collections.Generic;
+    using System.Threading.Tasks;
     using Apache.Ignite.Core.Cache;
     using Apache.Ignite.Core.Cache.Query;
 
     /// <summary>
     /// Client cache API. See <see cref="IIgniteClient.GetCache{K, V}"/>.
     /// </summary>
-    // ReSharper disable once TypeParameterCanBeVariant (ICache shoul not be variant, more methods will be added)
     public interface ICacheClient<TK, TV>
     {
         /// <summary>
@@ -44,6 +45,16 @@ namespace Apache.Ignite.Core.Client.Cache
         void Put(TK key, TV val);
 
         /// <summary>
+        /// Associates the specified value with the specified key in the cache.
+        /// <para />
+        /// If the cache previously contained a mapping for the key,
+        /// the old value is replaced by the specified value.
+        /// </summary>
+        /// <param name="key">Key with which the specified value is to be associated.</param>
+        /// <param name="val">Value to be associated with the specified key.</param>
+        Task PutAsync(TK key, TV val);
+
+        /// <summary>
         /// Retrieves value mapped to the specified key from cache.
         /// </summary>
         /// <param name="key">Key.</param>
@@ -55,6 +66,14 @@ namespace Apache.Ignite.Core.Client.Cache
         /// Retrieves value mapped to the specified key from cache.
         /// </summary>
         /// <param name="key">Key.</param>
+        /// <returns>Value.</returns>
+        /// <exception cref="KeyNotFoundException">If the key is not present in the cache.</exception>
+        Task<TV> GetAsync(TK key);
+
+        /// <summary>
+        /// Retrieves value mapped to the specified key from cache.
+        /// </summary>
+        /// <param name="key">Key.</param>
         /// <param name="value">When this method returns, the value associated with the specified key,
         /// if the key is found; otherwise, the default value for the type of the value parameter.
         /// This parameter is passed uninitialized.</param>
@@ -64,6 +83,15 @@ namespace Apache.Ignite.Core.Client.Cache
         bool TryGet(TK key, out TV value);
 
         /// <summary>
+        /// Retrieves value mapped to the specified key from cache.
+        /// </summary>
+        /// <param name="key">Key.</param>
+        /// <returns>
+        /// <see cref="CacheResult{T}"/> containing a bool success flag and a value.
+        /// </returns>
+        Task<CacheResult<TV>> TryGetAsync(TK key);
+
+        /// <summary>
         /// Retrieves values mapped to the specified keys from cache.
         /// </summary>
         /// <param name="keys">Keys.</param>
@@ -71,6 +99,13 @@ namespace Apache.Ignite.Core.Client.Cache
         ICollection<ICacheEntry<TK, TV>> GetAll(IEnumerable<TK> keys);
 
         /// <summary>
+        /// Retrieves values mapped to the specified keys from cache.
+        /// </summary>
+        /// <param name="keys">Keys.</param>
+        /// <returns>Map of key-value pairs.</returns>
+        Task<ICollection<ICacheEntry<TK, TV>>> GetAllAsync(IEnumerable<TK> keys);
+
+        /// <summary>
         /// Gets or sets a cache value with the specified key.
         /// Shortcut to <see cref="Get"/> and <see cref="Put"/>
         /// </summary>
@@ -87,6 +122,13 @@ namespace Apache.Ignite.Core.Client.Cache
         bool ContainsKey(TK key);
 
         /// <summary>
+        /// Check if cache contains mapping for this key.
+        /// </summary>
+        /// <param name="key">Key.</param>
+        /// <returns>True if cache contains mapping for this key.</returns>
+        Task<bool> ContainsKeyAsync(TK key);
+
+        /// <summary>
         /// Check if cache contains mapping for these keys.
         /// </summary>
         /// <param name="keys">Keys.</param>
@@ -94,6 +136,13 @@ namespace Apache.Ignite.Core.Client.Cache
         bool ContainsKeys(IEnumerable<TK> keys);
 
         /// <summary>
+        /// Check if cache contains mapping for these keys.
+        /// </summary>
+        /// <param name="keys">Keys.</param>
+        /// <returns>True if cache contains mapping for all these keys.</returns>
+        Task<bool> ContainsKeysAsync(IEnumerable<TK> keys);
+
+        /// <summary>
         /// Executes a Scan query.
         /// </summary>
         /// <param name="scanQuery">Scan query.</param>
@@ -126,6 +175,17 @@ namespace Apache.Ignite.Core.Client.Cache
         CacheResult<TV> GetAndPut(TK key, TV val);
 
         /// <summary>
+        /// Associates the specified value with the specified key in this cache,
+        /// returning an existing value if one existed.
+        /// </summary>
+        /// <param name="key">Key with which the specified value is to be associated.</param>
+        /// <param name="val">Value to be associated with the specified key.</param>
+        /// <returns>
+        /// The value associated with the key at the start of the operation.
+        /// </returns>
+        Task<CacheResult<TV>> GetAndPutAsync(TK key, TV val);
+
+        /// <summary>
         /// Atomically replaces the value for a given key if and only if there is a value currently mapped by the key.
         /// </summary>
         /// <param name="key">Key with which the specified value is to be associated.</param>
@@ -136,6 +196,16 @@ namespace Apache.Ignite.Core.Client.Cache
         CacheResult<TV> GetAndReplace(TK key, TV val);
 
         /// <summary>
+        /// Atomically replaces the value for a given key if and only if there is a value currently mapped by the key.
+        /// </summary>
+        /// <param name="key">Key with which the specified value is to be associated.</param>
+        /// <param name="val">Value to be associated with the specified key.</param>
+        /// <returns>
+        /// The previous value associated with the specified key.
+        /// </returns>
+        Task<CacheResult<TV>> GetAndReplaceAsync(TK key, TV val);
+
+        /// <summary>
         /// Atomically removes the entry for a key only if currently mapped to some value.
         /// </summary>
         /// <param name="key">Key with which the specified value is associated.</param>
@@ -143,6 +213,13 @@ namespace Apache.Ignite.Core.Client.Cache
         CacheResult<TV> GetAndRemove(TK key);
 
         /// <summary>
+        /// Atomically removes the entry for a key only if currently mapped to some value.
+        /// </summary>
+        /// <param name="key">Key with which the specified value is associated.</param>
+        /// <returns>The value if one existed.</returns>
+        Task<CacheResult<TV>> GetAndRemoveAsync(TK key);
+
+        /// <summary>
         /// Atomically associates the specified key with the given value if it is not already associated with a value.
         /// </summary>
         /// <param name="key">Key with which the specified value is to be associated.</param>
@@ -151,6 +228,14 @@ namespace Apache.Ignite.Core.Client.Cache
         bool PutIfAbsent(TK key, TV val);
 
         /// <summary>
+        /// Atomically associates the specified key with the given value if it is not already associated with a value.
+        /// </summary>
+        /// <param name="key">Key with which the specified value is to be associated.</param>
+        /// <param name="val">Value to be associated with the specified key.</param>
+        /// <returns>True if a value was set.</returns>
+        Task<bool> PutIfAbsentAsync(TK key, TV val);
+
+        /// <summary>
         /// Stores given key-value pair in cache only if cache had no previous mapping for it.
         /// </summary>
         /// <param name="key">Key to store in cache.</param>
@@ -161,6 +246,16 @@ namespace Apache.Ignite.Core.Client.Cache
         CacheResult<TV> GetAndPutIfAbsent(TK key, TV val);
 
         /// <summary>
+        /// Stores given key-value pair in cache only if cache had no previous mapping for it.
+        /// </summary>
+        /// <param name="key">Key to store in cache.</param>
+        /// <param name="val">Value to be associated with the given key.</param>
+        /// <returns>
+        /// Previously contained value regardless of whether put happened or not.
+        /// </returns>
+        Task<CacheResult<TV>> GetAndPutIfAbsentAsync(TK key, TV val);
+
+        /// <summary>
         /// Stores given key-value pair in cache only if there is a previous mapping for it.
         /// </summary>
         /// <param name="key">Key to store in cache.</param>
@@ -169,6 +264,14 @@ namespace Apache.Ignite.Core.Client.Cache
         bool Replace(TK key, TV val);
 
         /// <summary>
+        /// Stores given key-value pair in cache only if there is a previous mapping for it.
+        /// </summary>
+        /// <param name="key">Key to store in cache.</param>
+        /// <param name="val">Value to be associated with the given key.</param>
+        /// <returns>True if the value was replaced.</returns>
+        Task<bool> ReplaceAsync(TK key, TV val);
+
+        /// <summary>
         /// Stores given key-value pair in cache only if only if the previous value is equal to the
         /// old value passed as argument.
         /// </summary>
@@ -179,29 +282,62 @@ namespace Apache.Ignite.Core.Client.Cache
         bool Replace(TK key, TV oldVal, TV newVal);
 
         /// <summary>
+        /// Stores given key-value pair in cache only if only if the previous value is equal to the
+        /// old value passed as argument.
+        /// </summary>
+        /// <param name="key">Key to store in cache.</param>
+        /// <param name="oldVal">Old value to match.</param>
+        /// <param name="newVal">Value to be associated with the given key.</param>
+        /// <returns>True if replace happened, false otherwise.</returns>
+        Task<bool> ReplaceAsync(TK key, TV oldVal, TV newVal);
+
+        /// <summary>
         /// Stores given key-value pairs in cache.
         /// </summary>
         /// <param name="vals">Key-value pairs to store in cache.</param>
         void PutAll(IEnumerable<KeyValuePair<TK, TV>> vals);
 
         /// <summary>
+        /// Stores given key-value pairs in cache.
+        /// </summary>
+        /// <param name="vals">Key-value pairs to store in cache.</param>
+        Task PutAllAsync(IEnumerable<KeyValuePair<TK, TV>> vals);
+
+        /// <summary>
         /// Clears the contents of the cache, without notifying listeners or CacheWriters.
         /// </summary>
         void Clear();
 
         /// <summary>
+        /// Clears the contents of the cache, without notifying listeners or CacheWriters.
+        /// </summary>
+        Task ClearAsync();
+
+        /// <summary>
         /// Clear entry from the cache, without notifying listeners or CacheWriters.
         /// </summary>
         /// <param name="key">Key to clear.</param>
         void Clear(TK key);
 
         /// <summary>
+        /// Clear entry from the cache, without notifying listeners or CacheWriters.
+        /// </summary>
+        /// <param name="key">Key to clear.</param>
+        Task ClearAsync(TK key);
+
+        /// <summary>
         /// Clear entries from the cache, without notifying listeners or CacheWriters.
         /// </summary>
         /// <param name="keys">Keys to clear.</param>
         void ClearAll(IEnumerable<TK> keys);
 
         /// <summary>
+        /// Clear entries from the cache, without notifying listeners or CacheWriters.
+        /// </summary>
+        /// <param name="keys">Keys to clear.</param>
+        Task ClearAllAsync(IEnumerable<TK> keys);
+
+        /// <summary>
         /// Removes given key mapping from cache, notifying listeners and cache writers.
         /// </summary>
         /// <param name="key">Key to remove.</param>
@@ -209,6 +345,13 @@ namespace Apache.Ignite.Core.Client.Cache
         bool Remove(TK key);
 
         /// <summary>
+        /// Removes given key mapping from cache, notifying listeners and cache writers.
+        /// </summary>
+        /// <param name="key">Key to remove.</param>
+        /// <returns>True if entry was removed, false otherwise.</returns>
+        Task<bool> RemoveAsync(TK key);
+
+        /// <summary>
         /// Removes given key mapping from cache if one exists and value is equal to the passed in value.
         /// </summary>
         /// <param name="key">Key whose mapping is to be removed from cache.</param>
@@ -217,17 +360,36 @@ namespace Apache.Ignite.Core.Client.Cache
         bool Remove(TK key, TV val);
 
         /// <summary>
+        /// Removes given key mapping from cache if one exists and value is equal to the passed in value.
+        /// </summary>
+        /// <param name="key">Key whose mapping is to be removed from cache.</param>
+        /// <param name="val">Value to match against currently cached value.</param>
+        /// <returns>True if entry was removed, false otherwise.</returns>
+        Task<bool> RemoveAsync(TK key, TV val);
+
+        /// <summary>
         /// Removes given key mappings from cache, notifying listeners and cache writers.
         /// </summary>
         /// <param name="keys">Keys to be removed from cache.</param>
         void RemoveAll(IEnumerable<TK> keys);
 
         /// <summary>
+        /// Removes given key mappings from cache, notifying listeners and cache writers.
+        /// </summary>
+        /// <param name="keys">Keys to be removed from cache.</param>
+        Task RemoveAllAsync(IEnumerable<TK> keys);
+
+        /// <summary>
         /// Removes all mappings from cache, notifying listeners and cache writers.
         /// </summary>
         void RemoveAll();
 
         /// <summary>
+        /// Removes all mappings from cache, notifying listeners and cache writers.
+        /// </summary>
+        Task RemoveAllAsync();
+
+        /// <summary>
         /// Gets the number of all entries cached across all nodes.
         /// <para />
         /// NOTE: this operation is distributed and will query all participating nodes for their cache sizes.
@@ -237,6 +399,15 @@ namespace Apache.Ignite.Core.Client.Cache
         long GetSize(params CachePeekMode[] modes);
 
         /// <summary>
+        /// Gets the number of all entries cached across all nodes.
+        /// <para />
+        /// NOTE: this operation is distributed and will query all participating nodes for their cache sizes.
+        /// </summary>
+        /// <param name="modes">Optional peek modes. If not provided, then total cache size is returned.</param>
+        /// <returns>Cache size across all nodes.</returns>
+        Task<long> GetSizeAsync(params CachePeekMode[] modes);
+
+        /// <summary>
         /// Gets the cache configuration.
         /// </summary>
         CacheClientConfiguration GetConfiguration();
index 58f12fe..2b24aa4 100644 (file)
@@ -27,6 +27,8 @@ namespace Apache.Ignite.Core.Client
     /// Main entry point for Ignite Thin Client APIs.
     /// You can obtain an instance of <see cref="IIgniteClient"/> through one of the
     /// <see cref="Ignition.StartClient()"/> overloads.
+    /// <para />
+    /// Instances of this class and all nested APIs are thread safe.
     /// </summary>
     public interface IIgniteClient : IDisposable
     {
index e46ede4..e20666f 100644 (file)
@@ -17,6 +17,7 @@
 
 namespace Apache.Ignite.Core.Client
 {
+    using System;
     using System.ComponentModel;
     using System.Xml;
     using Apache.Ignite.Core.Binary;
@@ -48,6 +49,11 @@ namespace Apache.Ignite.Core.Client
         public const bool DefaultTcpNoDelay = true;
 
         /// <summary>
+        /// Default socket timeout.
+        /// </summary>
+        public static readonly TimeSpan DefaultSocketTimeout = TimeSpan.FromMilliseconds(5000);
+
+        /// <summary>
         /// Initializes a new instance of the <see cref="IgniteClientConfiguration"/> class.
         /// </summary>
         public IgniteClientConfiguration()
@@ -56,6 +62,7 @@ namespace Apache.Ignite.Core.Client
             SocketSendBufferSize = DefaultSocketBufferSize;
             SocketReceiveBufferSize = DefaultSocketBufferSize;
             TcpNoDelay = DefaultTcpNoDelay;
+            SocketTimeout = DefaultSocketTimeout;
         }
 
         /// <summary>
@@ -74,6 +81,7 @@ namespace Apache.Ignite.Core.Client
             SocketSendBufferSize = cfg.SocketSendBufferSize;
             SocketReceiveBufferSize = cfg.SocketReceiveBufferSize;
             TcpNoDelay = cfg.TcpNoDelay;
+            SocketTimeout = cfg.SocketTimeout;
 
             if (cfg.BinaryConfiguration != null)
             {
@@ -107,6 +115,12 @@ namespace Apache.Ignite.Core.Client
         public int SocketReceiveBufferSize { get; set; }
 
         /// <summary>
+        /// Gets or sets the socket operation timeout. Zero or negative means infinite timeout.
+        /// </summary>
+        [DefaultValue(typeof(TimeSpan), "00:00:05")]
+        public TimeSpan SocketTimeout { get; set; }
+
+        /// <summary>
         /// Gets or sets the value for <c>TCP_NODELAY</c> socket option. Each
         /// socket will be opened using provided value.
         /// <para />
index e7a6889..f71ce0b 100644 (file)
                     <xs:documentation>Value for TCP_NODELAY socket option.</xs:documentation>
                 </xs:annotation>
             </xs:attribute>
+            <xs:attribute name="socketTimeout" type="xs:string">
+                <xs:annotation>
+                    <xs:documentation>Socket operation timeout. Zero or negative for infinite timeout.</xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
         </xs:complexType>
     </xs:element>
 </xs:schema>
\ No newline at end of file
index a6082f1..6bd03fd 100644 (file)
@@ -356,7 +356,7 @@ namespace Apache.Ignite.Core.Impl.Binary.IO
         /// </summary>
         /// <param name="data">Data pointer.</param>
         /// <returns>Int value</returns>
-        private static int ReadInt0(byte* data) {
+        public static int ReadInt0(byte* data) {
             int val;
 
             if (LittleEndian)
index 2344417..8138b77 100644 (file)
@@ -1,4 +1,4 @@
-/*
+/*
  * 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.
@@ -22,6 +22,7 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
     using System.Diagnostics;
     using System.Diagnostics.CodeAnalysis;
     using System.IO;
+    using System.Threading.Tasks;
     using Apache.Ignite.Core.Binary;
     using Apache.Ignite.Core.Cache;
     using Apache.Ignite.Core.Cache.Configuration;
@@ -99,6 +100,14 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
         }
 
         /** <inheritDoc /> */
+        public Task<TV> GetAsync(TK key)
+        {
+            IgniteArgumentCheck.NotNull(key, "key");
+
+            return DoOutInOpAsync(ClientOp.CacheGet, w => w.WriteObject(key), UnmarshalNotNull<TV>);
+        }
+
+        /** <inheritDoc /> */
         public bool TryGet(TK key, out TV value)
         {
             IgniteArgumentCheck.NotNull(key, "key");
@@ -111,24 +120,27 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
         }
 
         /** <inheritDoc /> */
+        public Task<CacheResult<TV>> TryGetAsync(TK key)
+        {
+            IgniteArgumentCheck.NotNull(key, "key");
+
+            return DoOutInOpAsync(ClientOp.CacheGet, w => w.WriteObject(key), UnmarshalCacheResult<TV>);
+        }
+
+        /** <inheritDoc /> */
         public ICollection<ICacheEntry<TK, TV>> GetAll(IEnumerable<TK> keys)
         {
             IgniteArgumentCheck.NotNull(keys, "keys");
 
-            return DoOutInOp(ClientOp.CacheGetAll, w => w.WriteEnumerable(keys), stream =>
-            {
-                var reader = _marsh.StartUnmarshal(stream, _keepBinary);
-
-                var cnt = reader.ReadInt();
-                var res = new List<ICacheEntry<TK, TV>>(cnt);
+            return DoOutInOp(ClientOp.CacheGetAll, w => w.WriteEnumerable(keys), s => ReadCacheEntries(s));
+        }
 
-                for (var i = 0; i < cnt; i++)
-                {
-                    res.Add(new CacheEntry<TK, TV>(reader.ReadObject<TK>(), reader.ReadObject<TV>()));
-                }
+        /** <inheritDoc /> */
+        public Task<ICollection<ICacheEntry<TK, TV>>> GetAllAsync(IEnumerable<TK> keys)
+        {
+            IgniteArgumentCheck.NotNull(keys, "keys");
 
-                return res;
-            });
+            return DoOutInOpAsync(ClientOp.CacheGetAll, w => w.WriteEnumerable(keys), s => ReadCacheEntries(s));
         }
 
         /** <inheritDoc /> */
@@ -137,11 +149,16 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
             IgniteArgumentCheck.NotNull(key, "key");
             IgniteArgumentCheck.NotNull(val, "val");
 
-            DoOutOp(ClientOp.CachePut, w =>
-            {
-                w.WriteObjectDetached(key);
-                w.WriteObjectDetached(val);
-            });
+            DoOutOp(ClientOp.CachePut, w => WriteKeyVal(w, key, val));
+        }
+
+        /** <inheritDoc /> */
+        public Task PutAsync(TK key, TV val)
+        {
+            IgniteArgumentCheck.NotNull(key, "key");
+            IgniteArgumentCheck.NotNull(val, "val");
+
+            return DoOutOpAsync(ClientOp.CachePut, w => WriteKeyVal(w, key, val));
         }
 
         /** <inheritDoc /> */
@@ -153,6 +170,14 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
         }
 
         /** <inheritDoc /> */
+        public Task<bool> ContainsKeyAsync(TK key)
+        {
+            IgniteArgumentCheck.NotNull(key, "key");
+
+            return DoOutInOpAsync(ClientOp.CacheContainsKey, w => w.WriteObjectDetached(key), r => r.ReadBool());
+        }
+
+        /** <inheritDoc /> */
         public bool ContainsKeys(IEnumerable<TK> keys)
         {
             IgniteArgumentCheck.NotNull(keys, "keys");
@@ -161,6 +186,14 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
         }
 
         /** <inheritDoc /> */
+        public Task<bool> ContainsKeysAsync(IEnumerable<TK> keys)
+        {
+            IgniteArgumentCheck.NotNull(keys, "keys");
+
+            return DoOutInOpAsync(ClientOp.CacheContainsKeys, w => w.WriteEnumerable(keys), r => r.ReadBool());
+        }
+
+        /** <inheritDoc /> */
         public IQueryCursor<ICacheEntry<TK, TV>> Query(ScanQuery<TK, TV> scanQuery)
         {
             IgniteArgumentCheck.NotNull(scanQuery, "scanQuery");
@@ -209,11 +242,16 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
             IgniteArgumentCheck.NotNull(key, "key");
             IgniteArgumentCheck.NotNull(val, "val");
 
-            return DoOutInOp(ClientOp.CacheGetAndPut, w =>
-            {
-                w.WriteObjectDetached(key);
-                w.WriteObjectDetached(val);
-            }, UnmarshalCacheResult<TV>);
+            return DoOutInOp(ClientOp.CacheGetAndPut, w => WriteKeyVal(w, key, val), UnmarshalCacheResult<TV>);
+        }
+
+        /** <inheritDoc /> */
+        public Task<CacheResult<TV>> GetAndPutAsync(TK key, TV val)
+        {
+            IgniteArgumentCheck.NotNull(key, "key");
+            IgniteArgumentCheck.NotNull(val, "val");
+
+            return DoOutInOpAsync(ClientOp.CacheGetAndPut, w => WriteKeyVal(w, key, val), UnmarshalCacheResult<TV>);
         }
 
         /** <inheritDoc /> */
@@ -222,11 +260,16 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
             IgniteArgumentCheck.NotNull(key, "key");
             IgniteArgumentCheck.NotNull(val, "val");
 
-            return DoOutInOp(ClientOp.CacheGetAndReplace, w =>
-            {
-                w.WriteObjectDetached(key);
-                w.WriteObjectDetached(val);
-            }, UnmarshalCacheResult<TV>);
+            return DoOutInOp(ClientOp.CacheGetAndReplace, w => WriteKeyVal(w, key, val), UnmarshalCacheResult<TV>);
+        }
+
+        /** <inheritDoc /> */
+        public Task<CacheResult<TV>> GetAndReplaceAsync(TK key, TV val)
+        {
+            IgniteArgumentCheck.NotNull(key, "key");
+            IgniteArgumentCheck.NotNull(val, "val");
+
+            return DoOutInOpAsync(ClientOp.CacheGetAndReplace, w => WriteKeyVal(w, key, val), UnmarshalCacheResult<TV>);
         }
 
         /** <inheritDoc /> */
@@ -239,16 +282,30 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
         }
 
         /** <inheritDoc /> */
+        public Task<CacheResult<TV>> GetAndRemoveAsync(TK key)
+        {
+            IgniteArgumentCheck.NotNull(key, "key");
+
+            return DoOutInOpAsync(ClientOp.CacheGetAndRemove, w => w.WriteObjectDetached(key),
+                UnmarshalCacheResult<TV>);
+        }
+
+        /** <inheritDoc /> */
         public bool PutIfAbsent(TK key, TV val)
         {
             IgniteArgumentCheck.NotNull(key, "key");
             IgniteArgumentCheck.NotNull(val, "val");
 
-            return DoOutInOp(ClientOp.CachePutIfAbsent, w =>
-            {
-                w.WriteObjectDetached(key);
-                w.WriteObjectDetached(val);
-            }, s => s.ReadBool());
+            return DoOutInOp(ClientOp.CachePutIfAbsent, w => WriteKeyVal(w, key, val), s => s.ReadBool());
+        }
+
+        /** <inheritDoc /> */
+        public Task<bool> PutIfAbsentAsync(TK key, TV val)
+        {
+            IgniteArgumentCheck.NotNull(key, "key");
+            IgniteArgumentCheck.NotNull(val, "val");
+
+            return DoOutInOpAsync(ClientOp.CachePutIfAbsent, w => WriteKeyVal(w, key, val), s => s.ReadBool());
         }
 
         /** <inheritDoc /> */
@@ -257,11 +314,18 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
             IgniteArgumentCheck.NotNull(key, "key");
             IgniteArgumentCheck.NotNull(val, "val");
 
-            return DoOutInOp(ClientOp.CacheGetAndPutIfAbsent, w =>
-            {
-                w.WriteObjectDetached(key);
-                w.WriteObjectDetached(val);
-            }, UnmarshalCacheResult<TV>);
+            return DoOutInOp(ClientOp.CacheGetAndPutIfAbsent, w => WriteKeyVal(w, key, val),
+                UnmarshalCacheResult<TV>);
+        }
+
+        /** <inheritDoc /> */
+        public Task<CacheResult<TV>> GetAndPutIfAbsentAsync(TK key, TV val)
+        {
+            IgniteArgumentCheck.NotNull(key, "key");
+            IgniteArgumentCheck.NotNull(val, "val");
+
+            return DoOutInOpAsync(ClientOp.CacheGetAndPutIfAbsent, w => WriteKeyVal(w, key, val),
+                UnmarshalCacheResult<TV>);
         }
 
         /** <inheritDoc /> */
@@ -270,21 +334,41 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
             IgniteArgumentCheck.NotNull(key, "key");
             IgniteArgumentCheck.NotNull(val, "val");
 
-            return DoOutInOp(ClientOp.CacheReplace, w =>
+            return DoOutInOp(ClientOp.CacheReplace, w => WriteKeyVal(w, key, val), s => s.ReadBool());
+        }
+
+        /** <inheritDoc /> */
+        public Task<bool> ReplaceAsync(TK key, TV val)
+        {
+            IgniteArgumentCheck.NotNull(key, "key");
+            IgniteArgumentCheck.NotNull(val, "val");
+
+            return DoOutInOpAsync(ClientOp.CacheReplace, w => WriteKeyVal(w, key, val), s => s.ReadBool());
+        }
+
+        /** <inheritDoc /> */
+        public bool Replace(TK key, TV oldVal, TV newVal)
+        {
+            IgniteArgumentCheck.NotNull(key, "key");
+            IgniteArgumentCheck.NotNull(oldVal, "oldVal");
+            IgniteArgumentCheck.NotNull(newVal, "newVal");
+
+            return DoOutInOp(ClientOp.CacheReplaceIfEquals, w =>
             {
                 w.WriteObjectDetached(key);
-                w.WriteObjectDetached(val);
+                w.WriteObjectDetached(oldVal);
+                w.WriteObjectDetached(newVal);
             }, s => s.ReadBool());
         }
 
         /** <inheritDoc /> */
-        public bool Replace(TK key, TV oldVal, TV newVal)
+        public Task<bool> ReplaceAsync(TK key, TV oldVal, TV newVal)
         {
             IgniteArgumentCheck.NotNull(key, "key");
             IgniteArgumentCheck.NotNull(oldVal, "oldVal");
             IgniteArgumentCheck.NotNull(newVal, "newVal");
 
-            return DoOutInOp(ClientOp.CacheReplaceIfEquals, w =>
+            return DoOutInOpAsync(ClientOp.CacheReplaceIfEquals, w =>
             {
                 w.WriteObjectDetached(key);
                 w.WriteObjectDetached(oldVal);
@@ -301,12 +385,26 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
         }
 
         /** <inheritDoc /> */
+        public Task PutAllAsync(IEnumerable<KeyValuePair<TK, TV>> vals)
+        {
+            IgniteArgumentCheck.NotNull(vals, "vals");
+
+            return DoOutOpAsync(ClientOp.CachePutAll, w => w.WriteDictionary(vals));
+        }
+
+        /** <inheritDoc /> */
         public void Clear()
         {
             DoOutOp(ClientOp.CacheClear);
         }
 
         /** <inheritDoc /> */
+        public Task ClearAsync()
+        {
+            return DoOutOpAsync(ClientOp.CacheClear);
+        }
+
+        /** <inheritDoc /> */
         public void Clear(TK key)
         {
             IgniteArgumentCheck.NotNull(key, "key");
@@ -315,6 +413,14 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
         }
 
         /** <inheritDoc /> */
+        public Task ClearAsync(TK key)
+        {
+            IgniteArgumentCheck.NotNull(key, "key");
+
+            return DoOutOpAsync(ClientOp.CacheClearKey, w => w.WriteObjectDetached(key));
+        }
+
+        /** <inheritDoc /> */
         public void ClearAll(IEnumerable<TK> keys)
         {
             IgniteArgumentCheck.NotNull(keys, "keys");
@@ -323,6 +429,14 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
         }
 
         /** <inheritDoc /> */
+        public Task ClearAllAsync(IEnumerable<TK> keys)
+        {
+            IgniteArgumentCheck.NotNull(keys, "keys");
+
+            return DoOutOpAsync(ClientOp.CacheClearKeys, w => w.WriteEnumerable(keys));
+        }
+
+        /** <inheritDoc /> */
         public bool Remove(TK key)
         {
             IgniteArgumentCheck.NotNull(key, "key");
@@ -331,16 +445,29 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
         }
 
         /** <inheritDoc /> */
+        public Task<bool> RemoveAsync(TK key)
+        {
+            IgniteArgumentCheck.NotNull(key, "key");
+
+            return DoOutInOpAsync(ClientOp.CacheRemoveKey, w => w.WriteObjectDetached(key), r => r.ReadBool());
+        }
+
+        /** <inheritDoc /> */
         public bool Remove(TK key, TV val)
         {
             IgniteArgumentCheck.NotNull(key, "key");
             IgniteArgumentCheck.NotNull(val, "val");
 
-            return DoOutInOp(ClientOp.CacheRemoveIfEquals, w =>
-            {
-                w.WriteObjectDetached(key);
-                w.WriteObjectDetached(val);
-            }, r => r.ReadBool());
+            return DoOutInOp(ClientOp.CacheRemoveIfEquals, w => WriteKeyVal(w, key, val), r => r.ReadBool());
+        }
+
+        /** <inheritDoc /> */
+        public Task<bool> RemoveAsync(TK key, TV val)
+        {
+            IgniteArgumentCheck.NotNull(key, "key");
+            IgniteArgumentCheck.NotNull(val, "val");
+
+            return DoOutInOpAsync(ClientOp.CacheRemoveIfEquals, w => WriteKeyVal(w, key, val), r => r.ReadBool());
         }
 
         /** <inheritDoc /> */
@@ -352,18 +479,38 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
         }
 
         /** <inheritDoc /> */
+        public Task RemoveAllAsync(IEnumerable<TK> keys)
+        {
+            IgniteArgumentCheck.NotNull(keys, "keys");
+
+            return DoOutOpAsync(ClientOp.CacheRemoveKeys, w => w.WriteEnumerable(keys));
+        }
+
+        /** <inheritDoc /> */
         public void RemoveAll()
         {
             DoOutOp(ClientOp.CacheRemoveAll);
         }
 
         /** <inheritDoc /> */
+        public Task RemoveAllAsync()
+        {
+            return DoOutOpAsync(ClientOp.CacheRemoveAll);
+        }
+
+        /** <inheritDoc /> */
         public long GetSize(params CachePeekMode[] modes)
         {
             return DoOutInOp(ClientOp.CacheGetSize, w => WritePeekModes(modes, w), s => s.ReadLong());
         }
 
         /** <inheritDoc /> */
+        public Task<long> GetSizeAsync(params CachePeekMode[] modes)
+        {
+            return DoOutInOpAsync(ClientOp.CacheGetSize, w => WritePeekModes(modes, w), s => s.ReadLong());
+        }
+
+        /** <inheritDoc /> */
         public CacheClientConfiguration GetConfiguration()
         {
             return DoOutInOp(ClientOp.CacheGetConfiguration, null, s => new CacheClientConfiguration(s));
@@ -405,33 +552,57 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
         }
 
         /// <summary>
+        /// Does the out op.
+        /// </summary>
+        private void DoOutOp(ClientOp opId, Action<BinaryWriter> writeAction = null)
+        {
+            DoOutInOp<object>(opId, writeAction, null);
+        }
+
+        /// <summary>
+        /// Does the out op.
+        /// </summary>
+        private Task DoOutOpAsync(ClientOp opId, Action<BinaryWriter> writeAction = null)
+        {
+            return DoOutInOpAsync<object>(opId, writeAction, null);
+        }
+
+        /// <summary>
         /// Does the out in op.
         /// </summary>
         private T DoOutInOp<T>(ClientOp opId, Action<BinaryWriter> writeAction,
             Func<IBinaryStream, T> readFunc)
         {
-            return _ignite.Socket.DoOutInOp(opId, stream =>
-            {
-                stream.WriteInt(_id);
-                stream.WriteByte(0);  // Flags (skipStore, etc).
-
-                if (writeAction != null)
-                {
-                    var writer = _marsh.StartMarshal(stream);
-
-                    writeAction(writer);
+            return _ignite.Socket.DoOutInOp(opId, stream => WriteRequest(writeAction, stream), 
+                readFunc, HandleError<T>);
+        }
 
-                    _marsh.FinishMarshal(writer);
-                }
-            }, readFunc, HandleError<T>);
+        /// <summary>
+        /// Does the out in op.
+        /// </summary>
+        private Task<T> DoOutInOpAsync<T>(ClientOp opId, Action<BinaryWriter> writeAction,
+            Func<IBinaryStream, T> readFunc)
+        {
+            return _ignite.Socket.DoOutInOpAsync(opId, stream => WriteRequest(writeAction, stream), 
+                readFunc, HandleError<T>);
         }
 
         /// <summary>
-        /// Does the out op.
+        /// Writes the request.
         /// </summary>
-        private void DoOutOp(ClientOp opId, Action<BinaryWriter> writeAction = null)
+        private void WriteRequest(Action<BinaryWriter> writeAction, IBinaryStream stream)
         {
-            DoOutInOp<object>(opId, writeAction, null);
+            stream.WriteInt(_id);
+            stream.WriteByte(0); // Flags (skipStore, etc).
+
+            if (writeAction != null)
+            {
+                var writer = _marsh.StartMarshal(stream);
+
+                writeAction(writer);
+
+                _marsh.FinishMarshal(writer);
+            }
         }
 
         /// <summary>
@@ -618,5 +789,32 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
                 }
             }
         }
+
+        /// <summary>
+        /// Reads the cache entries.
+        /// </summary>
+        private ICollection<ICacheEntry<TK, TV>> ReadCacheEntries(IBinaryStream stream)
+        {
+            var reader = _marsh.StartUnmarshal(stream, _keepBinary);
+
+            var cnt = reader.ReadInt();
+            var res = new List<ICacheEntry<TK, TV>>(cnt);
+
+            for (var i = 0; i < cnt; i++)
+            {
+                res.Add(new CacheEntry<TK, TV>(reader.ReadObject<TK>(), reader.ReadObject<TV>()));
+            }
+
+            return res;
+        }
+
+        /// <summary>
+        /// Writes key and value.
+        /// </summary>
+        private static void WriteKeyVal(BinaryWriter w, TK key, TV val)
+        {
+            w.WriteObjectDetached(key);
+            w.WriteObjectDetached(val);
+        }
     }
 }
index b8218c1..8e19df5 100644 (file)
@@ -18,6 +18,7 @@
 namespace Apache.Ignite.Core.Impl.Client
 {
     using System;
+    using System.Collections.Concurrent;
     using System.Collections.Generic;
     using System.Diagnostics;
     using System.Diagnostics.CodeAnalysis;
@@ -25,6 +26,7 @@ namespace Apache.Ignite.Core.Impl.Client
     using System.Net;
     using System.Net.Sockets;
     using System.Threading;
+    using System.Threading.Tasks;
     using Apache.Ignite.Core.Client;
     using Apache.Ignite.Core.Common;
     using Apache.Ignite.Core.Impl.Binary;
@@ -33,7 +35,7 @@ namespace Apache.Ignite.Core.Impl.Client
     /// <summary>
     /// Wrapper over framework socket for Ignite thin client operations.
     /// </summary>
-    internal class ClientSocket : IDisposable
+    internal sealed class ClientSocket : IDisposable
     {
         /** Current version. */
         private static readonly ClientProtocolVersion CurrentProtocolVersion = new ClientProtocolVersion(1, 0, 0);
@@ -44,12 +46,41 @@ namespace Apache.Ignite.Core.Impl.Client
         /** Client type code. */
         private const byte ClientType = 2;
 
-        /** Unerlying socket. */
+        /** Underlying socket. */
         private readonly Socket _socket;
 
-        /** */
+        /** Operation timeout. */
+        private readonly TimeSpan _timeout;
+
+        /** Request timeout checker. */
+        private readonly Timer _timeoutCheckTimer;
+
+        /** Callback checker guard. */
+        private volatile bool _checkingTimeouts;
+
+        /** Current async operations, map from request id. */
+        private readonly ConcurrentDictionary<long, Request> _requests
+            = new ConcurrentDictionary<long, Request>();
+
+        /** Request id generator. */
         private long _requestId;
 
+        /** Socket failure exception. */
+        private volatile Exception _exception;
+
+        /** Locker. */
+        private readonly ReaderWriterLockSlim _sendRequestLock = 
+            new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
+
+        /** Background socket receiver trigger. */
+        private readonly ManualResetEventSlim _listenerEvent = new ManualResetEventSlim();
+
+        /** Dispose locker. */
+        private readonly object _disposeSyncRoot = new object();
+
+        /** Disposed flag. */
+        private bool _isDisposed;
+
         /// <summary>
         /// Initializes a new instance of the <see cref="ClientSocket" /> class.
         /// </summary>
@@ -59,9 +90,21 @@ namespace Apache.Ignite.Core.Impl.Client
         {
             Debug.Assert(clientConfiguration != null);
 
+            _timeout = clientConfiguration.SocketTimeout;
+
             _socket = Connect(clientConfiguration);
 
-            Handshake(_socket, version ?? CurrentProtocolVersion);
+            Handshake(version ?? CurrentProtocolVersion);
+
+            // Check periodically if any request has timed out.
+            if (_timeout > TimeSpan.Zero)
+            {
+                // Minimum Socket timeout is 500ms.
+                _timeoutCheckTimer = new Timer(CheckTimeouts, null, _timeout, TimeSpan.FromMilliseconds(500));
+            }
+
+            // Continuously and asynchronously wait for data from server.
+            Task.Factory.StartNew(WaitForMessages);
         }
 
         /// <summary>
@@ -70,48 +113,120 @@ namespace Apache.Ignite.Core.Impl.Client
         public T DoOutInOp<T>(ClientOp opId, Action<IBinaryStream> writeAction,
             Func<IBinaryStream, T> readFunc, Func<ClientStatusCode, string, T> errorFunc = null)
         {
-            var requestId = Interlocked.Increment(ref _requestId);
+            // Encode.
+            var reqMsg = WriteMessage(writeAction, opId);
+            
+            // Send.
+            var response = SendRequest(ref reqMsg);
+
+            // Decode.
+            return DecodeResponse(response, readFunc, errorFunc);
+        }
 
-            var resBytes = SendReceive(_socket, stream =>
-            {
-                stream.WriteShort((short) opId);
-                stream.WriteLong(requestId);
+        /// <summary>
+        /// Performs a send-receive operation asynchronously.
+        /// </summary>
+        public Task<T> DoOutInOpAsync<T>(ClientOp opId, Action<IBinaryStream> writeAction,
+            Func<IBinaryStream, T> readFunc, Func<ClientStatusCode, string, T> errorFunc = null)
+        {
+            // Encode.
+            var reqMsg = WriteMessage(writeAction, opId);
+
+            // Send.
+            var task = SendRequestAsync(ref reqMsg);
+
+            // Decode.
+            return task.ContinueWith(responseTask => DecodeResponse(responseTask.Result, readFunc, errorFunc));
+        }
 
-                if (writeAction != null)
+        /// <summary>
+        /// Starts waiting for the new message.
+        /// </summary>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        private void WaitForMessages()
+        {
+            try
+            {
+                // Null exception means active socket.
+                while (_exception == null)
                 {
-                    writeAction(stream);
+                    // Do not call Receive if there are no async requests pending.
+                    while (_requests.IsEmpty)
+                    {
+                        // Wait with a timeout so we check for disposed state periodically.
+                        _listenerEvent.Wait(1000);
+
+                        if (_exception != null)
+                        {
+                            return;
+                        }
+
+                        _listenerEvent.Reset();
+                    }
+
+                    var msg = ReceiveMessage();
+                    HandleResponse(msg);
                 }
-            });
+            }
+            catch (Exception ex)
+            {
+                // Socket failure (connection dropped, etc).
+                // Close socket and all pending requests.
+                // Note that this does not include request decoding exceptions (failed request, invalid data, etc).
+                _exception = ex;
+                Dispose();
+            }
+        }
+
+        /// <summary>
+        /// Handles the response.
+        /// </summary>
+        private void HandleResponse(byte[] response)
+        {
+            var stream = new BinaryHeapStream(response);
+            var requestId = stream.ReadLong();
 
-            using (var stream = new BinaryHeapStream(resBytes))
+            Request req;
+            if (!_requests.TryRemove(requestId, out req))
             {
-                var resRequestId = stream.ReadLong();
-                Debug.Assert(requestId == resRequestId);
+                // Response with unknown id.
+                throw new IgniteClientException("Invalid thin client response id: " + requestId);
+            }
 
-                var statusCode = (ClientStatusCode) stream.ReadInt();
+            req.CompletionSource.TrySetResult(stream);
+        }
 
-                if (statusCode == ClientStatusCode.Success)
-                {
-                    return readFunc != null ? readFunc(stream) : default(T);
-                }
+        /// <summary>
+        /// Decodes the response that we got from <see cref="HandleResponse"/>.
+        /// </summary>
+        private static T DecodeResponse<T>(BinaryHeapStream stream, Func<IBinaryStream, T> readFunc, 
+            Func<ClientStatusCode, string, T> errorFunc)
+        {
+            var statusCode = (ClientStatusCode)stream.ReadInt();
 
-                var msg = BinaryUtils.Marshaller.StartUnmarshal(stream).ReadString();
+            if (statusCode == ClientStatusCode.Success)
+            {
+                return readFunc != null ? readFunc(stream) : default(T);
+            }
 
-                if (errorFunc != null)
-                {
-                    return errorFunc(statusCode, msg);
-                }
+            var msg = BinaryUtils.Marshaller.StartUnmarshal(stream).ReadString();
 
-                throw new IgniteClientException(msg, null, statusCode);
+            if (errorFunc != null)
+            {
+                return errorFunc(statusCode, msg);
             }
+
+            throw new IgniteClientException(msg, null, statusCode);
         }
 
         /// <summary>
         /// Performs client protocol handshake.
         /// </summary>
-        private static void Handshake(Socket sock, ClientProtocolVersion version)
+        private void Handshake(ClientProtocolVersion version)
         {
-            var res = SendReceive(sock, stream =>
+            // Send request.
+            int messageLen;
+            var buf = WriteMessage(stream =>
             {
                 // Handshake.
                 stream.WriteByte(OpHandshake);
@@ -123,7 +238,15 @@ namespace Apache.Ignite.Core.Impl.Client
 
                 // Client type: platform.
                 stream.WriteByte(ClientType);
-            }, 20);
+            }, 12, out messageLen);
+
+            Debug.Assert(messageLen == 12);
+
+            var sent = _socket.Send(buf, messageLen, SocketFlags.None);
+            Debug.Assert(sent == messageLen);
+
+            // Decode response.
+            var res = ReceiveMessage();
 
             using (var stream = new BinaryHeapStream(res))
             {
@@ -140,43 +263,119 @@ namespace Apache.Ignite.Core.Impl.Client
                 var errMsg = BinaryUtils.Marshaller.Unmarshal<string>(stream);
 
                 throw new IgniteClientException(string.Format(
-                    "Client handhsake failed: '{0}'. Client version: {1}. Server version: {2}",
+                    "Client handshake failed: '{0}'. Client version: {1}. Server version: {2}",
                     errMsg, version, serverVersion));
             }
         }
 
         /// <summary>
-        /// Sends the request and receives a response.
+        /// Receives a message from socket.
         /// </summary>
-        private static byte[] SendReceive(Socket sock, Action<IBinaryStream> writeAction, int bufSize = 128)
+        private byte[] ReceiveMessage()
         {
-            int messageLen;
-            var buf = WriteMessage(writeAction, bufSize, out messageLen);
+            var size = GetInt(ReceiveBytes(4));
+            var msg = ReceiveBytes(size);
+            return msg;
+        }
 
-            lock (sock)
+        /// <summary>
+        /// Receives the data filling provided buffer entirely.
+        /// </summary>
+        private byte[] ReceiveBytes(int size)
+        {
+            Debug.Assert(size > 0);
+
+            // Socket.Receive can return any number of bytes, even 1.
+            // We should repeat Receive calls until required amount of data has been received.
+            var buf = new byte[size];
+            var received = _socket.Receive(buf);
+
+            while (received < size)
             {
-                var sent = sock.Send(buf, messageLen, SocketFlags.None);
-                Debug.Assert(sent == messageLen);
+                var res = _socket.Receive(buf, received, size - received, SocketFlags.None);
+
+                if (res == 0)
+                {
+                    // Disconnected.
+                    _exception = _exception ?? new SocketException((int) SocketError.ConnectionAborted);
+                    Dispose();
+                    CheckException();
+                }
+
+                received += res;
+            }
 
-                buf = new byte[4];
-                var received = sock.Receive(buf);
-                Debug.Assert(received == buf.Length);
+            return buf;
+        }
 
-                using (var stream = new BinaryHeapStream(buf))
+        /// <summary>
+        /// Sends the request synchronously.
+        /// </summary>
+        private BinaryHeapStream SendRequest(ref RequestMessage reqMsg)
+        {
+            // Do not enter lock when disposed.
+            CheckException();
+
+            // If there are no pending async requests, we can execute this operation synchronously,
+            // which is more efficient.
+            if (_sendRequestLock.TryEnterWriteLock(0))
+            {
+                try
                 {
-                    var size = stream.ReadInt();
-                    
-                    buf = new byte[size];
-                    received = sock.Receive(buf);
+                    CheckException();
 
-                    while (received < size)
+                    if (_requests.IsEmpty)
                     {
-                        received += sock.Receive(buf, received, size - received, SocketFlags.None);
-                    }
+                        _socket.Send(reqMsg.Buffer, 0, reqMsg.Length, SocketFlags.None);
 
-                    return buf;
+                        var respMsg = ReceiveMessage();
+                        var response = new BinaryHeapStream(respMsg);
+                        var responseId = response.ReadLong();
+                        Debug.Assert(responseId == reqMsg.Id);
+
+                        return response;
+                    }
+                }
+                finally
+                {
+                    if (_sendRequestLock.IsWriteLockHeld)
+                    {
+                        _sendRequestLock.ExitWriteLock();
+                    }
                 }
             }
+
+            // Fallback to async mechanism.
+            return SendRequestAsync(ref reqMsg).Result;
+        }
+
+        /// <summary>
+        /// Sends the request asynchronously and returns a task for corresponding response.
+        /// </summary>
+        private Task<BinaryHeapStream> SendRequestAsync(ref RequestMessage reqMsg)
+        {
+            // Do not enter lock when disposed.
+            CheckException();
+
+            _sendRequestLock.EnterReadLock();
+            try
+            {
+                CheckException();
+
+                // Register.
+                var req = new Request();
+                var added = _requests.TryAdd(reqMsg.Id, req);
+                Debug.Assert(added);
+
+                // Send.
+                _socket.Send(reqMsg.Buffer, 0, reqMsg.Length, SocketFlags.None);
+                _listenerEvent.Set();
+                return req.CompletionSource.Task;
+            }
+            finally
+            {
+                _sendRequestLock.ExitReadLock();
+            }
         }
 
         /// <summary>
@@ -184,18 +383,31 @@ namespace Apache.Ignite.Core.Impl.Client
         /// </summary>
         private static byte[] WriteMessage(Action<IBinaryStream> writeAction, int bufSize, out int messageLen)
         {
-            using (var stream = new BinaryHeapStream(bufSize))
-            {
-                stream.WriteInt(0); // Reserve message size.
+            var stream = new BinaryHeapStream(bufSize);
 
-                writeAction(stream);
+            stream.WriteInt(0); // Reserve message size.
+            writeAction(stream);
+            stream.WriteInt(0, stream.Position - 4); // Write message size.
 
-                stream.WriteInt(0, stream.Position - 4); // Write message size.
+            messageLen = stream.Position;
+            return stream.GetArray();
+        }
+
+        /// <summary>
+        /// Writes the message to a byte array.
+        /// </summary>
+        private RequestMessage WriteMessage(Action<IBinaryStream> writeAction, ClientOp opId)
+        {
+            var requestId = Interlocked.Increment(ref _requestId);
+            var stream = new BinaryHeapStream(256);
 
-                messageLen = stream.Position;
+            stream.WriteInt(0); // Reserve message size.
+            stream.WriteShort((short) opId);
+            stream.WriteLong(requestId);
+            writeAction(stream);
+            stream.WriteInt(0, stream.Position - 4); // Write message size.
 
-                return stream.GetArray();
-            }
+            return new RequestMessage(requestId, stream.GetArray(), stream.Position);
         }
 
         /// <summary>
@@ -213,7 +425,10 @@ namespace Apache.Ignite.Core.Impl.Client
                 {
                     var socket = new Socket(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
                     {
-                        NoDelay = cfg.TcpNoDelay
+                        NoDelay = cfg.TcpNoDelay,
+                        Blocking = true,
+                        SendTimeout = (int) cfg.SocketTimeout.TotalMilliseconds,
+                        ReceiveTimeout = (int) cfg.SocketTimeout.TotalMilliseconds
                     };
 
                     if (cfg.SocketSendBufferSize != IgniteClientConfiguration.DefaultSocketBufferSize)
@@ -274,13 +489,181 @@ namespace Apache.Ignite.Core.Impl.Client
         }
 
         /// <summary>
+        /// Checks if any of the current requests timed out.
+        /// </summary>
+        private void CheckTimeouts(object _)
+        {
+            if (_checkingTimeouts)
+            {
+                return;
+            }
+
+            _checkingTimeouts = true;
+
+            try
+            {
+                if (_exception != null)
+                {
+                    _timeoutCheckTimer.Dispose();
+                }
+
+                foreach (var pair in _requests)
+                {
+                    var req = pair.Value;
+
+                    if (req.Duration > _timeout)
+                    {
+                        Console.WriteLine(req.Duration);
+                        req.CompletionSource.TrySetException(new SocketException((int)SocketError.TimedOut));
+
+                        _requests.TryRemove(pair.Key, out req);
+                    }
+                }
+            }
+            finally
+            {
+                _checkingTimeouts = false;
+            }
+        }
+
+        /// <summary>
+        /// Gets the int from buffer.
+        /// </summary>
+        private static unsafe int GetInt(byte[] buf)
+        {
+            fixed (byte* b = buf)
+            {
+                return BinaryHeapStream.ReadInt0(b);
+            }
+        }
+
+        /// <summary>
+        /// Checks the exception.
+        /// </summary>
+        private void CheckException()
+        {
+            var ex = _exception;
+
+            if (ex != null)
+            {
+                throw ex;
+            }
+        }
+
+        /// <summary>
+        /// Closes the socket and completes all pending requests with an error.
+        /// </summary>
+        private void EndRequestsWithError()
+        {
+            var ex = _exception;
+            Debug.Assert(ex != null);
+
+            while (!_requests.IsEmpty)
+            {
+                foreach (var reqId in _requests.Keys.ToArray())
+                {
+                    Request req;
+                    if (_requests.TryRemove(reqId, out req))
+                    {
+                        req.CompletionSource.TrySetException(ex);
+                    }
+                }
+            }
+        }
+
+        /// <summary>
         /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
         /// </summary>
         [SuppressMessage("Microsoft.Usage", "CA1816:CallGCSuppressFinalizeCorrectly",
             Justification = "There is no finalizer.")]
         public void Dispose()
         {
-            _socket.Dispose();
+            lock (_disposeSyncRoot)
+            {
+                if (_isDisposed)
+                {
+                    return;
+                }
+
+                _exception = _exception ?? new ObjectDisposedException(typeof(ClientSocket).FullName);
+                EndRequestsWithError();
+                _socket.Dispose();
+                _listenerEvent.Set();
+                _listenerEvent.Dispose();
+                _timeoutCheckTimer.Dispose();
+
+                // Wait for lock to be released and dispose.
+                if (!_sendRequestLock.IsWriteLockHeld)
+                {
+                    _sendRequestLock.EnterWriteLock();
+                }
+                _sendRequestLock.ExitWriteLock();
+                _sendRequestLock.Dispose();
+
+                _isDisposed = true;
+            }
+        }
+
+        /// <summary>
+        /// Represents a request.
+        /// </summary>
+        private class Request
+        {
+            /** */
+            private readonly TaskCompletionSource<BinaryHeapStream> _completionSource;
+
+            /** */
+            private readonly DateTime _startTime;
+
+            /// <summary>
+            /// Initializes a new instance of the <see cref="Request"/> class.
+            /// </summary>
+            public Request()
+            {
+                _completionSource = new TaskCompletionSource<BinaryHeapStream>();
+                _startTime = DateTime.Now;
+            }
+
+            /// <summary>
+            /// Gets the completion source.
+            /// </summary>
+            public TaskCompletionSource<BinaryHeapStream> CompletionSource
+            {
+                get { return _completionSource; }
+            }
+
+            /// <summary>
+            /// Gets the duration.
+            /// </summary>
+            public TimeSpan Duration
+            {
+                get { return DateTime.Now - _startTime; }
+            }
+        }
+
+        /// <summary>
+        /// Represents a request message.
+        /// </summary>
+        private struct RequestMessage
+        {
+            /** */
+            public readonly long Id;
+
+            /** */
+            public readonly byte[] Buffer;
+
+            /** */
+            public readonly int Length;
+
+            /// <summary>
+            /// Initializes a new instance of the <see cref="RequestMessage"/> struct.
+            /// </summary>
+            public RequestMessage(long id, byte[] buffer, int length)
+            {
+                Id = id;
+                Length = length;
+                Buffer = buffer;
+            }
         }
     }
 }