From 84bdd65b26f9b8ab1fb07f0da36c4a2553e608a9 Mon Sep 17 00:00:00 2001 From: Alex Feinberg Date: Wed, 29 Dec 2010 23:17:15 -0800 Subject: [PATCH] Implement caching StoreClientFactory, which required adding equals/hashCode to the InconsistencyResolvers --- .../client/CachingStoreClientFactory.java | 85 +++++++++++++++++++ .../ArbitraryInconsistencyResolver.java | 11 +++ .../voldemort/versioning/ChainedResolver.java | 17 ++++ .../FailingInconsistencyResolver.java | 13 +++ .../MergingInconsistencyResolver.java | 13 +++ .../TimeBasedInconsistencyResolver.java | 13 +++ .../VectorClockInconsistencyResolver.java | 12 +++ .../client/CachingStoreClientFactoryTest.java | 49 +++++++++++ 8 files changed, 213 insertions(+) create mode 100644 src/java/voldemort/client/CachingStoreClientFactory.java create mode 100644 test/unit/voldemort/client/CachingStoreClientFactoryTest.java diff --git a/src/java/voldemort/client/CachingStoreClientFactory.java b/src/java/voldemort/client/CachingStoreClientFactory.java new file mode 100644 index 0000000000..e7430650ff --- /dev/null +++ b/src/java/voldemort/client/CachingStoreClientFactory.java @@ -0,0 +1,85 @@ +/* + * Copyright 2008-2010 LinkedIn, Inc + * + * Licensed 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. + */ + +package voldemort.client; + +import voldemort.cluster.failuredetector.FailureDetector; +import voldemort.store.Store; +import voldemort.utils.Pair; +import voldemort.versioning.InconsistencyResolver; +import voldemort.versioning.Versioned; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * A wrapper for a store {@link StoreClientFactory} which caches requests + * to getStoreClient + * + */ +public class CachingStoreClientFactory implements StoreClientFactory { + + private final StoreClientFactory inner; + private final ConcurrentMap, StoreClient> cache; + + public CachingStoreClientFactory(StoreClientFactory inner) { + this.inner = inner; + this.cache = new ConcurrentHashMap, StoreClient>(); + } + + + @SuppressWarnings("unchecked") + public StoreClient getStoreClient(String storeName) { + Pair key = Pair.create(storeName, null); + StoreClient retVal = cache.get(key); + if(retVal == null) { + retVal = inner.getStoreClient(storeName); + cache.putIfAbsent(key, retVal); + } + + return retVal; + } + + @SuppressWarnings("unchecked") + public StoreClient getStoreClient(String storeName, + InconsistencyResolver> resolver) { + Pair key = Pair.create(storeName, (Object) resolver); + StoreClient retVal = cache.get(key); + if(retVal == null) { + retVal = inner.getStoreClient(storeName, resolver); + cache.putIfAbsent(key, retVal); + } + + return retVal; + } + + public Store getRawStore(String storeName, + InconsistencyResolver> resolver) { + return inner.getRawStore(storeName, resolver); + } + + public void close() { + try { + cache.clear(); + } finally { + inner.close(); + } + } + + public FailureDetector getFailureDetector() { + return inner.getFailureDetector(); + } +} diff --git a/src/java/voldemort/versioning/ArbitraryInconsistencyResolver.java b/src/java/voldemort/versioning/ArbitraryInconsistencyResolver.java index da60f6fff2..4d60cd9216 100644 --- a/src/java/voldemort/versioning/ArbitraryInconsistencyResolver.java +++ b/src/java/voldemort/versioning/ArbitraryInconsistencyResolver.java @@ -41,4 +41,15 @@ public List resolveConflicts(List values) { return Collections.singletonList(values.get(0)); } + @Override + public boolean equals(Object o) { + if(o == this) + return true; + return (o instanceof ArbitraryInconsistencyResolver); + } + + @Override + public int hashCode() { + return 31 * this.getClass().hashCode(); + } } diff --git a/src/java/voldemort/versioning/ChainedResolver.java b/src/java/voldemort/versioning/ChainedResolver.java index fd0acc11f7..b8e2cbd796 100644 --- a/src/java/voldemort/versioning/ChainedResolver.java +++ b/src/java/voldemort/versioning/ChainedResolver.java @@ -48,4 +48,21 @@ public List resolveConflicts(List items) { return items; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ChainedResolver that = (ChainedResolver) o; + + if (resolvers != null ? !resolvers.equals(that.resolvers) : that.resolvers != null) + return false; + + return true; + } + + @Override + public int hashCode() { + return resolvers != null ? resolvers.hashCode() : 0; + } } diff --git a/src/java/voldemort/versioning/FailingInconsistencyResolver.java b/src/java/voldemort/versioning/FailingInconsistencyResolver.java index dfa91f605b..11f271bd50 100644 --- a/src/java/voldemort/versioning/FailingInconsistencyResolver.java +++ b/src/java/voldemort/versioning/FailingInconsistencyResolver.java @@ -31,4 +31,17 @@ public List resolveConflicts(List items) { else return items; } + + + @Override + public boolean equals(Object o) { + if(o == this) + return true; + return (o instanceof FailingInconsistencyResolver); + } + + @Override + public int hashCode() { + return 31 * this.getClass().hashCode(); + } } diff --git a/src/java/voldemort/versioning/MergingInconsistencyResolver.java b/src/java/voldemort/versioning/MergingInconsistencyResolver.java index c8339cc942..91d95487e1 100644 --- a/src/java/voldemort/versioning/MergingInconsistencyResolver.java +++ b/src/java/voldemort/versioning/MergingInconsistencyResolver.java @@ -49,4 +49,17 @@ public List> resolveConflicts(List> items) { return Collections.singletonList(new Versioned(merged, clock)); } } + + + @Override + public boolean equals(Object o) { + if(o == this) + return true; + return (o instanceof MergingInconsistencyResolver); + } + + @Override + public int hashCode() { + return 31 * this.getClass().hashCode(); + } } diff --git a/src/java/voldemort/versioning/TimeBasedInconsistencyResolver.java b/src/java/voldemort/versioning/TimeBasedInconsistencyResolver.java index ded077bc55..3b14e91e09 100644 --- a/src/java/voldemort/versioning/TimeBasedInconsistencyResolver.java +++ b/src/java/voldemort/versioning/TimeBasedInconsistencyResolver.java @@ -43,4 +43,17 @@ public List> resolveConflicts(List> items) { return Collections.singletonList(max); } } + + + @Override + public boolean equals(Object o) { + if(o == this) + return true; + return (o instanceof TimeBasedInconsistencyResolver); + } + + @Override + public int hashCode() { + return 31 * this.getClass().hashCode(); + } } diff --git a/src/java/voldemort/versioning/VectorClockInconsistencyResolver.java b/src/java/voldemort/versioning/VectorClockInconsistencyResolver.java index 7411a0786e..24fdeb4a9c 100644 --- a/src/java/voldemort/versioning/VectorClockInconsistencyResolver.java +++ b/src/java/voldemort/versioning/VectorClockInconsistencyResolver.java @@ -54,4 +54,16 @@ public List> resolveConflicts(List> items) { } return newItems; } + + @Override + public boolean equals(Object o) { + if(o == this) + return true; + return (o instanceof VectorClockInconsistencyResolver); + } + + @Override + public int hashCode() { + return 31 * this.getClass().hashCode(); + } } diff --git a/test/unit/voldemort/client/CachingStoreClientFactoryTest.java b/test/unit/voldemort/client/CachingStoreClientFactoryTest.java new file mode 100644 index 0000000000..862deef097 --- /dev/null +++ b/test/unit/voldemort/client/CachingStoreClientFactoryTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2008-2010 LinkedIn, Inc + * + * Licensed 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. + */ + +package voldemort.client; + +import org.junit.Test; +import voldemort.serialization.StringSerializer; +import voldemort.versioning.TimeBasedInconsistencyResolver; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +public class CachingStoreClientFactoryTest { + + @Test + public void testCaching() { + StoreClientFactory inner = new MockStoreClientFactory(new StringSerializer(), + new StringSerializer(), + null); + StoreClientFactory spyFactory = spy(inner); + StoreClientFactory cachingFactory = new CachingStoreClientFactory(spyFactory); + TimeBasedInconsistencyResolver resolver = new TimeBasedInconsistencyResolver(); + + when(spyFactory.getStoreClient(anyString())).thenCallRealMethod(); + when(spyFactory.getStoreClient(anyString(), eq(resolver))).thenCallRealMethod(); + + for(int i = 0; i < 10; i++) { + assertNotNull(cachingFactory.getStoreClient("foo")); + assertNotNull(cachingFactory.getStoreClient("foo", resolver)); + } + + verify(spyFactory, times(1)).getStoreClient("foo"); + verify(spyFactory, times(2)).getStoreClient("foo", resolver); + } +}