diff --git a/protosimplestore/src/main/java/com/uber/simplestore/proto/impl/SimpleProtoStoreImpl.java b/protosimplestore/src/main/java/com/uber/simplestore/proto/impl/SimpleProtoStoreImpl.java index 5d05d18..0489622 100644 --- a/protosimplestore/src/main/java/com/uber/simplestore/proto/impl/SimpleProtoStoreImpl.java +++ b/protosimplestore/src/main/java/com/uber/simplestore/proto/impl/SimpleProtoStoreImpl.java @@ -65,6 +65,11 @@ public ListenableFuture put(String key, @Nullable T v SimpleStoreConfig.getComputationExecutor()); } + @Override + public ListenableFuture contains(String key) { + return Futures.transform(get(key), value -> value != null && value.length > 0, SimpleStoreConfig.getComputationExecutor()); + } + @Override public ListenableFuture getString(String key) { return simpleStore.getString(key); diff --git a/simplestore/src/main/java/com/uber/simplestore/SimpleStore.java b/simplestore/src/main/java/com/uber/simplestore/SimpleStore.java index 2ac15ee..205c23b 100644 --- a/simplestore/src/main/java/com/uber/simplestore/SimpleStore.java +++ b/simplestore/src/main/java/com/uber/simplestore/SimpleStore.java @@ -42,6 +42,14 @@ public interface SimpleStore extends Closeable { @CheckReturnValue ListenableFuture put(String key, @Nullable byte[] value); + /** + * Determine if a key exists in storage. + * @param key to check + * @return if key is set + */ + @CheckReturnValue + ListenableFuture contains(String key); + /** * Delete all keys in this direct scope. */ diff --git a/simplestore/src/main/java/com/uber/simplestore/impl/SimpleStoreImpl.java b/simplestore/src/main/java/com/uber/simplestore/impl/SimpleStoreImpl.java index d9c21e9..61a6a87 100644 --- a/simplestore/src/main/java/com/uber/simplestore/impl/SimpleStoreImpl.java +++ b/simplestore/src/main/java/com/uber/simplestore/impl/SimpleStoreImpl.java @@ -19,6 +19,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.Nonnull; import javax.annotation.Nullable; /** @@ -28,6 +29,7 @@ final class SimpleStoreImpl implements SimpleStore { private static final int OPEN = 0; private static final int CLOSED = 1; private static final int TOMBSTONED = 2; + private static final byte[] EMPTY_BYTES = new byte[0]; private final Context context; private final String scope; @@ -89,11 +91,10 @@ public ListenableFuture get(String key) { } catch (IOException e) { return Futures.immediateFailedFuture(e); } - if (value == null) { - cache.remove(key); - } else { - cache.put(key, value); + if (value == null || value.length == 0) { + value = EMPTY_BYTES; } + cache.put(key, value); } return Futures.immediateFuture(value); }, orderedIoExecutor); @@ -106,9 +107,10 @@ public ListenableFuture put(String key, @Nullable byte[] value) { if (isClosed()) { return Futures.immediateFailedFuture(new StoreClosedException()); } - if (value == null) { - cache.remove(key); + if (value == null || value.length == 0) { + cache.put(key, EMPTY_BYTES); deleteFile(key); + return Futures.immediateFuture(EMPTY_BYTES); } else { cache.put(key, value); try { @@ -116,11 +118,18 @@ public ListenableFuture put(String key, @Nullable byte[] value) { } catch (IOException e) { return Futures.immediateFailedFuture(e); } + return Futures.immediateFuture(value); } - return Futures.immediateFuture(value); }, orderedIoExecutor); } + @Override + public ListenableFuture contains(String key) { + requireOpen(); + return Futures.transform(get(key), (value ) -> value != null && value.length > 0, + SimpleStoreConfig.getComputationExecutor()); + } + @Override public ListenableFuture deleteAll() { requireOpen(); diff --git a/simplestore/src/test/java/com/uber/simplestore/impl/SimpleStoreImplTest.java b/simplestore/src/test/java/com/uber/simplestore/impl/SimpleStoreImplTest.java index 6e007cf..6456a3e 100644 --- a/simplestore/src/test/java/com/uber/simplestore/impl/SimpleStoreImplTest.java +++ b/simplestore/src/test/java/com/uber/simplestore/impl/SimpleStoreImplTest.java @@ -37,10 +37,10 @@ public void reset() { } @Test - public void nullWhenMissing() throws Exception { + public void zeroLengthWhenMissing() throws Exception { try(SimpleStore store = SimpleStoreFactory.create(context, "")) { ListenableFuture future = store.get(TEST_KEY); - assertThat(future.get()).isNull(); + assertThat(future.get()).hasLength(0); } } @@ -49,7 +49,7 @@ public void puttingNullDeletesKey() throws Exception { try(SimpleStore store = SimpleStoreFactory.create(context, "")) { ListenableFuture first = store.put(TEST_KEY, new byte[1]); ListenableFuture second = store.put(TEST_KEY, null); - assertThat(second.get()).isNull(); + assertThat(second.get()).isEmpty(); } } @@ -59,7 +59,7 @@ public void deleteAll() throws Exception { ListenableFuture first = store.put(TEST_KEY, new byte[1]); ListenableFuture second = store.deleteAll(); ListenableFuture empty = store.get(TEST_KEY); - assertThat(empty.get()).isNull(); + assertThat(empty.get()).isEmpty(); } }