You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ak...@apache.org on 2015/09/05 04:31:57 UTC

[14/45] ignite git commit: IGNITE-1348: Moved GridGain's .Net module to Ignite.

http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Cache/CacheAbstractTest.cs
----------------------------------------------------------------------
diff --git a/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Cache/CacheAbstractTest.cs b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Cache/CacheAbstractTest.cs
new file mode 100644
index 0000000..24d2b73
--- /dev/null
+++ b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Cache/CacheAbstractTest.cs
@@ -0,0 +1,3321 @@
+/*
+ * 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.Cache
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Diagnostics.CodeAnalysis;
+    using System.Linq;
+    using System.Runtime.Serialization;
+    using System.Text;
+    using System.Threading;
+    using System.Threading.Tasks;
+    using Apache.Ignite.Core.Cache;
+    using Apache.Ignite.Core.Cache.Expiry;
+    using Apache.Ignite.Core.Cluster;
+    using Apache.Ignite.Core.Common;
+    using Apache.Ignite.Core.Impl;
+    using Apache.Ignite.Core.Impl.Cache;
+    using Apache.Ignite.Core.Portable;
+    using Apache.Ignite.Core.Tests.Query;
+    using Apache.Ignite.Core.Transactions;
+    using NUnit.Framework;
+
+    /// <summary>
+    ///
+    /// </summary>
+    class CacheTestKey
+    {
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        public CacheTestKey()
+        {
+            // No-op.
+        }
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        /// <param name="id">ID.</param>
+        public CacheTestKey(int id)
+        {
+            Id = id;
+        }
+
+        /// <summary>
+        /// ID.
+        /// </summary>
+        public int Id
+        {
+            get;
+            set;
+        }
+
+        /** <inheritdoc /> */
+        public override bool Equals(object obj)
+        {
+            CacheTestKey other = obj as CacheTestKey;
+
+            return other != null && Id == other.Id;
+        }
+
+        /** <inheritdoc /> */
+        public override int GetHashCode()
+        {
+            return Id;
+        }
+
+        /** <inheritdoc /> */
+        public override string ToString()
+        {
+            return new StringBuilder()
+                .Append(typeof(CacheTestKey).Name)
+                .Append(" [id=").Append(Id)
+                .Append(']').ToString();
+        }
+    }
+
+    /// <summary>
+    ///
+    /// </summary>
+    class TestReferenceObject
+    {
+        public TestReferenceObject Obj;
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        public TestReferenceObject()
+        {
+            // No-op.
+        }
+
+        public TestReferenceObject(TestReferenceObject obj)
+        {
+            Obj = obj;
+        }
+    }
+
+    [Serializable]
+    public class TestSerializableObject
+    {
+        public string Name { get; set; }
+        public int Id { get; set; }
+
+        public override bool Equals(object obj)
+        {
+            if (ReferenceEquals(null, obj)) return false;
+            if (ReferenceEquals(this, obj)) return true;
+
+            var other = (TestSerializableObject) obj;
+            return obj.GetType() == GetType() && (string.Equals(Name, other.Name) && Id == other.Id);
+        }
+
+        public override int GetHashCode()
+        {
+            unchecked
+            {
+                return ((Name != null ? Name.GetHashCode() : 0) * 397) ^ Id;
+            }
+        }
+    }
+
+    /// <summary>
+    /// Cache entry processor that adds argument value to the entry value.
+    /// </summary>
+    [Serializable]
+    public class AddArgCacheEntryProcessor : ICacheEntryProcessor<int, int, int, int>
+    {
+        // Expected exception text
+        public const string ExceptionText = "Exception from AddArgCacheEntryProcessor.";
+
+        // Error flag
+        public bool ThrowErr { get; set; }
+
+        // Error flag
+        public bool ThrowErrPortable { get; set; }
+
+        // Error flag
+        public bool ThrowErrNonSerializable { get; set; }
+
+        // Key value to throw error on
+        public int ThrowOnKey { get; set; }
+
+        // Remove flag
+        public bool Remove { get; set; }
+
+        // Exists flag
+        public bool Exists { get; set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AddArgCacheEntryProcessor"/> class.
+        /// </summary>
+        public AddArgCacheEntryProcessor()
+        {
+            Exists = true;
+            ThrowOnKey = -1;
+        }
+
+        /** <inheritdoc /> */
+        int ICacheEntryProcessor<int, int, int, int>.Process(IMutableCacheEntry<int, int> entry, int arg)
+        {
+            if (ThrowOnKey < 0 || ThrowOnKey == entry.Key)
+            {
+                if (ThrowErr)
+                    throw new Exception(ExceptionText);
+
+                if (ThrowErrPortable)
+                    throw new PortableTestException {Info = ExceptionText};
+
+                if (ThrowErrNonSerializable)
+                    throw new NonSerializableException();
+            }
+
+            Assert.AreEqual(Exists, entry.Exists);
+
+            if (Remove)
+                entry.Remove();
+            else
+                entry.Value = entry.Value + arg;
+            
+            return entry.Value;
+        }
+
+        /** <inheritdoc /> */
+        public int Process(IMutableCacheEntry<int, int> entry, int arg)
+        {
+            throw new Exception("Invalid method");
+        }
+    }
+
+    /// <summary>
+    /// Portable add processor.
+    /// </summary>
+    public class PortableAddArgCacheEntryProcessor : AddArgCacheEntryProcessor, IPortableMarshalAware
+    {
+        /** <inheritdoc /> */
+        public void WritePortable(IPortableWriter writer)
+        {
+            var w = writer.RawWriter();
+
+            w.WriteBoolean(ThrowErr);
+            w.WriteBoolean(ThrowErrPortable);
+            w.WriteBoolean(ThrowErrNonSerializable);
+            w.WriteInt(ThrowOnKey);
+            w.WriteBoolean(Remove);
+            w.WriteBoolean(Exists);
+        }
+
+        /** <inheritdoc /> */
+        public void ReadPortable(IPortableReader reader)
+        {
+            var r = reader.RawReader();
+
+            ThrowErr = r.ReadBoolean();
+            ThrowErrPortable = r.ReadBoolean();
+            ThrowErrNonSerializable = r.ReadBoolean();
+            ThrowOnKey = r.ReadInt();
+            Remove = r.ReadBoolean();
+            Exists = r.ReadBoolean();
+        }
+    }
+
+    /// <summary>
+    /// Non-serializable processor.
+    /// </summary>
+    public class NonSerializableCacheEntryProcessor : AddArgCacheEntryProcessor
+    {
+        // No-op.
+    }
+
+    /// <summary>
+    /// Portable exception.
+    /// </summary>
+    public class PortableTestException : Exception, IPortableMarshalAware
+    {
+        /// <summary>
+        /// Gets or sets exception info.
+        /// </summary>
+        public string Info { get; set; }
+
+        /** <inheritdoc /> */
+        public override string Message
+        {
+            get { return Info; }
+        }
+
+        /** <inheritdoc /> */
+        public void WritePortable(IPortableWriter writer)
+        {
+            writer.RawWriter().WriteString(Info);
+        }
+
+        /** <inheritdoc /> */
+        public void ReadPortable(IPortableReader reader)
+        {
+            Info = reader.RawReader().ReadString();
+        }
+    }
+
+    /// <summary>
+    /// Non-serializable exception.
+    /// </summary>
+    public class NonSerializableException : Exception
+    {
+        // No-op
+    }
+
+    /// <summary>
+    ///
+    /// </summary>
+    [SuppressMessage("ReSharper", "UnusedVariable")]
+    public abstract class CacheAbstractTest {
+        /// <summary>
+        ///
+        /// </summary>
+        [TestFixtureSetUp]
+        public virtual void StartGrids() {
+            TestUtils.KillProcesses();
+
+            IgniteConfigurationEx cfg = new IgniteConfigurationEx();
+
+            PortableConfiguration portCfg = new PortableConfiguration();
+
+            ICollection<PortableTypeConfiguration> portTypeCfgs = new List<PortableTypeConfiguration>();
+
+            portTypeCfgs.Add(new PortableTypeConfiguration(typeof(PortablePerson)));
+            portTypeCfgs.Add(new PortableTypeConfiguration(typeof(CacheTestKey)));
+            portTypeCfgs.Add(new PortableTypeConfiguration(typeof(TestReferenceObject)));
+            portTypeCfgs.Add(new PortableTypeConfiguration(typeof(PortableAddArgCacheEntryProcessor)));
+            portTypeCfgs.Add(new PortableTypeConfiguration(typeof(PortableTestException)));
+
+            portCfg.TypeConfigurations = portTypeCfgs;
+
+            cfg.PortableConfiguration = portCfg;
+            cfg.JvmClasspath = TestUtils.CreateTestClasspath();
+            cfg.JvmOptions = TestUtils.TestJavaOptions();
+            cfg.SpringConfigUrl = "config\\native-client-test-cache.xml";
+
+            for (int i = 0; i < GridCount(); i++) {
+                cfg.GridName = "grid-" + i;
+
+                Ignition.Start(cfg);
+            }
+        }
+
+        /// <summary>
+        ///
+        /// </summary>
+        [TestFixtureTearDown]
+        public virtual void StopGrids() {
+            for (int i = 0; i < GridCount(); i++)
+                Ignition.Stop("grid-" + i, true);
+        }
+
+        /// <summary>
+        ///
+        /// </summary>
+        [SetUp]
+        public virtual void BeforeTest()
+        {
+            Console.WriteLine("Test started: " + TestContext.CurrentContext.Test.Name);
+        }
+
+        /// <summary>
+        ///
+        /// </summary>
+        [TearDown]
+        public virtual void AfterTest() {
+            for (int i = 0; i < GridCount(); i++) 
+                Cache(i).RemoveAll();
+
+            for (int i = 0; i < GridCount(); i++)
+            {
+                var cache = Cache(i);
+
+                if (!cache.IsEmpty)
+                {
+                    var entries = Enumerable.Range(0, 2000)
+                        .Select(x => new KeyValuePair<int, int>(x, cache.LocalPeek(x)))
+                        .Where(x => x.Value != 0)
+                        .Select(pair => pair.ToString() + GetKeyAffinity(cache, pair.Key))
+                        .Aggregate((acc, val) => string.Format("{0}, {1}", acc, val));
+
+                    Assert.Fail("Cache '{0}' is not empty in grid [{1}]: ({2})", CacheName(), i, entries);
+                }
+            }
+
+            Console.WriteLine("Test finished: " + TestContext.CurrentContext.Test.Name);
+        }
+
+        public IIgnite GetIgnite(int idx)
+        {
+            return Ignition.GetIgnite("grid-" + idx);
+        }
+
+        public ICache<int, int> Cache(int idx) {
+            return Cache<int, int>(idx);
+        }
+
+        public ICache<TK, TV> Cache<TK, TV>(int idx) {
+            return GetIgnite(idx).Cache<TK, TV>(CacheName());
+        }
+
+        public ICache<int, int> Cache()
+        {
+            return Cache<int, int>(0);
+        }
+
+        public ICache<TK, TV> Cache<TK, TV>()
+        {
+            return Cache<TK, TV>(0);
+        }
+
+        public ICacheAffinity Affinity()
+        {
+            return GetIgnite(0).Affinity(CacheName());
+        }
+
+        public ITransactions Transactions
+        {
+            get { return GetIgnite(0).Transactions; }
+        }
+
+        [Test]
+        public void TestCircularReference()
+        {
+            var cache = Cache().WithKeepPortable<int, object>();
+
+            TestReferenceObject obj1 = new TestReferenceObject();
+
+            obj1.Obj = new TestReferenceObject(obj1);
+
+            cache.Put(1, obj1);
+
+            var po = (IPortableObject) cache.Get(1);
+
+            Assert.IsNotNull(po);
+
+            TestReferenceObject objRef = po.Deserialize<TestReferenceObject>();
+
+            Assert.IsNotNull(objRef);
+        }
+
+        [Test]
+        public void TestName()
+        {
+            for (int i = 0; i < GridCount(); i++ )
+                Assert.AreEqual(CacheName(), Cache(i).Name);
+        }
+
+        [Test]
+        public void TestIsEmpty()
+        {
+            for (int i = 0; i < GridCount(); i++)
+            {
+                var cache = Cache(i);
+
+                Assert.IsTrue(cache.IsEmpty);
+            }
+
+            for (int i = 0; i < GridCount(); i++)
+            {
+                var cache = Cache(i);
+
+                cache.Put(PrimaryKeyForCache(cache), 1);
+            }
+
+            for (int i = 0; i < GridCount(); i++)
+            {
+                var cache = Cache(i);
+
+                Assert.IsFalse(cache.IsEmpty);
+            }
+        }
+
+        [Test]
+        public void TestContainsKey()
+        {
+            var cache = Cache();
+
+            int key = PrimaryKeyForCache(cache);
+
+            cache.Put(key, 1);
+
+            Assert.IsTrue(cache.ContainsKey(key));
+            Assert.IsFalse(cache.ContainsKey(-1));
+        }
+        
+        [Test]
+        public void TestContainsKeys()
+        {
+            var cache = Cache();
+
+            var keys = PrimaryKeysForCache(cache, 5);
+
+            Assert.IsFalse(cache.ContainsKeys(keys));
+
+            cache.PutAll(keys.ToDictionary(k => k, k => k));
+
+            Assert.IsTrue(cache.ContainsKeys(keys));
+
+            Assert.IsFalse(cache.ContainsKeys(keys.Concat(new[] {int.MaxValue})));
+        }
+        
+        [Test]
+        public void TestPeek()
+        {
+            var cache = Cache();
+
+            int key1 = PrimaryKeyForCache(cache);
+
+            cache.Put(key1, 1);
+
+            Assert.AreEqual(1, cache.LocalPeek(key1));
+            Assert.AreEqual(0, cache.LocalPeek(-1));
+
+            Assert.AreEqual(1, cache.LocalPeek(key1, CachePeekMode.All));
+            Assert.AreEqual(0, cache.LocalPeek(-1, CachePeekMode.All));
+        }
+
+        [Test]
+        public void TestGet()
+        {
+            var cache = Cache();
+
+            cache.Put(1, 1);
+            cache.Put(2, 2);
+
+            Assert.AreEqual(1, cache.Get(1));
+            Assert.AreEqual(2, cache.Get(2));
+            Assert.AreEqual(0, cache.Get(3));
+        }
+
+        [Test]
+        public void TestGetAsync()
+        {
+            var cache = Cache().WithAsync().WrapAsync();
+
+            cache.Put(1, 1);
+            cache.Put(2, 2);
+            
+            Assert.AreEqual(1, cache.Get(1));
+            Assert.AreEqual(2, cache.Get(2));
+            Assert.AreEqual(0, cache.Get(3));
+        }
+
+        [Test]
+        public void TestGetAll()
+        {
+            var cache = Cache();
+
+            cache.Put(1, 1);
+            cache.Put(2, 2);
+            cache.Put(3, 3);
+            cache.Put(4, 4);
+            cache.Put(5, 5);
+
+            IDictionary<int, int> map = cache.GetAll(new List<int> { 0, 1, 2, 5 });
+
+            Assert.AreEqual(3, map.Count);
+
+            Assert.AreEqual(1, map[1]);
+            Assert.AreEqual(2, map[2]);
+        }
+
+        [Test]
+        public void TestGetAllAsync()
+        {
+            var cache = Cache().WithAsync().WrapAsync();
+
+            cache.Put(1, 1);
+            cache.Put(2, 2);
+            cache.Put(3, 3);
+
+            var map = cache.GetAll(new List<int> { 0, 1, 2 });
+
+            Assert.AreEqual(2, map.Count);
+
+            Assert.AreEqual(1, map[1]);
+            Assert.AreEqual(2, map[2]);
+        }
+
+        [Test]
+        public void TestGetAndPut()
+        {
+            var cache = Cache();
+
+            Assert.AreEqual(0, cache.Get(1));
+
+            int old = cache.GetAndPut(1, 1);
+
+            Assert.AreEqual(0, old);
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            old = cache.GetAndPut(1, 2);
+
+            Assert.AreEqual(1, old);
+
+            Assert.AreEqual(2, cache.Get(1));
+        }
+
+        [Test]
+        public void TestGetAndReplace()
+        {
+            var cache = Cache();
+
+            cache.Put(1, 10);
+
+            Assert.AreEqual(10, cache.GetAndReplace(1, 100));
+
+            Assert.AreEqual(0, cache.GetAndReplace(2, 2));
+
+            Assert.AreEqual(0, cache.Get(2));
+
+            Assert.AreEqual(100, cache.Get(1));
+
+            Assert.IsTrue(cache.Remove(1));
+        }
+
+        [Test]
+        public void TestGetAndRemove()
+        {
+            var cache = Cache();
+
+            cache.Put(1, 1);
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            Assert.AreEqual(0, cache.GetAndRemove(0));
+            
+            Assert.AreEqual(1, cache.GetAndRemove(1));
+            
+            Assert.AreEqual(0, cache.GetAndRemove(1));
+            
+            Assert.AreEqual(0, cache.Get(1));
+        }
+
+        [Test]
+        public void TestGetAndPutAsync()
+        {
+            var cache = Cache().WithAsync().WrapAsync();
+
+            Assert.AreEqual(0, cache.Get(1));
+
+            int old = cache.GetAndPut(1, 1);
+
+            Assert.AreEqual(0, old);
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            old = cache.GetAndPut(1, 2);
+
+            Assert.AreEqual(1, old);
+
+            Assert.AreEqual(2, cache.Get(1));
+        }
+
+        [Test]
+        public void TestPut()
+        {
+            var cache = Cache();
+
+            cache.Put(1, 1);
+
+            Assert.AreEqual(1, cache.Get(1));
+        }
+
+        [Test]
+        public void TestPutxAsync()
+        {
+            var cache = Cache().WithAsync().WrapAsync();
+
+            cache.Put(1, 1);
+
+            Assert.AreEqual(1, cache.Get(1));
+        }
+
+        [Test]
+        public void TestPutIfAbsent()
+        {
+            var cache = Cache();
+
+            Assert.AreEqual(0, cache.Get(1));
+
+            Assert.AreEqual(true, cache.PutIfAbsent(1, 1));
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            Assert.AreEqual(false, cache.PutIfAbsent(1, 2));
+
+            Assert.AreEqual(1, cache.Get(1));
+        }
+
+        [Test]
+        public void TestGetAndPutIfAbsent()
+        {
+            var cache = Cache();
+
+            Assert.AreEqual(0, cache.Get(1));
+
+            Assert.AreEqual(0, cache.GetAndPutIfAbsent(1, 1));
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            Assert.AreEqual(1, cache.GetAndPutIfAbsent(1, 2));
+
+            Assert.AreEqual(1, cache.Get(1));
+        }
+
+        [Test]
+        public void TestGetAndPutIfAbsentAsync()
+        {
+            var cache = Cache().WithAsync().WrapAsync();
+
+            Assert.AreEqual(0, cache.Get(1));
+
+            int old = cache.GetAndPutIfAbsent(1, 1);
+
+            Assert.AreEqual(0, old);
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            old = cache.GetAndPutIfAbsent(1, 2);
+
+            Assert.AreEqual(1, old);
+
+            Assert.AreEqual(1, cache.Get(1));
+        }
+
+        [Test]
+        public void TestPutIfAbsentAsync()
+        {
+            var cache = Cache().WithAsync().WrapAsync();
+
+            Assert.AreEqual(0, cache.Get(1));
+
+            Assert.IsTrue(cache.PutIfAbsent(1, 1));
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            Assert.IsFalse(cache.PutIfAbsent(1, 2));
+
+            Assert.AreEqual(1, cache.Get(1));
+        }
+
+        [Test]
+        public void TestReplace()
+        {
+            var cache = Cache();
+
+            Assert.AreEqual(0, cache.Get(1));
+
+            bool success = cache.Replace(1, 1);
+
+            Assert.AreEqual(false, success);
+
+            Assert.AreEqual(0, cache.Get(1));
+
+            cache.Put(1, 1);
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            success = cache.Replace(1, 2);
+
+            Assert.AreEqual(true, success);
+
+            Assert.AreEqual(2, cache.Get(1));
+
+            Assert.IsFalse(cache.Replace(1, -1, 3));
+
+            Assert.AreEqual(2, cache.Get(1));
+
+            Assert.IsTrue(cache.Replace(1, 2, 3));
+
+            Assert.AreEqual(3, cache.Get(1));
+        }
+
+        [Test]
+        public void TestGetAndReplaceAsync()
+        {
+            var cache = Cache().WithAsync().WrapAsync();
+
+            Assert.AreEqual(0, cache.Get(1));
+
+            int old = cache.GetAndReplace(1, 1);
+
+            Assert.AreEqual(0, old);
+
+            Assert.AreEqual(0, cache.Get(1));
+
+            cache.Put(1, 1);
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            old = cache.GetAndReplace(1, 2);
+
+            Assert.AreEqual(1, old);
+
+            Assert.AreEqual(2, cache.Get(1));
+
+            Assert.IsFalse(cache.Replace(1, -1, 3));
+
+            Assert.AreEqual(2, cache.Get(1));
+
+            Assert.IsTrue(cache.Replace(1, 2, 3));
+
+            Assert.AreEqual(3, cache.Get(1));
+        }
+
+        [Test]
+        public void TestReplacex()
+        {
+            var cache = Cache();
+
+            Assert.AreEqual(0, cache.Get(1));
+
+            Assert.IsFalse(cache.Replace(1, 1));
+
+            Assert.AreEqual(0, cache.Get(1));
+
+            cache.Put(1, 1);
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            Assert.IsTrue(cache.Replace(1, 2));
+
+            Assert.AreEqual(2, cache.Get(1));
+        }
+
+        [Test]
+        public void TestReplaceAsync()
+        {
+            var cache = Cache().WithAsync().WrapAsync();
+
+            Assert.AreEqual(0, cache.Get(1));
+
+            Assert.IsFalse(cache.Replace(1, 1));
+
+            Assert.AreEqual(0, cache.Get(1));
+
+            cache.Put(1, 1);
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            Assert.IsTrue(cache.Replace(1, 2));
+
+            Assert.AreEqual(2, cache.Get(1));
+        }
+
+        [Test]
+        public void TestPutAll()
+        {
+            var cache = Cache();
+
+            cache.PutAll(new Dictionary<int, int> { { 1, 1 }, { 2, 2 }, { 3, 3 } });
+
+            Assert.AreEqual(1, cache.Get(1));
+            Assert.AreEqual(2, cache.Get(2));
+            Assert.AreEqual(3, cache.Get(3));
+        }
+
+        [Test]
+        public void TestPutAllAsync()
+        {
+            var cache = Cache().WithAsync().WrapAsync();
+
+            cache.PutAll(new Dictionary<int, int> { { 1, 1 }, { 2, 2 }, { 3, 3 } });
+
+            Assert.AreEqual(1, cache.Get(1));
+            Assert.AreEqual(2, cache.Get(2));
+            Assert.AreEqual(3, cache.Get(3));
+        }
+
+        /// <summary>
+        /// Expiry policy tests.
+        /// </summary>
+        [Test]
+        public void TestWithExpiryPolicy()
+        {
+            ICache<int, int> cache0 = Cache(0);
+            
+            int key0;
+            int key1;
+
+            if (LocalCache())
+            {
+                key0 = 0;
+                key1 = 1;
+            }
+            else
+            {
+                key0 = PrimaryKeyForCache(cache0);
+                key1 = PrimaryKeyForCache(Cache(1));
+            }
+            
+            // Test unchanged expiration.
+            ICache<int, int> cache = cache0.WithExpiryPolicy(new ExpiryPolicy(null, null, null));
+
+            cache.Put(key0, key0);
+            cache.Put(key1, key1);
+            Thread.Sleep(200);
+            Assert.IsTrue(cache0.ContainsKey(key0));
+            Assert.IsTrue(cache0.ContainsKey(key1));
+
+            cache.Put(key0, key0 + 1);
+            cache.Put(key1, key1 + 1);
+            Thread.Sleep(200);
+            Assert.IsTrue(cache0.ContainsKey(key0));
+            Assert.IsTrue(cache0.ContainsKey(key1));
+
+            cache.Get(key0);
+            cache.Get(key1);
+            Thread.Sleep(200);
+            Assert.IsTrue(cache0.ContainsKey(key0));
+            Assert.IsTrue(cache0.ContainsKey(key1));
+
+            cache0.RemoveAll(new List<int> { key0, key1 });
+
+            // Test eternal expiration.
+            cache = cache0.WithExpiryPolicy(new ExpiryPolicy(TimeSpan.MaxValue, TimeSpan.MaxValue, TimeSpan.MaxValue));
+
+            cache.Put(key0, key0);
+            cache.Put(key1, key1);
+            Thread.Sleep(200);
+            Assert.IsTrue(cache0.ContainsKey(key0));
+            Assert.IsTrue(cache0.ContainsKey(key1));
+
+            cache.Put(key0, key0 + 1);
+            cache.Put(key1, key1 + 1);
+            Thread.Sleep(200);
+            Assert.IsTrue(cache0.ContainsKey(key0));
+            Assert.IsTrue(cache0.ContainsKey(key1));
+
+            cache.Get(key0);
+            cache.Get(key1);
+            Thread.Sleep(200);
+            Assert.IsTrue(cache0.ContainsKey(key0));
+            Assert.IsTrue(cache0.ContainsKey(key1));
+
+            cache0.RemoveAll(new List<int> { key0, key1 });
+
+            // Test zero expiration.
+            cache = cache0.WithExpiryPolicy(new ExpiryPolicy(TimeSpan.Zero, TimeSpan.Zero, TimeSpan.Zero));
+
+            cache.Put(key0, key0);
+            cache.Put(key1, key1);
+            Assert.IsFalse(cache0.ContainsKey(key0));
+            Assert.IsFalse(cache0.ContainsKey(key1));
+
+            cache0.Put(key0, key0);
+            cache0.Put(key1, key1);
+            Assert.IsTrue(cache0.ContainsKey(key0));
+            Assert.IsTrue(cache0.ContainsKey(key1));
+            cache.Put(key0, key0 + 1);
+            cache.Put(key1, key1 + 1);
+            Assert.IsFalse(cache0.ContainsKey(key0));
+            Assert.IsFalse(cache0.ContainsKey(key1));
+
+            cache0.Put(key0, key0);
+            cache0.Put(key1, key1);
+            cache.Get(key0);
+            cache.Get(key1);
+            Assert.IsFalse(cache0.ContainsKey(key0));
+            Assert.IsFalse(cache0.ContainsKey(key1));
+
+            cache0.RemoveAll(new List<int> { key0, key1 });
+
+            // Test negative expiration.
+            cache = cache0.WithExpiryPolicy(new ExpiryPolicy(TimeSpan.FromMilliseconds(-100), 
+                TimeSpan.FromMilliseconds(-100), TimeSpan.FromMilliseconds(-100)));
+
+            cache.Put(key0, key0);
+            cache.Put(key1, key1);
+            Assert.IsFalse(cache0.ContainsKey(key0));
+            Assert.IsFalse(cache0.ContainsKey(key1));
+
+            cache0.Put(key0, key0);
+            cache0.Put(key1, key1);
+            Assert.IsTrue(cache0.ContainsKey(key0));
+            Assert.IsTrue(cache0.ContainsKey(key1));
+            cache.Put(key0, key0 + 1);
+            cache.Put(key1, key1 + 1);
+            Assert.IsFalse(cache0.ContainsKey(key0));
+            Assert.IsFalse(cache0.ContainsKey(key1));
+
+            cache0.Put(key0, key0);
+            cache0.Put(key1, key1);
+            cache.Get(key0);
+            cache.Get(key1);
+            Assert.IsFalse(cache0.ContainsKey(key0));
+            Assert.IsFalse(cache0.ContainsKey(key1));
+
+            cache0.RemoveAll(new List<int> { key0, key1 });
+
+            // Test regular expiration.
+            cache = cache0.WithExpiryPolicy(new ExpiryPolicy(TimeSpan.FromMilliseconds(100),
+                TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(100)));
+
+            cache.Put(key0, key0);
+            cache.Put(key1, key1);
+            Assert.IsTrue(cache0.ContainsKey(key0));
+            Assert.IsTrue(cache0.ContainsKey(key1));
+            Thread.Sleep(200);
+            Assert.IsFalse(cache0.ContainsKey(key0));
+            Assert.IsFalse(cache0.ContainsKey(key1));
+
+            cache0.Put(key0, key0);
+            cache0.Put(key1, key1);
+            cache.Put(key0, key0 + 1);
+            cache.Put(key1, key1 + 1);
+            Assert.IsTrue(cache0.ContainsKey(key0));
+            Assert.IsTrue(cache0.ContainsKey(key1));
+            Thread.Sleep(200);
+            Assert.IsFalse(cache0.ContainsKey(key0));
+            Assert.IsFalse(cache0.ContainsKey(key1));
+
+            cache0.Put(key0, key0);
+            cache0.Put(key1, key1);
+            cache.Get(key0); 
+            cache.Get(key1);
+            Assert.IsTrue(cache0.ContainsKey(key0));
+            Assert.IsTrue(cache0.ContainsKey(key1));
+            Thread.Sleep(200);
+            Assert.IsFalse(cache0.ContainsKey(key0));
+            Assert.IsFalse(cache0.ContainsKey(key1));
+        }
+
+        [Test]
+        public void TestEvict()
+        {
+            var cache = Cache();
+
+            int key = PrimaryKeyForCache(cache);
+
+            cache.Put(key, 1);
+
+            Assert.AreEqual(1, PeekInt(cache, key));
+
+            cache.LocalEvict(new[] {key});
+
+            Assert.AreEqual(0, cache.LocalSize(CachePeekMode.Onheap));
+
+            Assert.AreEqual(0, PeekInt(cache, key));
+
+            Assert.AreEqual(1, cache.Get(key));
+
+            Assert.AreEqual(1, cache.LocalSize(CachePeekMode.Onheap));
+
+            Assert.AreEqual(1, PeekInt(cache, key));
+        }
+
+        [Test]
+        public void TestEvictAllKeys()
+        {
+            var cache = Cache();
+
+            List<int> keys = PrimaryKeysForCache(cache, 3);
+
+            cache.Put(keys[0], 1);
+            cache.Put(keys[1], 2);
+            cache.Put(keys[2], 3);
+
+            Assert.AreEqual(1, PeekInt(cache, keys[0]));
+            Assert.AreEqual(2, PeekInt(cache, keys[1]));
+            Assert.AreEqual(3, PeekInt(cache, keys[2]));
+
+            cache.LocalEvict(new List<int> { -1, keys[0], keys[1] });
+
+            Assert.AreEqual(1, cache.LocalSize(CachePeekMode.Onheap));
+
+            Assert.AreEqual(0, PeekInt(cache, keys[0]));
+            Assert.AreEqual(0, PeekInt(cache, keys[1]));
+            Assert.AreEqual(3, PeekInt(cache, keys[2]));
+
+            Assert.AreEqual(1, cache.Get(keys[0]));
+            Assert.AreEqual(2, cache.Get(keys[1]));
+
+            Assert.AreEqual(3, cache.LocalSize());
+
+            Assert.AreEqual(1, PeekInt(cache, keys[0]));
+            Assert.AreEqual(2, PeekInt(cache, keys[1]));
+            Assert.AreEqual(3, PeekInt(cache, keys[2]));
+        }
+
+        [Test]
+        public void TestClear()
+        {
+            for (int i = 0; i < GridCount(); i++)
+            {
+                var cache = Cache(i);
+
+                cache.Put(PrimaryKeyForCache(cache, 500), 1);
+
+                Assert.IsFalse(cache.IsEmpty);
+            }
+
+            Cache().Clear();
+
+            for (int i = 0; i < GridCount(); i++)
+                Assert.IsTrue(Cache(i).IsEmpty);
+        }
+
+        [Test]
+        public void TestClearKey()
+        {
+            var cache = Cache();
+            var keys = PrimaryKeysForCache(cache, 10);
+
+            foreach (var key in keys)
+                cache.Put(key, 3);
+
+            var i = cache.Size();
+
+            foreach (var key in keys)
+            {
+                cache.Clear(key);
+
+                Assert.AreEqual(0, cache.Get(key));
+
+                Assert.Less(cache.Size(), i);
+
+                i = cache.Size();
+            }
+        }
+
+        [Test]
+        public void TestClearKeys()
+        {
+            var cache = Cache();
+            var keys = PrimaryKeysForCache(cache, 10);
+
+            foreach (var key in keys)
+                cache.Put(key, 3);
+
+            cache.ClearAll(keys);
+
+            foreach (var key in keys)
+                Assert.AreEqual(0, cache.Get(key));
+        }
+
+        [Test]
+        public void TestLocalClearKey()
+        {
+            var cache = Cache();
+            var keys = PrimaryKeysForCache(cache, 10);
+
+            foreach (var key in keys)
+                cache.Put(key, 3);
+
+            var i = cache.Size();
+
+            foreach (var key in keys)
+            {
+                cache.LocalClear(key);
+
+                Assert.AreEqual(0, cache.LocalPeek(key));
+
+                Assert.Less(cache.Size(), i);
+
+                i = cache.Size();
+            }
+
+            cache.Clear();
+        }
+
+        [Test]
+        public void TestLocalClearKeys()
+        {
+            var cache = Cache();
+            var keys = PrimaryKeysForCache(cache, 10);
+
+            foreach (var key in keys)
+                cache.Put(key, 3);
+
+            cache.LocalClearAll(keys);
+
+            foreach (var key in keys)
+                Assert.AreEqual(0, cache.LocalPeek(key));
+
+            cache.Clear();
+        }
+
+        [Test]
+        public void TestRemove()
+        {
+            var cache = Cache();
+
+            cache.Put(1, 1);
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            Assert.AreEqual(true, cache.Remove(1));
+
+            Assert.AreEqual(0, cache.Size());
+
+            Assert.AreEqual(0, cache.Get(1));
+
+            cache.Put(1, 1);
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            Assert.IsFalse(cache.Remove(1, -1));
+            Assert.IsTrue(cache.Remove(1, 1));
+
+            Assert.AreEqual(0, cache.Size());
+
+            Assert.AreEqual(0, cache.Get(1));
+        }
+
+        [Test]
+        public void TestGetAndRemoveAsync()
+        {
+            var cache = Cache().WithAsync().WrapAsync();
+
+            cache.Put(1, 1);
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            Assert.AreEqual(1, cache.GetAndRemove(1));
+
+            Assert.AreEqual(0, cache.Size());
+
+            Assert.AreEqual(0, cache.Get(1));
+
+            cache.Put(1, 1);
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            Assert.IsFalse(cache.Remove(1, -1));
+            Assert.IsTrue(cache.Remove(1, 1));
+
+            Assert.AreEqual(0, cache.Size());
+
+            Assert.AreEqual(0, cache.Get(1));
+        }
+
+        [Test]
+        public void TestRemovex()
+        {
+            var cache = Cache();
+
+            cache.Put(1, 1);
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            Assert.IsFalse(cache.Remove(-1));
+            Assert.IsTrue(cache.Remove(1));
+
+            Assert.AreEqual(0, cache.Size());
+
+            Assert.AreEqual(0, cache.Get(1));
+        }
+
+        [Test]
+        public void TestRemoveAsync()
+        {
+            var cache = Cache().WithAsync().WrapAsync();
+
+            cache.Put(1, 1);
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            Assert.IsFalse(cache.Remove(-1));
+            Assert.IsTrue(cache.Remove(1));
+
+            Assert.AreEqual(0, cache.Size());
+
+            Assert.AreEqual(0, cache.Get(1));
+        }
+
+        [Test]
+        public void TestRemoveAll()
+        {
+            var cache = Cache();
+
+            List<int> keys = PrimaryKeysForCache(cache, 2);
+
+            cache.Put(keys[0], 1);
+            cache.Put(keys[1], 2);
+
+            Assert.AreEqual(1, cache.Get(keys[0]));
+            Assert.AreEqual(2, cache.Get(keys[1]));
+
+            cache.RemoveAll();
+
+            Assert.AreEqual(0, cache.Size());
+
+            Assert.AreEqual(0, cache.Get(keys[0]));
+            Assert.AreEqual(0, cache.Get(keys[1]));
+        }
+
+        [Test]
+        public void TestRemoveAllAsync()
+        {
+            var cache = Cache().WithAsync().WrapAsync();
+
+            List<int> keys = PrimaryKeysForCache(cache, 2);
+
+            cache.Put(keys[0], 1);
+            cache.Put(keys[1], 2);
+
+            Assert.AreEqual(1, cache.Get(keys[0]));
+            Assert.AreEqual(2, cache.Get(keys[1]));
+
+            cache.RemoveAll();
+
+            Assert.AreEqual(0, cache.Size());
+
+            Assert.AreEqual(0, cache.Get(keys[0]));
+            Assert.AreEqual(0, cache.Get(keys[1]));
+        }
+
+        [Test]
+        public void TestRemoveAllKeys()
+        {
+            var cache = Cache();
+
+            Assert.AreEqual(0, cache.Size());
+
+            cache.Put(1, 1);
+            cache.Put(2, 2);
+            cache.Put(3, 3);
+
+            Assert.AreEqual(1, cache.Get(1));
+            Assert.AreEqual(2, cache.Get(2));
+            Assert.AreEqual(3, cache.Get(3));
+
+            cache.RemoveAll(new List<int> { 0, 1, 2 });
+
+            Assert.AreEqual(1, cache.Size(CachePeekMode.Primary));
+
+            Assert.AreEqual(0, cache.Get(1));
+            Assert.AreEqual(0, cache.Get(2));
+            Assert.AreEqual(3, cache.Get(3));
+        }
+
+        [Test]
+        public void TestRemoveAllKeysAsync()
+        {
+            var cache = Cache().WithAsync().WrapAsync();
+
+            Assert.AreEqual(0, cache.Size());
+
+            cache.Put(1, 1);
+            cache.Put(2, 2);
+            cache.Put(3, 3);
+
+            Assert.AreEqual(1, cache.Get(1));
+            Assert.AreEqual(2, cache.Get(2));
+            Assert.AreEqual(3, cache.Get(3));
+
+            cache.RemoveAll(new List<int> { 0, 1, 2 });
+
+            Assert.AreEqual(1, cache.Size(CachePeekMode.Primary));
+
+            Assert.AreEqual(0, cache.Get(1));
+            Assert.AreEqual(0, cache.Get(2));
+            Assert.AreEqual(3, cache.Get(3));
+        }
+
+        [Test]
+        public void TestSizes()
+        {
+            for (int i = 0; i < GridCount(); i++)
+            {
+                var cache = Cache(i);
+
+                List<int> keys = PrimaryKeysForCache(cache, 2);
+
+                foreach (int key in keys)
+                    cache.Put(key, 1);
+
+                Assert.IsTrue(cache.Size() >= 2);
+                Assert.AreEqual(2, cache.LocalSize(CachePeekMode.Primary));
+            }
+
+            ICache<int, int> cache0 = Cache();
+
+            Assert.AreEqual(GridCount() * 2, cache0.Size(CachePeekMode.Primary));
+
+            if (!LocalCache() && !ReplicatedCache())
+            {
+                int nearKey = NearKeyForCache(cache0);
+
+                cache0.Put(nearKey, 1);
+
+                Assert.AreEqual(NearEnabled() ? 1 : 0, cache0.Size(CachePeekMode.Near));
+            }
+        }
+
+        [Test]
+        public void TestLocalSize()
+        {
+            var cache = Cache();
+            var keys = PrimaryKeysForCache(cache, 3);
+
+            cache.Put(keys[0], 1);
+            cache.Put(keys[1], 2);
+
+            var localSize = cache.LocalSize();
+
+            cache.LocalEvict(keys.Take(2).ToArray());
+
+            Assert.AreEqual(0, cache.LocalSize(CachePeekMode.Onheap));
+            Assert.AreEqual(localSize, cache.LocalSize(CachePeekMode.All));
+
+            cache.Put(keys[2], 3);
+
+            Assert.AreEqual(localSize + 1, cache.LocalSize(CachePeekMode.All));
+
+            cache.RemoveAll(keys.Take(2).ToArray());
+        }
+
+        /// <summary>
+        /// Test enumerators.
+        /// </summary>
+        [Test]
+        [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
+        public void TestEnumerators()
+        {
+            var cache = Cache();
+            var keys = PrimaryKeysForCache(cache, 2);
+
+            cache.Put(keys[0], keys[0] + 1);
+            cache.Put(keys[1], keys[1] + 1);
+
+            // Check distributed enumerator.
+            IEnumerable<ICacheEntry<int, int>> e = cache;
+
+            CheckEnumerator(e.GetEnumerator(), keys);
+            CheckEnumerator(e.GetEnumerator(), keys);
+
+            // Check local enumerator.
+            e = cache.GetLocalEntries();
+
+            CheckEnumerator(e.GetEnumerator(), keys);
+            CheckEnumerator(e.GetEnumerator(), keys);
+
+            // Evict and check peek modes.
+            cache.LocalEvict(new List<int> { keys[0] } );
+
+            e = cache.GetLocalEntries(CachePeekMode.Onheap);
+            CheckEnumerator(e.GetEnumerator(), new List<int> { keys[1] });
+            CheckEnumerator(e.GetEnumerator(), new List<int> { keys[1] });
+
+            e = cache.GetLocalEntries(CachePeekMode.All);
+            CheckEnumerator(e.GetEnumerator(), keys);
+            CheckEnumerator(e.GetEnumerator(), keys);
+
+            e = cache.GetLocalEntries(CachePeekMode.Onheap, CachePeekMode.Swap);
+            CheckEnumerator(e.GetEnumerator(), keys);
+            CheckEnumerator(e.GetEnumerator(), keys);
+
+            cache.Remove(keys[0]);
+        }
+
+        /// <summary>
+        /// Check enumerator content.
+        /// </summary>
+        /// <param name="e">Enumerator.</param>
+        /// <param name="keys">Keys.</param>
+        private static void CheckEnumerator(IEnumerator<ICacheEntry<int, int>> e, IList<int> keys)
+        {
+            CheckEnumerator0(e, keys);
+
+            e.Reset();
+
+            CheckEnumerator0(e, keys);
+
+            e.Dispose();
+
+            Assert.Throws<ObjectDisposedException>(() => { e.MoveNext(); });
+            Assert.Throws<ObjectDisposedException>(() => { var entry = e.Current; });
+            Assert.Throws<ObjectDisposedException>(e.Reset);
+
+            e.Dispose();
+        }
+
+        /// <summary>
+        /// Check enumerator content.
+        /// </summary>
+        /// <param name="e">Enumerator.</param>
+        /// <param name="keys">Keys.</param>
+        private static void CheckEnumerator0(IEnumerator<ICacheEntry<int, int>> e, IList<int> keys)
+        {
+            Assert.Throws<InvalidOperationException>(() => { var entry = e.Current; });
+
+            int cnt = 0;
+
+            while (e.MoveNext())
+            {
+                ICacheEntry<int, int> entry = e.Current;
+
+                Assert.IsTrue(keys.Contains(entry.Key), "Unexpected entry: " + entry);
+
+                Assert.AreEqual(entry.Key + 1, entry.Value);
+
+                cnt++;
+            }
+
+            Assert.AreEqual(keys.Count, cnt);
+
+            Assert.IsFalse(e.MoveNext());
+
+            Assert.Throws<InvalidOperationException>(() => { var entry = e.Current; });
+        }
+
+        [Test]
+        public void TestPromote()
+        {
+            var cache = Cache();
+
+            int key = PrimaryKeyForCache(cache);
+
+            cache.Put(key, 1);
+
+            Assert.AreEqual(1, PeekInt(cache, key));
+
+            cache.LocalEvict(new[] {key});
+
+            Assert.AreEqual(0, cache.LocalSize(CachePeekMode.Onheap));
+
+            Assert.AreEqual(0, PeekInt(cache, key));
+
+            cache.LocalPromote(new[] { key });
+
+            Assert.AreEqual(1, cache.LocalSize(CachePeekMode.Onheap));
+
+            Assert.AreEqual(1, PeekInt(cache, key));
+        }
+
+        [Test]
+        public void TestPromoteAll()
+        {
+            var cache = Cache();
+
+            List<int> keys = PrimaryKeysForCache(cache, 3);
+
+            cache.Put(keys[0], 1);
+            cache.Put(keys[1], 2);
+            cache.Put(keys[2], 3);
+
+            Assert.AreEqual(1, PeekInt(cache, keys[0]));
+            Assert.AreEqual(2, PeekInt(cache, keys[1]));
+            Assert.AreEqual(3, PeekInt(cache, keys[2]));
+
+            cache.LocalEvict(new List<int> { -1, keys[0], keys[1] });
+
+            Assert.AreEqual(1, cache.LocalSize(CachePeekMode.Onheap));
+
+            Assert.AreEqual(0, PeekInt(cache, keys[0]));
+            Assert.AreEqual(0, PeekInt(cache, keys[1]));
+            Assert.AreEqual(3, PeekInt(cache, keys[2]));
+
+            cache.LocalPromote(new[] {keys[0], keys[1]});
+
+            Assert.AreEqual(3, cache.LocalSize(CachePeekMode.Onheap));
+
+            Assert.AreEqual(1, PeekInt(cache, keys[0]));
+            Assert.AreEqual(2, PeekInt(cache, keys[1]));
+            Assert.AreEqual(3, PeekInt(cache, keys[2]));
+        }
+
+        [Test]
+        public void TestPutGetPortable()
+        {
+            var cache = Cache<int, PortablePerson>();
+
+            PortablePerson obj1 = new PortablePerson("obj1", 1);
+
+            cache.Put(1, obj1);
+
+            obj1 = cache.Get(1);
+
+            Assert.AreEqual("obj1", obj1.Name);
+            Assert.AreEqual(1, obj1.Age);
+        }
+
+        [Test]
+        public void TestPutGetPortableAsync()
+        {
+            var cache = Cache<int, PortablePerson>().WithAsync().WrapAsync();
+
+            PortablePerson obj1 = new PortablePerson("obj1", 1);
+
+            cache.Put(1, obj1);
+
+            obj1 = cache.Get(1);
+
+            Assert.AreEqual("obj1", obj1.Name);
+            Assert.AreEqual(1, obj1.Age);
+        }
+
+        [Test]
+        public void TestPutGetPortableKey()
+        {
+            var cache = Cache<CacheTestKey, string>();
+
+            int cnt = 100;
+
+            for (int i = 0; i < cnt; i++)
+                cache.Put(new CacheTestKey(i), "val-" + i);
+
+            for (int i = 0; i < cnt; i++)
+                Assert.AreEqual("val-" + i, cache.Get(new CacheTestKey(i)));
+        }
+
+        [Test]
+        public void TestGetAsync2()
+        {
+            var cache = Cache().WithAsync();
+
+            for (int i = 0; i < 100; i++)
+            {
+                cache.Put(i, i);
+
+                cache.GetFuture<object>().Get();
+            }
+
+            var futs = new List<IFuture<int>>();
+
+            for (int i = 0; i < 1000; i++)
+            {
+                cache.Get(i % 100);
+
+                futs.Add(cache.GetFuture<int>());
+            }
+
+            for (int i = 0; i < 1000; i++) {
+                Assert.AreEqual(i % 100, futs[i].Get(), "Unexpected result: " + i);
+
+                Assert.IsTrue(futs[i].IsDone);
+            }
+        }
+
+        [Test]
+        [Category(TestUtils.CategoryIntensive)]
+        public void TestGetAsyncMultithreaded()
+        {
+            var cache = Cache().WithAsync();
+
+            for (int i = 0; i < 100; i++)
+            {
+                cache.Put(i, i);
+
+                cache.GetFuture<object>().Get();
+            }
+
+            TestUtils.RunMultiThreaded(() =>
+            {
+                for (int i = 0; i < 100; i++)
+                {
+                    var futs = new List<IFuture<int>>();
+
+                    for (int j = 0; j < 100; j++)
+                    {
+                        cache.Get(j);
+
+                        futs.Add(cache.GetFuture<int>());
+                    }
+
+                    for (int j = 0; j < 100; j++)
+                        Assert.AreEqual(j, futs[j].Get());
+                }
+            }, 10);
+        }
+
+        [Test]
+        [Category(TestUtils.CategoryIntensive)]
+        public void TestPutxAsyncMultithreaded()
+        {
+            var cache = Cache().WithAsync();
+
+            TestUtils.RunMultiThreaded(() =>
+            {
+                Random rnd = new Random();
+
+                for (int i = 0; i < 50; i++)
+                {
+                    var futs = new List<IFuture<object>>();
+
+                    for (int j = 0; j < 10; j++)
+                    {
+                        cache.Put(rnd.Next(1000), i);
+
+                        futs.Add(cache.GetFuture<object>());
+                    }
+
+                    foreach (var fut in futs)
+                        fut.Get();
+                }
+            }, 5);
+        }
+
+        [Test]
+        [Category(TestUtils.CategoryIntensive)]
+        public void TestPutGetAsyncMultithreaded()
+        {
+            var cache = Cache<CacheTestKey, PortablePerson>().WithAsync();
+
+            const int threads = 10;
+            const int objPerThread = 1000;
+
+            int cntr = 0;
+
+            TestUtils.RunMultiThreaded(() =>
+            {
+                // ReSharper disable once AccessToModifiedClosure
+                int threadIdx = Interlocked.Increment(ref cntr);
+
+                var futs = new List<IFuture<object>>();
+
+                for (int i = 0; i < objPerThread; i++)
+                {
+                    int key = threadIdx * objPerThread + i;
+
+                    cache.Put(new CacheTestKey(key), new PortablePerson("Person-" + key, key));
+
+                    futs.Add(cache.GetFuture<object>());
+                }
+
+                foreach (var fut in futs)
+                {
+                    fut.Get();
+
+                    Assert.IsTrue(fut.IsDone);
+                }
+            }, threads);
+
+            for (int i = 0; i < threads; i++)
+            {
+                int threadIdx = i + 1;
+
+                for (int j = 0; j < objPerThread; j++)
+                {
+                    int key = threadIdx * objPerThread + i;
+
+                    cache.Get(new CacheTestKey(key));
+                    var p = cache.GetFuture<PortablePerson>().Get();
+
+                    Assert.IsNotNull(p);
+                    Assert.AreEqual(key, p.Age);
+                    Assert.AreEqual("Person-" + key, p.Name);
+                }
+            }
+
+            cntr = 0;
+
+            TestUtils.RunMultiThreaded(() =>
+            {
+                int threadIdx = Interlocked.Increment(ref cntr);
+
+                for (int i = 0; i < objPerThread; i++)
+                {
+                    int key = threadIdx * objPerThread + i;
+
+                    cache.Put(new CacheTestKey(key), new PortablePerson("Person-" + key, key));
+
+                    cache.GetFuture<object>().Get();
+                }
+            }, threads);
+
+            cntr = 0;
+
+            TestUtils.RunMultiThreaded(() =>
+            {
+                int threadIdx = Interlocked.Increment(ref cntr);
+
+                var futs = new List<IFuture<PortablePerson>>();
+
+                for (int i = 0; i < objPerThread; i++)
+                {
+                    int key = threadIdx * objPerThread + i;
+
+                    cache.Get(new CacheTestKey(key));
+
+                    futs.Add(cache.GetFuture<PortablePerson>());
+                }
+
+                for (int i = 0; i < objPerThread; i++)
+                {
+                    var fut = futs[i];
+
+                    int key = threadIdx * objPerThread + i;
+
+                    var p = fut.Get();
+
+                    Assert.IsNotNull(p);
+                    Assert.AreEqual(key, p.Age);
+                    Assert.AreEqual("Person-" + key, p.Name);
+                }
+            }, threads);
+        }
+
+        //[Test]
+        //[Category(TestUtils.CATEGORY_INTENSIVE)]
+        public void TestAsyncMultithreadedKeepPortable()
+        {
+            var cache = Cache().WithAsync().WithKeepPortable<CacheTestKey, PortablePerson>();
+            var portCache = Cache().WithAsync().WithKeepPortable<CacheTestKey, IPortableObject>();
+
+            const int threads = 10;
+            const int objPerThread = 1000;
+
+            int cntr = 0;
+
+            TestUtils.RunMultiThreaded(() =>
+            {
+                // ReSharper disable once AccessToModifiedClosure
+                int threadIdx = Interlocked.Increment(ref cntr);
+
+                var futs = new List<IFuture<object>>();
+
+                for (int i = 0; i < objPerThread; i++)
+                {
+                    int key = threadIdx * objPerThread + i;
+
+                    cache.Put(new CacheTestKey(key), new PortablePerson("Person-" + key, key));
+
+                    futs.Add(cache.GetFuture<object>());
+                }
+
+                foreach (var fut in futs)
+                    Assert.IsNull(fut.Get());
+            }, threads);
+
+            for (int i = 0; i < threads; i++)
+            {
+                int threadIdx = i + 1;
+
+                for (int j = 0; j < objPerThread; j++)
+                {
+                    int key = threadIdx * objPerThread + i;
+
+                    IPortableObject p = portCache.Get(new CacheTestKey(key));
+
+                    Assert.IsNotNull(p);
+                    Assert.AreEqual(key, p.Field<int>("age"));
+                    Assert.AreEqual("Person-" + key, p.Field<string>("name"));
+                }
+            }
+
+            cntr = 0;
+
+            TestUtils.RunMultiThreaded(() =>
+            {
+                int threadIdx = Interlocked.Increment(ref cntr);
+
+                var futs = new List<IFuture<IPortableObject>>();
+
+                for (int i = 0; i < objPerThread; i++)
+                {
+                    int key = threadIdx * objPerThread + i;
+
+                    portCache.Get(new CacheTestKey(key));
+
+                    futs.Add(cache.GetFuture<IPortableObject>());
+                }
+
+                for (int i = 0; i < objPerThread; i++)
+                {
+                    var fut = futs[i];
+
+                    int key = threadIdx * objPerThread + i;
+
+                    var p = fut.Get();
+
+                    Assert.IsNotNull(p);
+                    Assert.AreEqual(key, p.Field<int>("age"));
+                    Assert.AreEqual("Person-" + key, p.Field<string>("name"));
+                }
+            }, threads);
+
+            cntr = 0;
+
+            TestUtils.RunMultiThreaded(() =>
+            {
+                int threadIdx = Interlocked.Increment(ref cntr);
+
+                var futs = new List<IFuture<bool>>();
+
+                for (int i = 0; i < objPerThread; i++)
+                {
+                    int key = threadIdx * objPerThread + i;
+
+                    cache.Remove(new CacheTestKey(key));
+
+                    futs.Add(cache.GetFuture<bool>());
+                }
+
+                for (int i = 0; i < objPerThread; i++)
+                {
+                    var fut = futs[i];
+
+                    Assert.AreEqual(true, fut.Get());
+                }
+            }, threads);
+        }
+
+        [Test]
+        [Ignore("IGNITE-835")]
+        public void TestLock()
+        {
+            if (!LockingEnabled())
+                return;
+
+            var cache = Cache();
+
+            const int key = 7;
+
+            // Lock
+            CheckLock(cache, key, () => cache.Lock(key));
+
+            // LockAll
+            CheckLock(cache, key, () => cache.LockAll(new[] { key, 2, 3, 4, 5 }));
+        }
+
+        /// <summary>
+        /// Internal lock test routine.
+        /// </summary>
+        /// <param name="cache">Cache.</param>
+        /// <param name="key">Key.</param>
+        /// <param name="getLock">Function to get the lock.</param>
+        private static void CheckLock(ICache<int, int> cache, int key, Func<ICacheLock> getLock)
+        {
+            var sharedLock = getLock();
+            
+            using (sharedLock)
+            {
+                Assert.Throws<InvalidOperationException>(() => sharedLock.Exit());  // can't exit if not entered
+
+                sharedLock.Enter();
+
+                try
+                {
+                    Assert.IsTrue(cache.IsLocalLocked(key, true));
+                    Assert.IsTrue(cache.IsLocalLocked(key, false));
+
+                    EnsureCannotLock(getLock, sharedLock);
+
+                    sharedLock.Enter();
+
+                    try
+                    {
+                        Assert.IsTrue(cache.IsLocalLocked(key, true));
+                        Assert.IsTrue(cache.IsLocalLocked(key, false));
+
+                        EnsureCannotLock(getLock, sharedLock);
+                    }
+                    finally
+                    {
+                        sharedLock.Exit();
+                    }
+
+                    Assert.IsTrue(cache.IsLocalLocked(key, true));
+                    Assert.IsTrue(cache.IsLocalLocked(key, false));
+
+                    EnsureCannotLock(getLock, sharedLock);
+
+                    Assert.Throws<SynchronizationLockException>(() => sharedLock.Dispose()); // can't dispose while locked
+                }
+                finally
+                {
+                    sharedLock.Exit();
+                }
+
+                Assert.IsFalse(cache.IsLocalLocked(key, true));
+                Assert.IsFalse(cache.IsLocalLocked(key, false));
+
+                var innerTask = new Task(() =>
+                {
+                    Assert.IsTrue(sharedLock.TryEnter());
+                    sharedLock.Exit();
+
+                    using (var otherLock = getLock())
+                    {
+                        Assert.IsTrue(otherLock.TryEnter());
+                        otherLock.Exit();
+                    }
+                });
+
+                innerTask.Start();
+                innerTask.Wait();
+            }
+            
+            Assert.IsFalse(cache.IsLocalLocked(key, true));
+            Assert.IsFalse(cache.IsLocalLocked(key, false));
+            
+            var outerTask = new Task(() =>
+            {
+                using (var otherLock = getLock())
+                {
+                    Assert.IsTrue(otherLock.TryEnter());
+                    otherLock.Exit();
+                }
+            });
+
+            outerTask.Start();
+            outerTask.Wait();
+
+            Assert.Throws<ObjectDisposedException>(() => sharedLock.Enter());  // Can't enter disposed lock
+        }
+
+        /// <summary>
+        /// ENsure taht lock cannot be obtained by other threads.
+        /// </summary>
+        /// <param name="getLock">Get lock function.</param>
+        /// <param name="sharedLock">Shared lock.</param>
+        private static void EnsureCannotLock(Func<ICacheLock> getLock, ICacheLock sharedLock)
+        {
+            var task = new Task(() =>
+            {
+                Assert.IsFalse(sharedLock.TryEnter());
+                Assert.IsFalse(sharedLock.TryEnter(TimeSpan.FromMilliseconds(100)));
+
+                using (var otherLock = getLock())
+                {
+                    Assert.IsFalse(otherLock.TryEnter());
+                    Assert.IsFalse(otherLock.TryEnter(TimeSpan.FromMilliseconds(100)));
+                }
+            });
+
+            task.Start();
+            task.Wait();
+        }
+
+        [Test]
+        public void TestTxCommit()
+        {
+            TestTxCommit(false);
+        }
+
+        [Test]
+        public void TestTxCommitAsync()
+        {
+            TestTxCommit(true);
+        }
+
+        private void TestTxCommit(bool async)
+        {
+            if (!TxEnabled())
+                return;
+
+            var cache = Cache();
+
+            ITransaction tx = Transactions.Tx;
+
+            Assert.IsNull(tx);
+
+            tx = Transactions.TxStart();
+
+            try
+            {
+                cache.Put(1, 1);
+
+                cache.Put(2, 2);
+
+                if (async)
+                {
+                    var asyncTx = tx.WithAsync();
+                    
+                    asyncTx.Commit();
+
+                    var fut = asyncTx.GetFuture();
+
+                    fut.Get();
+
+                    Assert.IsTrue(fut.IsDone);
+                    Assert.AreEqual(fut.Get(), null);
+                }
+                else
+                    tx.Commit();
+            }
+            finally
+            {
+                tx.Dispose();
+            }
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            Assert.AreEqual(2, cache.Get(2));
+
+            tx = Transactions.Tx;
+
+            Assert.IsNull(tx);
+        }
+
+        [Test]
+        public void TestTxRollback()
+        {
+            if (!TxEnabled())
+                return;
+
+            var cache = Cache();
+
+            cache.Put(1, 1);
+
+            cache.Put(2, 2);
+
+            ITransaction tx = Transactions.Tx;
+
+            Assert.IsNull(tx);
+
+            tx = Transactions.TxStart();
+
+            try {
+                cache.Put(1, 10);
+
+                cache.Put(2, 20);
+            }
+            finally {
+                tx.Rollback();
+            }
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            Assert.AreEqual(2, cache.Get(2));
+
+            Assert.IsNull(Transactions.Tx);
+        }
+
+        [Test]
+        public void TestTxClose()
+        {
+            if (!TxEnabled())
+                return;
+
+            var cache = Cache();
+
+            cache.Put(1, 1);
+
+            cache.Put(2, 2);
+
+            ITransaction tx = Transactions.Tx;
+
+            Assert.IsNull(tx);
+
+            tx = Transactions.TxStart();
+
+            try
+            {
+                cache.Put(1, 10);
+
+                cache.Put(2, 20);
+            }
+            finally
+            {
+                tx.Dispose();
+            }
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            Assert.AreEqual(2, cache.Get(2));
+
+            tx = Transactions.Tx;
+
+            Assert.IsNull(tx);
+        }
+        
+        [Test]
+        public void TestTxAllModes()
+        {
+            TestTxAllModes(false);
+
+            TestTxAllModes(true);
+
+            Console.WriteLine("Done");
+        }
+
+        protected void TestTxAllModes(bool withTimeout)
+        {
+            if (!TxEnabled())
+                return;
+
+            var cache = Cache();
+
+            int cntr = 0;
+
+            foreach (TransactionConcurrency concurrency in Enum.GetValues(typeof(TransactionConcurrency))) {
+                foreach (TransactionIsolation isolation in Enum.GetValues(typeof(TransactionIsolation))) {
+                    Console.WriteLine("Test tx [concurrency=" + concurrency + ", isolation=" + isolation + "]");
+
+                    ITransaction tx = Transactions.Tx;
+
+                    Assert.IsNull(tx);
+
+                    tx = withTimeout 
+                        ? Transactions.TxStart(concurrency, isolation, TimeSpan.FromMilliseconds(1100), 10)
+                        : Transactions.TxStart(concurrency, isolation);
+
+                    Assert.AreEqual(concurrency, tx.Concurrency);
+                    Assert.AreEqual(isolation, tx.Isolation);
+
+                    if (withTimeout)
+                        Assert.AreEqual(1100, tx.Timeout.TotalMilliseconds);
+
+                    try {
+                        cache.Put(1, cntr);
+
+                        tx.Commit();
+                    }
+                    finally {
+                        tx.Dispose();
+                    }
+
+                    tx = Transactions.Tx;
+
+                    Assert.IsNull(tx);
+
+                    Assert.AreEqual(cntr, cache.Get(1));
+
+                    cntr++;
+                }
+            }
+        }
+
+        [Test]
+        public void TestTxAttributes()
+        {
+            if (!TxEnabled())
+                return;
+
+            ITransaction tx = Transactions.TxStart(TransactionConcurrency.Optimistic,
+                TransactionIsolation.RepeatableRead, TimeSpan.FromMilliseconds(2500), 100);
+
+            Assert.IsFalse(tx.IsRollbackOnly);
+            Assert.AreEqual(TransactionConcurrency.Optimistic, tx.Concurrency);
+            Assert.AreEqual(TransactionIsolation.RepeatableRead, tx.Isolation);
+            Assert.AreEqual(2500, tx.Timeout.TotalMilliseconds);
+            Assert.AreEqual(TransactionState.Active, tx.State);
+            Assert.IsTrue(tx.StartTime.Ticks > 0);
+            Assert.AreEqual(tx.NodeId, GetIgnite(0).Cluster.LocalNode.Id);
+
+            DateTime startTime1 = tx.StartTime;
+
+            tx.Commit();
+
+            Assert.IsFalse(tx.IsRollbackOnly);
+            Assert.AreEqual(TransactionState.Committed, tx.State);
+            Assert.AreEqual(TransactionConcurrency.Optimistic, tx.Concurrency);
+            Assert.AreEqual(TransactionIsolation.RepeatableRead, tx.Isolation);
+            Assert.AreEqual(2500, tx.Timeout.TotalMilliseconds);
+            Assert.AreEqual(startTime1, tx.StartTime);
+
+            Thread.Sleep(100);
+
+            tx = Transactions.TxStart(TransactionConcurrency.Pessimistic, TransactionIsolation.ReadCommitted,
+                TimeSpan.FromMilliseconds(3500), 200);
+
+            Assert.IsFalse(tx.IsRollbackOnly);
+            Assert.AreEqual(TransactionConcurrency.Pessimistic, tx.Concurrency);
+            Assert.AreEqual(TransactionIsolation.ReadCommitted, tx.Isolation);
+            Assert.AreEqual(3500, tx.Timeout.TotalMilliseconds);
+            Assert.AreEqual(TransactionState.Active, tx.State);
+            Assert.IsTrue(tx.StartTime.Ticks > 0);
+            Assert.IsTrue(tx.StartTime > startTime1);
+
+            DateTime startTime2 = tx.StartTime;
+
+            tx.Rollback();
+
+            Assert.AreEqual(TransactionState.RolledBack, tx.State);
+            Assert.AreEqual(TransactionConcurrency.Pessimistic, tx.Concurrency);
+            Assert.AreEqual(TransactionIsolation.ReadCommitted, tx.Isolation);
+            Assert.AreEqual(3500, tx.Timeout.TotalMilliseconds);
+            Assert.AreEqual(startTime2, tx.StartTime);
+
+            Thread.Sleep(100);
+
+            tx = Transactions.TxStart(TransactionConcurrency.Optimistic, TransactionIsolation.RepeatableRead,
+                TimeSpan.FromMilliseconds(2500), 100);
+
+            Assert.IsFalse(tx.IsRollbackOnly);
+            Assert.AreEqual(TransactionConcurrency.Optimistic, tx.Concurrency);
+            Assert.AreEqual(TransactionIsolation.RepeatableRead, tx.Isolation);
+            Assert.AreEqual(2500, tx.Timeout.TotalMilliseconds);
+            Assert.AreEqual(TransactionState.Active, tx.State);
+            Assert.IsTrue(tx.StartTime > startTime2);
+
+            DateTime startTime3 = tx.StartTime;
+
+            tx.Commit();
+
+            Assert.IsFalse(tx.IsRollbackOnly);
+            Assert.AreEqual(TransactionState.Committed, tx.State);
+            Assert.AreEqual(TransactionConcurrency.Optimistic, tx.Concurrency);
+            Assert.AreEqual(TransactionIsolation.RepeatableRead, tx.Isolation);
+            Assert.AreEqual(2500, tx.Timeout.TotalMilliseconds);
+            Assert.AreEqual(startTime3, tx.StartTime);
+        }
+
+        [Test]
+        public void TestTxRollbackOnly()
+        {
+            if (!TxEnabled())
+                return;
+
+            var cache = Cache();
+
+            cache.Put(1, 1);
+
+            cache.Put(2, 2);
+
+            ITransaction tx = Transactions.TxStart();
+
+            cache.Put(1, 10);
+
+            cache.Put(2, 20);
+
+            Assert.IsFalse(tx.IsRollbackOnly);
+
+            tx.SetRollbackonly();
+
+            Assert.IsTrue(tx.IsRollbackOnly);
+
+            Assert.AreEqual(TransactionState.MarkedRollback, tx.State);
+
+            try
+            {
+                tx.Commit();
+
+                Assert.Fail("Commit must fail.");
+            }
+            catch (IgniteException e)
+            {
+                Console.WriteLine("Expected exception: " + e);
+            }
+
+            tx.Dispose();
+
+            Assert.AreEqual(TransactionState.RolledBack, tx.State);
+
+            Assert.IsTrue(tx.IsRollbackOnly);
+
+            Assert.AreEqual(1, cache.Get(1));
+
+            Assert.AreEqual(2, cache.Get(2));
+
+            tx = Transactions.Tx;
+
+            Assert.IsNull(tx);
+        }
+
+        [Test]
+        public void TestTxMetrics()
+        {
+            if (!TxEnabled())
+                return;
+
+            var cache = Cache();
+            
+            var startTime = DateTime.UtcNow.AddSeconds(-1);
+
+            Transactions.ResetMetrics();
+
+            var metrics = Transactions.GetMetrics();
+            
+            Assert.AreEqual(0, metrics.TxCommits);
+            Assert.AreEqual(0, metrics.TxRollbacks);
+
+            using (Transactions.TxStart())
+            {
+                cache.Put(1, 1);
+            }
+            
+            using (var tx = Transactions.TxStart())
+            {
+                cache.Put(1, 1);
+                tx.Commit();
+            }
+
+            metrics = Transactions.GetMetrics();
+
+            Assert.AreEqual(1, metrics.TxCommits);
+            Assert.AreEqual(1, metrics.TxRollbacks);
+
+            Assert.LessOrEqual(startTime, metrics.CommitTime);
+            Assert.LessOrEqual(startTime, metrics.RollbackTime);
+
+            Assert.GreaterOrEqual(DateTime.UtcNow, metrics.CommitTime);
+            Assert.GreaterOrEqual(DateTime.UtcNow, metrics.RollbackTime);
+        }
+
+        [Test]
+        public void TestTxStateAndExceptions()
+        {
+            if (!TxEnabled())
+                return;
+
+            var tx = Transactions.TxStart();
+            
+            Assert.AreEqual(TransactionState.Active, tx.State);
+
+            tx.Rollback();
+
+            Assert.AreEqual(TransactionState.RolledBack, tx.State);
+
+            try
+            {
+                tx.Commit();
+                Assert.Fail();
+            }
+            catch (InvalidOperationException)
+            {
+                // Expected
+            }
+
+            tx = Transactions.TxStart().WithAsync();
+
+            Assert.AreEqual(TransactionState.Active, tx.State);
+
+            tx.Commit();
+
+            tx.GetFuture().Get();
+
+            Assert.AreEqual(TransactionState.Committed, tx.State);
+
+            tx.Rollback();  // Illegal, but should not fail here; will fail in future
+
+            try
+            {
+                tx.GetFuture<object>().Get();
+                Assert.Fail();
+            }
+            catch (InvalidOperationException)
+            {
+                // Expected
+            }
+        }
+        
+        /// <summary>
+        /// Test thraed-locals leak.
+        /// </summary>
+        [Test]
+        [Category(TestUtils.CategoryIntensive)]
+        public void TestThreadLocalLeak()
+        {
+            var cache = Cache<string, string>();
+
+            Exception err = null;
+
+            const int threadCnt = 10;
+
+            Thread[] threads = new Thread[threadCnt];
+
+            ThreadStart[] threadStarts = new ThreadStart[threadCnt];
+
+            for (int j = 0; j < threadCnt; j++)
+            {
+                string key = "key" + j;
+
+                threadStarts[j] = () =>
+                {
+                    try
+                    {
+                        cache.Put(key, key);
+
+                        Assert.AreEqual(key, cache.Get(key));
+                    }
+                    catch (Exception e)
+                    {
+                        Interlocked.CompareExchange(ref err, e, null);
+
+                        Assert.Fail("Unexpected error: " + e);
+                    }
+                };
+            }
+
+            for (int i = 0; i < 100 && err == null; i++)
+            {
+                for (int j = 0 ; j < threadCnt; j++) {
+                    Thread t = new Thread(threadStarts[j]);
+
+                    threads[j] = t;
+                }
+
+                foreach (Thread t in threads)
+                    t.Start();
+
+                foreach (Thread t in threads)
+                    t.Join();
+
+                if (i % 500 == 0)
+                {
+                    Console.WriteLine("Iteration: " + i);
+
+                    GC.Collect();
+                }
+            }
+
+            Assert.IsNull(err);
+        }
+
+        //// TODO [Test]
+        //public void TestCacheEntryGetSetRemove()
+        //{
+        //    TestCacheEntryGetSetRemove(false);
+        //}
+
+        //// TODO [Test]
+        //public void TestCacheEntryGetSetRemoveAsync()
+        //{
+        //    TestCacheEntryGetSetRemove(true);
+        //}
+
+        //private void TestCacheEntryGetSetRemove(bool async)
+        //{
+        //    IGridCache cache = Cache();
+
+        //    int key = PrimaryKeyForCache(cache);
+
+        //    IGridCacheEntry<int, int> e = cache.Entry(key);
+
+        //    Assert.IsNotNull(e);
+
+        //    if (async)
+        //    {
+        //        IGridFuture<int> fut = e.GetAsync();
+
+        //        Assert.IsNull(fut.Result);
+
+        //        Assert.IsTrue(fut.IsDone);
+
+        //        fut = e.SetAsync(10);
+
+        //        Assert.IsNull(fut.Result);
+
+        //        Assert.IsTrue(fut.IsDone);
+
+        //        fut = e.GetAsync();
+
+        //        Assert.AreEqual(10, fut.Result);
+
+        //        Assert.IsTrue(fut.IsDone);
+        //    }
+        //    else
+        //    {
+        //        Assert.IsNull(e.Get());
+
+        //        Assert.IsNull(e.Set(10));
+
+        //        Assert.AreEqual(10, e.Get());
+        //    }
+
+        //    Assert.AreEqual(10, cache.Get(key));
+
+        //    if (async)
+        //    {
+        //        IGridFuture<int> fut = e.RemoveAsync();
+
+        //        Assert.AreEqual(10, fut.Result);
+
+        //        Assert.IsTrue(fut.IsDone);
+        //    }
+        //    else
+        //        Assert.AreEqual(10, e.Remove());
+
+        //    Assert.IsNull(e.Get());
+
+        //    Assert.IsNull(cache.Get(key));
+        //}
+
+        /**
+         * Test tries to provoke garbage collection for .Net future before it was completed to verify
+         * futures pinning works.
+         */
+        [Test]
+        [Category(TestUtils.CategoryIntensive)]
+        public void TestFuturesGc()
+        {
+            var cache = Cache().WithAsync();
+
+            cache.Put(1, 1);
+
+            for (int i = 0; i < 10; i++)
+            {
+                TestUtils.RunMultiThreaded(() =>
+                {
+                    for (int j = 0; j < 1000; j++)
+                        cache.Get(1);
+                }, 5);
+
+                GC.Collect();
+
+                cache.Get(1);
+                Assert.AreEqual(1, cache.GetFuture<int>().Get());
+            }
+
+            Thread.Sleep(2000);
+        }
+
+        [Test]
+        public void TestPartitions()
+        {
+            ICacheAffinity aff = Affinity();
+
+            for (int i = 0; i < 5; i++ )
+                Assert.AreEqual(CachePartitions(), aff.Partitions);
+        }
+
+        [Test]
+        public void TestKeyPartition()
+        {
+            ICacheAffinity aff = Affinity();
+
+            {
+                ISet<int> parts = new HashSet<int>();
+
+                for (int i = 0; i < 1000; i++)
+                    parts.Add(aff.Partition(i));
+
+                if (LocalCache())
+                    Assert.AreEqual(1, parts.Count);
+                else
+                    Assert.IsTrue(parts.Count > 10);
+            }
+
+            {
+                ISet<int> parts = new HashSet<int>();
+
+                for (int i = 0; i < 1000; i++)
+                    parts.Add(aff.Partition("key" + i));
+
+                if (LocalCache())
+                    Assert.AreEqual(1, parts.Count);
+                else
+                    Assert.IsTrue(parts.Count > 10);
+            }
+        }
+
+        [Test]
+        public void TestIsPrimaryOrBackup()
+        {
+            ICacheAffinity aff = Affinity();
+
+            ICollection<IClusterNode> nodes = GetIgnite(0).Cluster.Nodes();
+
+            Assert.IsTrue(nodes.Count > 0);
+
+            IClusterNode node = nodes.First();
+
+            {
+                bool found = false;
+
+                for (int i = 0; i < 1000; i++)
+                {
+                    if (aff.IsPrimary(node, i))
+                    {
+                        Assert.IsTrue(aff.IsPrimaryOrBackup(node, i));
+
+                        found = true;
+
+                        if (nodes.Count > 1)
+                            Assert.IsFalse(aff.IsPrimary(nodes.Last(), i));
+
+                        break;
+                    }
+                }
+
+                Assert.IsTrue(found, "Failed to find primary key for node " + node);
+            }
+
+            if (nodes.Count > 1)
+            {
+                bool found = false;
+
+                for (int i = 0; i < 1000; i++)
+                {
+                    if (aff.IsBackup(node, i))
+                    {
+                        Assert.IsTrue(aff.IsPrimaryOrBackup(node, i));
+
+                        found = true;
+
+                        break;
+                    }
+                }
+
+                Assert.IsTrue(found, "Failed to find backup key for node " + node);
+            }
+        }
+
+        [Test]
+        public void TestNodePartitions()
+        {
+            ICacheAffinity aff = Affinity();
+
+            ICollection<IClusterNode> nodes = GetIgnite(0).Cluster.Nodes();
+
+            Assert.IsTrue(nodes.Count > 0);
+
+            if (nodes.Count == 1)
+            {
+                IClusterNode node = nodes.First();
+
+                int[] parts = aff.BackupPartitions(node);
+
+                Assert.AreEqual(0, parts.Length);
+
+                parts = aff.AllPartitions(node);
+
+                Assert.AreEqual(CachePartitions(), parts.Length);
+            }
+            else
+            {
+                IList<int> allPrimaryParts = new List<int>();
+                IList<int> allBackupParts = new List<int>();
+                IList<int> allParts = new List<int>();
+
+                foreach(IClusterNode node in nodes) {
+                    int[] parts = aff.PrimaryPartitions(node);
+
+                    foreach (int part in parts)
+                        allPrimaryParts.Add(part);
+
+                    parts = aff.BackupPartitions(node);
+
+                    foreach (int part in parts)
+                        allBackupParts.Add(part);
+
+                    parts = aff.AllPartitions(node);
+
+                    foreach (int part in parts)
+                        allParts.Add(part);
+                }
+
+                Assert.AreEqual(CachePartitions(), allPrimaryParts.Count);
+                Assert.AreEqual(CachePartitions() * Backups(), allBackupParts.Count);
+                Assert.AreEqual(CachePartitions() * (Backups() + 1), allParts.Count);
+            }
+        }
+
+        [Test]
+        public void TestAffinityKey()
+        {
+            ICacheAffinity aff = Affinity();
+
+            Assert.AreEqual(10, aff.AffinityKey<int, int>(10));
+
+            Assert.AreEqual("string", aff.AffinityKey<string, string>("string"));
+        }
+
+        [Test]
+        public void TestMapToNode()
+        {
+            ICacheAffinity aff = Affinity();
+
+            const int key = 1;
+
+            IClusterNode node = aff.MapKeyToNode(key);
+
+            Assert.IsNotNull(node);
+
+            Assert.IsTrue(GetIgnite(0).Cluster.Nodes().Contains(node));
+
+            Assert.IsTrue(aff.IsPrimary(node, key));
+
+            Assert.IsTrue(aff.IsPrimaryOrBackup(node, key));
+
+            Assert.IsFalse(aff.IsBackup(node, key));
+
+            int part = aff.Partition(key);
+
+            IClusterNode partNode = aff.MapPartitionToNode(part);
+
+            Assert.AreEqual(node, partNode);
+        }
+
+        [Test]
+        public void TestMapToPrimaryAndBackups()
+        {
+            ICacheAffinity aff = Affinity();
+
+            const int key = 1;
+
+            IList<IClusterNode> nodes = aff.MapKeyToPrimaryAndBackups(key);
+
+            Assert.IsTrue(nodes.Count > 0);
+
+            for (int i = 0; i < nodes.Count; i++)
+            {
+                if (i == 0)
+                    Assert.IsTrue(aff.IsPrimary(nodes[i], key));
+                else
+                    Assert.IsTrue(aff.IsBackup(nodes[i], key));
+            }
+
+            int part = aff.Partition(key);
+
+            IList<IClusterNode> partNodes = aff.MapPartitionToPrimaryAndBackups(part);
+
+            Assert.AreEqual(nodes, partNodes);
+        }
+
+        [Test]
+        public void TestMapKeysToNodes()
+        {
+            ICacheAffinity aff = Affinity();
+
+            IList<int> keys = new List<int> {1, 2, 3};
+
+            IDictionary<IClusterNode, IList<int>> map = aff.MapKeysToNodes(keys);
+
+            Assert.IsTrue(map.Count > 0);
+
+            foreach (int key in keys)
+            {
+                IClusterNode primary = aff.MapKeyToNode(key);
+
+                Assert.IsTrue(map.ContainsKey(primary));
+
+                IList<int> nodeKeys = map[primary];
+
+                Assert.IsNotNull(nodeKeys);
+
+                Assert.IsTrue(nodeKeys.Contains(key));
+            }
+        }
+
+        [Test]
+        public void TestMapPartitionsToNodes()
+        {
+            ICacheAffinity aff = Affinity();
+
+            if (LocalCache())
+            {
+                IList<int> parts = new List<int> { 0 };
+
+                IDictionary<int, IClusterNode> map = aff.MapPartitionsToNodes(parts);
+
+                Assert.AreEqual(parts.Count, map.Count);
+
+                Assert.AreEqual(GetIgnite(0).Cluster.LocalNode, map[0]);
+            }
+            else
+            {
+                IList<int> parts = new List<int> { 1, 2, 3 };
+
+                IDictionary<int, IClusterNode> map = aff.MapPartitionsToNodes(parts);
+
+                Assert.AreEqual(parts.Count, map.Count);
+
+                foreach (int part in parts)
+                {
+                    Assert.IsTrue(map.ContainsKey(part));
+
+                    IClusterNode primary = aff.MapPartitionToNode(part);
+
+                    Assert.AreEqual(primary, map[part], "Wrong node for partition: " + part);
+                }
+            }
+        }
+
+        [Test]
+        public void TestKeepPortableFlag()
+        {
+            TestKeepPortableFlag(false);
+        }
+
+        [Test]
+        public void TestKeepPortableFlagAsync()
+        {
+            TestKeepPortableFlag(true);
+        }
+
+        [Test]
+        public void TestNearKeys()
+        {
+            if (!NearEnabled())
+                return;
+
+            const int count = 20;
+
+            var cache = Cache();
+            var aff = cache.Ignite.Affinity(cache.Name);
+            var node = cache.Ignite.Cluster.LocalNode;
+
+            for (int i = 0; i < count; i++)
+                cache.Put(i, -i - 1);
+
+            var nearKeys = Enumerable.Range(0, count).Where(x => !aff.IsPrimaryOrBackup(node, x)).ToArray();
+
+            var nearKeysString = nearKeys.Select(x => x.ToString()).Aggregate((x, y) => x + ", " + y);
+
+            Console.WriteLine("Near keys: " + nearKeysString);
+
+            foreach (var nearKey in nearKeys.Take(3))
+                Assert.AreNotEqual(0, cache.Get(nearKey));
+        }
+        
+        [Test]
+        public void TestSerializable()
+        {
+            var cache = Cache<int, TestSerializableObject>();
+
+            var obj = new TestSerializableObject {Name = "Vasya", Id = 128};
+
+            cache.Put(1, obj);
+
+            var resultObj = cache.Get(1);
+
+            Assert.AreEqual(obj, resultObj);
+        }
+
+        [Test]
+        public void TestInvoke()
+        {
+            TestInvoke(false);
+        }
+
+        [Test]
+        public void TestInvokeAsync()
+        {
+            TestInvoke(true);
+        }
+
+        private void TestInvoke(bool async)
+        {
+            TestInvoke<AddArgCacheEntryProcessor>(async);
+            TestInvoke<PortableAddArgCacheEntryProcessor>(async);
+
+            try
+            {
+                TestInvoke<NonSerializableCacheEntryProcessor>(async);
+                Assert.Fail();
+            }
+            catch (SerializationException)
+            {
+                // Expected
+            }
+        }
+
+        private void TestInvoke<T>(bool async) where T: AddArgCacheEntryProcessor, new()
+        {
+            var cache = async ? Cache().WithAsync().WrapAsync() : Cache();
+
+            cache.Clear();
+
+            const int key = 1;
+            const int value = 3;
+            const int arg = 5;
+
+            cache.Put(key, value);
+
+            // Existing entry
+            Assert.AreEqual(value + arg, cache.Invoke(key, new T(), arg));
+            Assert.AreEqual(value + arg, cache.Get(key));
+
+            // Non-existing entry
+            Assert.AreEqual(arg, cache.Invoke(10, new T {Exists = false}, arg));
+            Assert.AreEqual(arg, cache.Get(10));
+
+            // Remove entry
+            Assert.AreEqual(0, cache.Invoke(key, new T {Remove = true}, arg));
+            Assert.AreEqual(0, cache.Get(key));
+
+            // Test exceptions
+            AssertThrowsCacheEntryProcessorException(() => cache.Invoke(key, new T {ThrowErr = true}, arg));
+            AssertThrowsCacheEntryProcessorException(
+                () => cache.Invoke(key, new T {ThrowErrPortable = true}, arg));
+            AssertThrowsCacheEntryProcessorException(
+                () => cache.Invoke(key, new T { ThrowErrNonSerializable = true }, arg), "SerializationException");
+        }
+
+        private static void AssertThrowsCacheEntryProcessorException(Action action, string containsText = null)
+        {
+            try
+            {
+                action();
+
+                Assert.Fail();
+            }
+            catch (Exception ex)
+            {
+                Assert.IsInstanceOf<CacheEntryProcessorException>(ex);
+
+                if (string.IsNullOrEmpty(containsText))
+                    Assert.AreEqual(ex.InnerException.Message, AddArgCacheEntryProcessor.ExceptionText);
+                else
+                    Assert.IsTrue(ex.ToString().Contains(containsText));
+            }
+        }
+
+        [Test]
+        public void TestInvokeAll()
+        {
+            TestInvokeAll(false);
+        }
+
+        [Test]
+        public void TestInvokeAllAsync()
+        {
+            TestInvokeAll(true);
+        }
+
+        private void TestInvokeAll(bool async)
+        {
+            for (var i = 1; i < 10; i++)
+            {
+                TestInvokeAll<AddArgCacheEntryProcessor>(async, i);
+                TestInvokeAll<PortableAddArgCacheEntryProcessor>(async, i);
+
+                try
+                {
+                    TestInvokeAll<NonSerializableCacheEntryProcessor>(async, i);
+                    Assert.Fail();
+                }
+                catch (SerializationException)
+                {
+                    // Expected
+                }
+            }
+        }
+
+        public void TestInvokeAll<T>(bool async, int entryCount) where T : AddArgCacheEntryProcessor, new()
+        {
+            var cache = async ? Cache().WithAsync().WrapAsync() : Cache();
+
+            var entries = Enumerable.Range(1, entryCount).ToDictionary(x => x, x => x + 1);
+
+            cache.PutAll(entries);
+
+            const int arg = 5;
+
+            // Existing entries
+            var res = cache.InvokeAll(entries.Keys, new T(), arg);
+
+            var results = res.OrderBy(x => x.Key).Select(x => x.Value.Result);
+            var expectedResults = entries.OrderBy(x => x.Key).Select(x => x.Value + arg);
+            
+            Assert.IsTrue(results.SequenceEqual(expectedResults));
+
+            var resultEntries = cache.GetAll(entries.Keys);
+
+            Assert.IsTrue(resultEntries.All(x => x.Value == x.Key + 1 + arg));
+
+            // Remove entries
+            res = cache.InvokeAll(entries.Keys, new T {Remove = true}, arg);
+
+            Assert.IsTrue(res.All(x => x.Value.Result == 0));
+            Assert.AreEqual(0, cache.GetAll(entries.Keys).Count);
+
+            // Non-existing entries
+            res = cache.InvokeAll(entries.Keys, new T {Exists = false}, arg);
+
+            Assert.IsTrue(res.All(x => x.Value.Result == arg));
+            Assert.IsTrue(cache.GetAll(entries.Keys).All(x => x.Value == arg)); 
+
+            // Test exceptions
+            var errKey = entries.Keys.Reverse().Take(5).Last();
+
+            TestInvokeAllException(cache, entries, new T { ThrowErr = true, ThrowOnKey = errKey }, arg, errKey);
+            TestInvokeAllException(cache, entries, new T { ThrowErrPortable = true, ThrowOnKey = errKey }, 
+                arg, errKey);
+            TestInvokeAllException(cache, entries, new T { ThrowErrNonSerializable = true, ThrowOnKey = errKey }, 
+                arg, errKey, "SerializationException");
+
+        }
+
+        private static void TestInvokeAllException<T>(ICache<int, int> cache, Dictionary<int, int> entries, 
+            T processor, int arg, int errKey, string exceptionText = null) where T : AddArgCacheEntryProcessor
+        {
+            var res = cache.InvokeAll(entries.Keys, processor, arg);
+
+            foreach (var procRes in res)
+            {
+                if (procRes.Key == errKey)
+                    // ReSharper disable once AccessToForEachVariableInClosure
+                    AssertThrowsCacheEntryProcessorException(() => { var x = procRes.Value.Result; }, exceptionText);
+                else
+                    Assert.Greater(procRes.Value.Result, 0);
+            }
+        }
+
+        /// <summary>
+        /// Test skip-store semantics.
+        /// </summary>
+        [Test]
+        public void TestSkipStore()
+        {
+            CacheProxyImpl<int, int> cache = (CacheProxyImpl<int, int>)Cache();
+
+            Assert.IsFalse(cache.SkipStore);
+
+            // Ensure correct flag set.
+ 

<TRUNCATED>