diff --git a/src/main/java/part2/cache/CachingDataStorageImpl.java b/src/main/java/part2/cache/CachingDataStorageImpl.java index a2ae460..8abac84 100755 --- a/src/main/java/part2/cache/CachingDataStorageImpl.java +++ b/src/main/java/part2/cache/CachingDataStorageImpl.java @@ -1,7 +1,6 @@ package part2.cache; import db.DataStorage; -import db.SlowCompletableFutureDb; import java.util.concurrent.*; @@ -10,7 +9,7 @@ public class CachingDataStorageImpl implements CachingDataStorage private final DataStorage db; private final int timeout; private final TimeUnit timeoutUnits; - // TODO can we use Map here? Why? + // TODO can we use Map here? Why? - cause we write for a multithreading app private final ConcurrentMap> cache = new ConcurrentHashMap<>(); private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { @@ -32,12 +31,34 @@ public CachingDataStorageImpl(DataStorage db, int timeout, TimeUnit t @Override public OutdatableResult getOutdatable(String key) { - // TODO implement - // TODO use ScheduledExecutorService to remove outdated result from cache - see SlowCompletableFutureDb implementation - // TODO complete OutdatableResult::outdated after removing outdated result from cache - // TODO don't use obtrudeException on result - just don't - // TODO use remove(Object key, Object value) to remove target value - // TODO Start timeout after receiving result in CompletableFuture, not after receiving CompletableFuture itself - throw new UnsupportedOperationException(); + final OutdatableResult result = + new OutdatableResult<>(new CompletableFuture<>(), new CompletableFuture<>()); + final OutdatableResult previous = cache.putIfAbsent(key, result); + + if (previous != null) { + return previous; + } + + db.get(key).whenComplete((value, throwable) -> { + + if (throwable != null) { + result.getResult().completeExceptionally(throwable); + } else { + result.getResult().complete(value); + } + + scheduledExecutorService.schedule( + () -> { + cache.remove(key, result); + result.getOutdated().complete(null); + }, + timeout, + timeoutUnits + ); + + }); + + return result; } -} + +} \ No newline at end of file diff --git a/src/test/java/part2/cache/CachingDataStorageImplTest.java b/src/test/java/part2/cache/CachingDataStorageImplTest.java index 041370d..4a44e09 100755 --- a/src/test/java/part2/cache/CachingDataStorageImplTest.java +++ b/src/test/java/part2/cache/CachingDataStorageImplTest.java @@ -55,7 +55,7 @@ public static void after() { } @Test - public void expiration() throws InterruptedException, ExecutionException, TimeoutException { + public void expirationEmployee() throws InterruptedException, ExecutionException, TimeoutException { final CachingDataStorageImpl employeeCache = new CachingDataStorageImpl<>(employeeDb, 100, TimeUnit.MILLISECONDS); @@ -84,4 +84,64 @@ public void expiration() throws InterruptedException, ExecutionException, Timeou assertEquals(person2, result3.getResult().get().getPerson()); } + @Test + public void expirationEmployer() throws InterruptedException, ExecutionException, TimeoutException { + final CachingDataStorageImpl employerCache = + new CachingDataStorageImpl<>(employerDb, 100, TimeUnit.MILLISECONDS); + + Map values = new HashMap<>(); + Employer google = Employer.Google; + values.put("a", google); + employerDb.setValues(values); + + final OutdatableResult result1 = employerCache.getOutdatable("a"); + + values = new HashMap<>(); + Employer yandex = Employer.Yandex; + values.put("a", yandex); + employerDb.setValues(values); + + Thread.sleep(10); + final OutdatableResult result2 = employerCache.getOutdatable("a"); + + assertEquals(google, result1.getResult().get()); + assertEquals(result1.getResult().get(), result2.getResult().get()); + + result1.getOutdated().get(100, TimeUnit.MILLISECONDS); + + OutdatableResult result3 = employerCache.getOutdatable("a"); + + assertEquals(yandex, result3.getResult().get()); + } + + @Test + public void expirationPosition() throws InterruptedException, ExecutionException, TimeoutException { + final CachingDataStorageImpl employerCache = + new CachingDataStorageImpl<>(positionDb, 100, TimeUnit.MILLISECONDS); + + Map values = new HashMap<>(); + Position dev = Position.DEV; + values.put("a", dev); + positionDb.setValues(values); + + final OutdatableResult result1 = employerCache.getOutdatable("a"); + + values = new HashMap<>(); + Position devOps = Position.DevOps; + values.put("a", devOps); + positionDb.setValues(values); + + Thread.sleep(10); + final OutdatableResult result2 = employerCache.getOutdatable("a"); + + assertEquals(dev, result1.getResult().get()); + assertEquals(result1.getResult().get(), result2.getResult().get()); + + result1.getOutdated().get(100, TimeUnit.MILLISECONDS); + + OutdatableResult result3 = employerCache.getOutdatable("a"); + + assertEquals(devOps, result3.getResult().get()); + } + }