diff --git a/gerrit-cache-h2/BUCK b/gerrit-cache-h2/BUCK index c7a2221e23..37c8b96388 100644 --- a/gerrit-cache-h2/BUCK +++ b/gerrit-cache-h2/BUCK @@ -13,3 +13,16 @@ java_library( ], visibility = ['PUBLIC'], ) + +java_test( + name = 'tests', + srcs = glob(['src/test/java/**/*.java']), + deps = [ + ':cache-h2', + '//gerrit-server:server', + '//lib:guava', + '//lib:h2', + '//lib/guice:guice', + '//lib:junit', + ], +) diff --git a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java index 2b8be62ef1..289c8cc29f 100644 --- a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java +++ b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java @@ -46,6 +46,7 @@ import java.util.Calendar; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; @@ -127,6 +128,12 @@ public class H2CacheImpl extends AbstractLoadingCache implements throw new UnsupportedOperationException(); } + @Override + public V get(K key, Callable valueLoader) + throws ExecutionException { + return mem.get(key, new LoadingCallable(key, valueLoader)).value; + } + @Override public void put(final K key, V val) { final ValueHolder h = new ValueHolder<>(val); @@ -250,6 +257,36 @@ public class H2CacheImpl extends AbstractLoadingCache implements } } + private class LoadingCallable implements Callable> { + private final K key; + private final Callable loader; + + LoadingCallable(K key, Callable loader) { + this.key = key; + this.loader = loader; + } + + @Override + public ValueHolder call() throws Exception { + if (store.mightContain(key)) { + ValueHolder h = store.getIfPresent(key); + if (h != null) { + return h; + } + } + + final ValueHolder h = new ValueHolder(loader.call()); + h.created = TimeUtil.nowMs(); + executor.execute(new Runnable() { + @Override + public void run() { + store.put(key, h); + } + }); + return h; + } + } + private static class KeyType { String columnType() { return "OTHER"; diff --git a/gerrit-cache-h2/src/test/java/com/google/gerrit/server/cache/h2/H2CacheTest.java b/gerrit-cache-h2/src/test/java/com/google/gerrit/server/cache/h2/H2CacheTest.java new file mode 100644 index 0000000000..3b7e43613c --- /dev/null +++ b/gerrit-cache-h2/src/test/java/com/google/gerrit/server/cache/h2/H2CacheTest.java @@ -0,0 +1,82 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// 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 com.google.gerrit.server.cache.h2; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.gerrit.server.cache.h2.H2CacheImpl.SqlStore; +import com.google.gerrit.server.cache.h2.H2CacheImpl.ValueHolder; +import com.google.inject.TypeLiteral; + +import org.junit.Before; +import org.junit.Test; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; + +public class H2CacheTest { + private static int dbCnt; + + private Cache> mem; + private H2CacheImpl impl; + + @Before + public void setUp() { + mem = CacheBuilder.newBuilder().build(); + + TypeLiteral keyType = new TypeLiteral() {}; + SqlStore store = new SqlStore<>( + "jdbc:h2:mem:" + "Test_" + (++dbCnt), + keyType, + 1 << 20, + 0); + impl = + new H2CacheImpl<>(MoreExecutors.directExecutor(), store, keyType, mem); + } + + @Test + public void get() throws ExecutionException { + assertNull(impl.getIfPresent("foo")); + + final AtomicBoolean called = new AtomicBoolean(); + assertTrue(impl.get("foo", new Callable() { + @Override + public Boolean call() throws Exception { + called.set(true); + return true; + } + })); + assertTrue("used Callable", called.get()); + assertTrue("exists in cache", impl.getIfPresent("foo")); + mem.invalidate("foo"); + assertTrue("exists on disk", impl.getIfPresent("foo")); + + called.set(false); + assertTrue(impl.get("foo", new Callable() { + @Override + public Boolean call() throws Exception { + called.set(true); + return true; + } + })); + assertFalse("did not invoke Callable", called.get()); + } +} \ No newline at end of file