| /* |
| * 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.Store |
| { |
| using System; |
| using System.Collections; |
| using System.Collections.Generic; |
| using System.IO; |
| using System.Linq; |
| using Apache.Ignite.Core.Binary; |
| using Apache.Ignite.Core.Cache; |
| using Apache.Ignite.Core.Cache.Configuration; |
| using Apache.Ignite.Core.Cache.Store; |
| using Apache.Ignite.Core.Common; |
| using Apache.Ignite.Core.Impl; |
| using NUnit.Framework; |
| |
| /// <summary> |
| /// Tests cache store functionality. |
| /// </summary> |
| public class CacheStoreTest |
| { |
| /** */ |
| private const string BinaryStoreCacheName = "binary_store"; |
| |
| /** */ |
| private const string ObjectStoreCacheName = "object_store"; |
| |
| /** */ |
| private const string CustomStoreCacheName = "custom_store"; |
| |
| /** */ |
| private const string TemplateStoreCacheName = "template_store*"; |
| |
| /// <summary> |
| /// Fixture set up. |
| /// </summary> |
| [TestFixtureSetUp] |
| public virtual void BeforeTests() |
| { |
| Restart(); |
| } |
| |
| /// <summary> |
| /// Fixture tear down. |
| /// </summary> |
| [TestFixtureTearDown] |
| public void AfterTests() |
| { |
| try |
| { |
| // 3 stores are expected in HandleRegistry. |
| TestUtils.AssertHandleRegistryHasItems(Ignition.GetIgnite(), 3, 1000); |
| } |
| finally |
| { |
| Ignition.StopAll(true); |
| } |
| } |
| |
| |
| /// <summary> |
| /// Stop all instances, set and start a test instance. |
| /// </summary> |
| private void Restart() |
| { |
| Ignition.StopAll(true); |
| |
| Ignition.Start(new IgniteConfiguration(TestUtils.GetTestConfiguration()) |
| { |
| IgniteInstanceName = GridName, |
| SpringConfigUrl = Path.Combine("Config", "native-client-test-cache-store.xml"), |
| }); |
| } |
| |
| /// <summary> |
| /// Test tear down. |
| /// </summary> |
| [TearDown] |
| public void AfterTest() |
| { |
| CacheTestStore.Reset(); |
| |
| var cache = GetCache(); |
| |
| cache.Clear(); |
| |
| Assert.IsTrue(cache.IsEmpty(), |
| "Cache is not empty: " + |
| string.Join(", ", cache.Select(x => string.Format("[{0}:{1}]", x.Key, x.Value)))); |
| |
| TestUtils.AssertHandleRegistryHasItems(300, 3, Ignition.GetIgnite(GridName)); |
| } |
| |
| /// <summary> |
| /// Tests that simple cache loading works and exceptions are propagated properly. |
| /// </summary> |
| [Test] |
| public void TestLoadCache() |
| { |
| var cache = GetCache(); |
| |
| Assert.AreEqual(0, cache.GetSize()); |
| |
| cache.LoadCache(new CacheEntryFilter(), 100, 10); |
| |
| Assert.AreEqual(5, cache.GetSize()); |
| |
| for (int i = 105; i < 110; i++) |
| Assert.AreEqual("val_" + i, cache.Get(i)); |
| |
| // Test exception in filter |
| Assert.Throws<CacheStoreException>(() => cache.LoadCache(new ExceptionalEntryFilter(), 100, 10)); |
| |
| // Test exception in store |
| CacheTestStore.ThrowError = true; |
| CheckCustomStoreError(Assert.Throws<CacheStoreException>(() => |
| cache.LoadCache(new CacheEntryFilter(), 100, 10)).InnerException); |
| } |
| |
| /// <summary> |
| /// Tests cache loading in local mode. |
| /// </summary> |
| [Test] |
| public void TestLocalLoadCache() |
| { |
| var cache = GetCache(); |
| |
| Assert.AreEqual(0, cache.GetSize()); |
| |
| cache.LocalLoadCache(new CacheEntryFilter(), 100, 10); |
| |
| Assert.AreEqual(5, cache.GetSize()); |
| |
| for (int i = 105; i < 110; i++) |
| Assert.AreEqual("val_" + i, cache.Get(i)); |
| } |
| |
| /// <summary> |
| /// Tests that object metadata propagates properly during cache loading. |
| /// </summary> |
| [Test] |
| public void TestLoadCacheMetadata() |
| { |
| CacheTestStore.LoadObjects = true; |
| |
| var cache = GetCache(); |
| |
| Assert.AreEqual(0, cache.GetSize()); |
| |
| cache.LocalLoadCache(null, 0, 3); |
| |
| Assert.AreEqual(3, cache.GetSize()); |
| |
| var meta = cache.WithKeepBinary<Key, IBinaryObject>().Get(new Key(0)).GetBinaryType(); |
| |
| Assert.NotNull(meta); |
| |
| Assert.AreEqual("Apache.Ignite.Core.Tests.Cache.Store.Value", meta.TypeName); |
| } |
| |
| /// <summary> |
| /// Tests asynchronous cache load. |
| /// </summary> |
| [Test] |
| public void TestLoadCacheAsync() |
| { |
| var cache = GetCache(); |
| |
| Assert.AreEqual(0, cache.GetSize()); |
| Assert.AreEqual(0, cache.GetSizeLongAsync().Result); |
| |
| cache.LocalLoadCacheAsync(new CacheEntryFilter(), 100, 10).Wait(); |
| |
| Assert.AreEqual(5, cache.GetSizeAsync().Result); |
| Assert.AreEqual(5, cache.GetSizeLongAsync().Result); |
| |
| for (int i = 105; i < 110; i++) |
| { |
| Assert.AreEqual("val_" + i, cache.GetAsync(i).Result); |
| } |
| |
| // Test errors |
| CacheTestStore.ThrowError = true; |
| CheckCustomStoreError( |
| Assert.Throws<AggregateException>( |
| () => cache.LocalLoadCacheAsync(new CacheEntryFilter(), 100, 10).Wait()) |
| .InnerException); |
| } |
| |
| /// <summary> |
| /// Tests write-through and read-through behavior. |
| /// </summary> |
| [Test] |
| public void TestPutLoad() |
| { |
| var cache = GetCache(); |
| |
| cache.Put(1, "val"); |
| |
| IDictionary map = GetStoreMap(); |
| |
| Assert.AreEqual(1, map.Count); |
| |
| // TODO: IGNITE-4535 |
| //cache.LocalEvict(new[] { 1 }); |
| |
| //Assert.AreEqual(0, cache.GetSize(CachePeekMode.All)); |
| |
| Assert.AreEqual("val", cache.Get(1)); |
| |
| Assert.AreEqual(1, cache.GetSize()); |
| } |
| |
| /// <summary> |
| /// Tests that exceptions from user code are propagated properly. |
| /// </summary> |
| [Test] |
| public void TestExceptions() |
| { |
| var cache = GetCache(); |
| |
| cache.Put(1, "val"); |
| |
| CacheTestStore.ThrowError = true; |
| |
| var ex = Assert.Throws<CacheStoreException>(() => cache.Put(-2, "fail")); |
| |
| Assert.IsTrue(ex.ToString().Contains( |
| "at Apache.Ignite.Core.Tests.Cache.Store.CacheTestStore.ThrowIfNeeded")); // Check proper stack trace. |
| |
| Assert.IsNotNull(ex.InnerException); // RollbackException. |
| |
| var javaEx = ex.InnerException.InnerException as JavaException; |
| Assert.IsNotNull(javaEx); |
| |
| CheckCustomStoreError(javaEx.InnerException); |
| |
| // TODO: IGNITE-4535 |
| //cache.LocalEvict(new[] {1}); |
| //CheckCustomStoreError(Assert.Throws<CacheStoreException>(() => cache.Get(1)).InnerException); |
| |
| CacheTestStore.ThrowError = false; |
| |
| cache.Remove(1); |
| } |
| |
| /// <summary> |
| /// Tests that exceptions from CacheStoreFactory are propagated properly. |
| /// </summary> |
| [Test] |
| public void TestFailedCacheStoreException() |
| { |
| try |
| { |
| var ccfg = new CacheConfiguration("CacheWithFailedStore") |
| { |
| CacheStoreFactory = new FailedCacheStoreFactory(), |
| ReadThrough = true |
| }; |
| |
| Assert.Throws<CacheException>(() => Ignition.GetIgnite(GridName).GetOrCreateCache<int, int>(ccfg)); |
| } |
| finally |
| { |
| // After failed cache grid is in ivalid state. Should be restarted. |
| Restart(); |
| } |
| |
| } |
| |
| [Test] |
| [Ignore("IGNITE-4657")] |
| public void TestExceptionsNoRemove() |
| { |
| var cache = GetCache(); |
| |
| cache.Put(1, "val"); |
| |
| CacheTestStore.ThrowError = true; |
| CheckCustomStoreError(Assert.Throws<CacheStoreException>(() => cache.Put(-2, "fail")).InnerException); |
| |
| cache.LocalEvict(new[] {1}); |
| CheckCustomStoreError(Assert.Throws<CacheStoreException>(() => cache.Get(1)).InnerException); |
| } |
| |
| /// <summary> |
| /// Tests write-through and read-through behavior with binarizable values. |
| /// </summary> |
| [Test] |
| public void TestPutLoadBinarizable() |
| { |
| var cache = GetBinaryStoreCache<int, Value>(); |
| |
| cache.Put(1, new Value(1)); |
| |
| IDictionary map = GetStoreMap(); |
| |
| Assert.AreEqual(1, map.Count); |
| |
| IBinaryObject v = (IBinaryObject)map[1]; |
| |
| Assert.AreEqual(1, v.GetField<int>("_idx")); |
| |
| // TODO: IGNITE-4535 |
| //cache.LocalEvict(new[] { 1 }); |
| |
| //Assert.AreEqual(0, cache.GetSize()); |
| |
| Assert.AreEqual(1, cache.Get(1).Index); |
| |
| Assert.AreEqual(1, cache.GetSize()); |
| } |
| |
| /// <summary> |
| /// Tests write-through and read-through behavior with storeKeepBinary=false. |
| /// </summary> |
| [Test] |
| public void TestPutLoadObjects() |
| { |
| var cache = GetObjectStoreCache<int, Value>(); |
| |
| cache.Put(1, new Value(1)); |
| |
| IDictionary map = GetStoreMap(); |
| |
| Assert.AreEqual(1, map.Count); |
| |
| Value v = (Value)map[1]; |
| |
| Assert.AreEqual(1, v.Index); |
| |
| // TODO: IGNITE-4535 |
| //cache.LocalEvict(new[] { 1 }); |
| |
| //Assert.AreEqual(0, cache.GetSize()); |
| |
| Assert.AreEqual(1, cache.Get(1).Index); |
| |
| Assert.AreEqual(1, cache.GetSize()); |
| } |
| |
| /// <summary> |
| /// Tests cache store LoadAll functionality. |
| /// </summary> |
| [Test] |
| public void TestPutLoadAll() |
| { |
| var putMap = new Dictionary<int, string>(); |
| |
| for (int i = 0; i < 10; i++) |
| putMap.Add(i, "val_" + i); |
| |
| var cache = GetCache(); |
| |
| cache.PutAll(putMap); |
| |
| IDictionary map = GetStoreMap(); |
| |
| Assert.AreEqual(10, map.Count); |
| |
| for (int i = 0; i < 10; i++) |
| Assert.AreEqual("val_" + i, map[i]); |
| |
| cache.Clear(); |
| |
| Assert.AreEqual(0, cache.GetSize()); |
| |
| ICollection<int> keys = new List<int>(); |
| |
| for (int i = 0; i < 10; i++) |
| keys.Add(i); |
| |
| IDictionary<int, string> loaded = cache.GetAll(keys).ToDictionary(x => x.Key, x => x.Value); |
| |
| Assert.AreEqual(10, loaded.Count); |
| |
| for (int i = 0; i < 10; i++) |
| Assert.AreEqual("val_" + i, loaded[i]); |
| |
| Assert.AreEqual(10, cache.GetSize()); |
| } |
| |
| /// <summary> |
| /// Tests cache store removal. |
| /// </summary> |
| [Test] |
| public void TestRemove() |
| { |
| var cache = GetCache(); |
| |
| for (int i = 0; i < 10; i++) |
| cache.Put(i, "val_" + i); |
| |
| IDictionary map = GetStoreMap(); |
| |
| Assert.AreEqual(10, map.Count); |
| |
| for (int i = 0; i < 5; i++) |
| cache.Remove(i); |
| |
| Assert.AreEqual(5, map.Count); |
| |
| for (int i = 5; i < 10; i++) |
| Assert.AreEqual("val_" + i, map[i]); |
| } |
| |
| /// <summary> |
| /// Tests cache store removal. |
| /// </summary> |
| [Test] |
| public void TestRemoveAll() |
| { |
| var cache = GetCache(); |
| |
| for (int i = 0; i < 10; i++) |
| cache.Put(i, "val_" + i); |
| |
| IDictionary map = GetStoreMap(); |
| |
| Assert.AreEqual(10, map.Count); |
| |
| cache.RemoveAll(new List<int> { 0, 1, 2, 3, 4 }); |
| |
| Assert.AreEqual(5, map.Count); |
| |
| for (int i = 5; i < 10; i++) |
| Assert.AreEqual("val_" + i, map[i]); |
| } |
| |
| /// <summary> |
| /// Tests cache store with transactions. |
| /// </summary> |
| [Test] |
| public void TestTx() |
| { |
| var cache = GetCache(); |
| |
| using (var tx = cache.Ignite.GetTransactions().TxStart()) |
| { |
| tx.AddMeta("meta", 100); |
| |
| cache.Put(1, "val"); |
| |
| tx.Commit(); |
| } |
| |
| IDictionary map = GetStoreMap(); |
| |
| Assert.AreEqual(1, map.Count); |
| |
| Assert.AreEqual("val", map[1]); |
| } |
| |
| /// <summary> |
| /// Tests multithreaded cache loading. |
| /// </summary> |
| [Test] |
| public void TestLoadCacheMultithreaded() |
| { |
| CacheTestStore.LoadMultithreaded = true; |
| |
| var cache = GetCache(); |
| |
| Assert.AreEqual(0, cache.GetSize()); |
| |
| cache.LocalLoadCache(null, 0, null); |
| |
| Assert.AreEqual(1000, cache.GetSize()); |
| |
| for (int i = 0; i < 1000; i++) |
| Assert.AreEqual("val_" + i, cache.Get(i)); |
| } |
| |
| /// <summary> |
| /// Tests that cache store property values are propagated from Spring XML. |
| /// </summary> |
| [Test] |
| public void TestCustomStoreProperties() |
| { |
| var cache = GetCustomStoreCache(); |
| Assert.IsNotNull(cache); |
| |
| Assert.AreEqual(42, CacheTestStore.intProperty); |
| Assert.AreEqual("String value", CacheTestStore.stringProperty); |
| } |
| |
| /// <summary> |
| /// Tests cache store with dynamically started cache. |
| /// </summary> |
| [Test] |
| public void TestDynamicStoreStart() |
| { |
| var grid = Ignition.GetIgnite(GridName); |
| var reg = ((Ignite) grid).HandleRegistry; |
| var handleCount = reg.Count; |
| |
| var cache = GetTemplateStoreCache(); |
| Assert.IsNotNull(cache); |
| |
| cache.Put(1, cache.Name); |
| Assert.AreEqual(cache.Name, CacheTestStore.Map[1]); |
| |
| Assert.AreEqual(handleCount + 1, reg.Count); |
| grid.DestroyCache(cache.Name); |
| Assert.AreEqual(handleCount, reg.Count); |
| } |
| |
| /// <summary> |
| /// Tests the load all. |
| /// </summary> |
| [Test] |
| public void TestLoadAll([Values(true, false)] bool isAsync) |
| { |
| var cache = GetCache(); |
| |
| var loadAll = isAsync |
| ? (Action<IEnumerable<int>, bool>) ((x, y) => { cache.LoadAllAsync(x, y).Wait(); }) |
| : cache.LoadAll; |
| |
| Assert.AreEqual(0, cache.GetSize()); |
| |
| loadAll(Enumerable.Range(105, 5), false); |
| |
| Assert.AreEqual(5, cache.GetSize()); |
| |
| for (int i = 105; i < 110; i++) |
| Assert.AreEqual("val_" + i, cache[i]); |
| |
| // Test overwrite |
| cache[105] = "42"; |
| |
| cache.LocalEvict(new[] { 105 }); |
| loadAll(new[] {105}, false); |
| Assert.AreEqual("42", cache[105]); |
| |
| loadAll(new[] {105, 106}, true); |
| Assert.AreEqual("val_105", cache[105]); |
| Assert.AreEqual("val_106", cache[106]); |
| } |
| |
| /// <summary> |
| /// Tests the argument passing to LoadCache method. |
| /// </summary> |
| [Test] |
| public void TestArgumentPassing() |
| { |
| var cache = GetBinaryStoreCache<object, object>(); |
| |
| Action<object> checkValue = o => |
| { |
| cache.Clear(); |
| Assert.AreEqual(0, cache.GetSize()); |
| cache.LoadCache(null, null, 1, o); |
| Assert.AreEqual(o, cache[1]); |
| }; |
| |
| // Null. |
| cache.LoadCache(null, null); |
| Assert.AreEqual(0, cache.GetSize()); |
| |
| // Empty args array. |
| cache.LoadCache(null); |
| Assert.AreEqual(0, cache.GetSize()); |
| |
| // Simple types. |
| checkValue(1); |
| checkValue(new[] {1, 2, 3}); |
| |
| checkValue("1"); |
| checkValue(new[] {"1", "2"}); |
| |
| checkValue(Guid.NewGuid()); |
| checkValue(new[] {Guid.NewGuid(), Guid.NewGuid()}); |
| |
| checkValue(DateTime.Now); |
| checkValue(new[] {DateTime.Now, DateTime.UtcNow}); |
| |
| // Collections. |
| checkValue(new ArrayList {1, "2", 3.3}); |
| checkValue(new List<int> {1, 2}); |
| checkValue(new Dictionary<int, string> {{1, "foo"}}); |
| } |
| |
| /// <summary> |
| /// Get's grid name for this test. |
| /// </summary> |
| /// <value>Grid name.</value> |
| protected virtual string GridName |
| { |
| get { return null; } |
| } |
| |
| /// <summary> |
| /// Gets the store map. |
| /// </summary> |
| private static IDictionary GetStoreMap() |
| { |
| return CacheTestStore.Map; |
| } |
| |
| /// <summary> |
| /// Gets the cache. |
| /// </summary> |
| private ICache<int, string> GetCache() |
| { |
| return GetBinaryStoreCache<int, string>(); |
| } |
| |
| /// <summary> |
| /// Gets the binary store cache. |
| /// </summary> |
| private ICache<TK, TV> GetBinaryStoreCache<TK, TV>() |
| { |
| return Ignition.GetIgnite(GridName).GetCache<TK, TV>(BinaryStoreCacheName); |
| } |
| |
| /// <summary> |
| /// Gets the object store cache. |
| /// </summary> |
| private ICache<TK, TV> GetObjectStoreCache<TK, TV>() |
| { |
| return Ignition.GetIgnite(GridName).GetCache<TK, TV>(ObjectStoreCacheName); |
| } |
| |
| /// <summary> |
| /// Gets the custom store cache. |
| /// </summary> |
| private ICache<int, string> GetCustomStoreCache() |
| { |
| return Ignition.GetIgnite(GridName).GetCache<int, string>(CustomStoreCacheName); |
| } |
| |
| /// <summary> |
| /// Gets the template store cache. |
| /// </summary> |
| private ICache<int, string> GetTemplateStoreCache() |
| { |
| var cacheName = TemplateStoreCacheName.Replace("*", Guid.NewGuid().ToString()); |
| |
| return Ignition.GetIgnite(GridName).GetOrCreateCache<int, string>(cacheName); |
| } |
| |
| /// <summary> |
| /// Checks the custom store error. |
| /// </summary> |
| private static void CheckCustomStoreError(Exception err) |
| { |
| var customErr = err.GetBaseException() as CacheTestStore.CustomStoreException; |
| |
| Assert.IsNotNull(customErr); |
| Assert.AreEqual(customErr.Message, customErr.Details); |
| } |
| } |
| |
| /// <summary> |
| /// Cache key. |
| /// </summary> |
| internal class Key |
| { |
| /** */ |
| private readonly int _idx; |
| |
| /// <summary> |
| /// Initializes a new instance of the <see cref="Key"/> class. |
| /// </summary> |
| public Key(int idx) |
| { |
| _idx = idx; |
| } |
| |
| /** <inheritdoc /> */ |
| public override bool Equals(object obj) |
| { |
| if (obj == null || obj.GetType() != GetType()) |
| return false; |
| |
| return ((Key)obj)._idx == _idx; |
| } |
| |
| /** <inheritdoc /> */ |
| public override int GetHashCode() |
| { |
| return _idx; |
| } |
| } |
| |
| /// <summary> |
| /// Cache value. |
| /// </summary> |
| internal class Value |
| { |
| /** */ |
| private readonly int _idx; |
| |
| /// <summary> |
| /// Initializes a new instance of the <see cref="Value"/> class. |
| /// </summary> |
| public Value(int idx) |
| { |
| _idx = idx; |
| } |
| |
| /// <summary> |
| /// Gets the index. |
| /// </summary> |
| public int Index |
| { |
| get { return _idx; } |
| } |
| } |
| |
| /// <summary> |
| /// Cache entry predicate. |
| /// </summary> |
| [Serializable] |
| public class CacheEntryFilter : ICacheEntryFilter<int, string> |
| { |
| /** <inheritdoc /> */ |
| public bool Invoke(ICacheEntry<int, string> entry) |
| { |
| return entry.Key >= 105; |
| } |
| } |
| |
| /// <summary> |
| /// Cache entry predicate that throws an exception. |
| /// </summary> |
| [Serializable] |
| public class ExceptionalEntryFilter : ICacheEntryFilter<int, string> |
| { |
| /** <inheritdoc /> */ |
| public bool Invoke(ICacheEntry<int, string> entry) |
| { |
| throw new Exception("Expected exception in ExceptionalEntryFilter"); |
| } |
| } |
| |
| /// <summary> |
| /// Test factory. |
| /// </summary> |
| [Serializable] |
| public class FailedCacheStoreFactory : IFactory<ICacheStore> |
| { |
| /// <summary> |
| /// Creates an instance of the cache store. Throws an exception during creation. |
| /// </summary> |
| public ICacheStore CreateInstance() |
| { |
| throw new Exception("FailedCacheStoreFactory.CreateInstance exception"); |
| } |
| } |
| } |