From a035bbe18f27bbdc96437a1e02b8595fcb64581f Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Mon, 26 Feb 2024 14:20:02 -0600 Subject: [PATCH] Add getResultThrowing; remove Deprecated getResult --- .../core/utils/memoization/Memoization.java | 62 +++++++++---------- .../utils/memoization/MemoizationTest.java | 57 ++++++++++------- 2 files changed, 62 insertions(+), 57 deletions(-) diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/memoization/Memoization.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/memoization/Memoization.java index 36ae4aa0..ae373024 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/memoization/Memoization.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/memoization/Memoization.java @@ -58,42 +58,15 @@ public class Memoization - /******************************************************************************* - ** Get the memoized Value for a given input Key. - ** - ** But note, this looks the same to the caller, whether the key just wasn't in - ** the internal map (e.g., had never been looked up), or if it was previously looked - ** up, and that returned null. In either case, the optional will be empty. - ** - ** See getMemoizedResult for where we can tell the difference (and we would - ** generally want to call that. - *******************************************************************************/ - @Deprecated - public Optional getResult(K key) - { - MemoizedResult result = map.get(key); - if(result != null) - { - if(result.getTime().isAfter(Instant.now().minus(timeout))) - { - return (Optional.ofNullable(result.getResult())); - } - } - - return (Optional.empty()); - } - - - /******************************************************************************* ** Get the memoized Value for a given input Key - computing it if it wasn't previously ** memoized (or expired). ** - ** In here, if the optional is empty, it means the value is null (whether that + ** If the returned Optional is empty, it means the value is null (whether that ** came form memoization, or from the lookupFunction, you don't care - the answer ** is null). *******************************************************************************/ - public Optional getResult(K key, UnsafeFunction lookupFunction) + public Optional getResultThrowing(K key, UnsafeFunction lookupFunction) throws E { MemoizedResult result = map.get(key); if(result != null) @@ -111,12 +84,33 @@ public class Memoization ///////////////////////////////////////////////////////////////////////////////////////////// // ok - either we never memoized this key, or it's expired, so, apply the lookup function, // // store the result, and then return the value (in an Optional.ofNullable) // + // and if the lookup function throws - then we let it throw. // ///////////////////////////////////////////////////////////////////////////////////////////// + V value = lookupFunction.apply(key); + storeResult(key, value); + return (Optional.ofNullable(value)); + } + + + + /******************************************************************************* + ** Get the memoized Value for a given input Key - computing it if it wasn't previously + ** memoized (or expired). + ** + ** If a null value was memoized, the resulting optional here will be empty. + ** + ** If the lookup function throws, then a null value will be memoized and an empty + ** Optional will be returned. + ** + ** In here, if the optional is empty, it means the value is null (whether that + ** came form memoization, or from the lookupFunction, you don't care - the answer + ** is null). + *******************************************************************************/ + public Optional getResult(K key, UnsafeFunction lookupFunction) + { try { - V value = lookupFunction.apply(key); - storeResult(key, value); - return (Optional.ofNullable(value)); + return getResultThrowing(key, lookupFunction); } catch(Exception e) { @@ -131,8 +125,8 @@ public class Memoization /******************************************************************************* ** Get a memoized result, optionally containing a Value, for a given input Key. ** - ** In this method (contrasted with getResult), if the returned Optional is empty, - ** it means that we haven't ever looked up or memoized the key (or it's expired). + ** If the returned Optional is empty, it means that we haven't ever looked up + ** or memoized the key (or it's expired). ** ** If the returned Optional is not empty, then it means we've memoized something ** (and it's not expired) - so if the Value from the MemoizedResult is null, diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/memoization/MemoizationTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/memoization/MemoizationTest.java index 27f60d0c..5d999353 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/memoization/MemoizationTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/memoization/MemoizationTest.java @@ -39,6 +39,7 @@ import com.kingsrook.qqq.backend.core.utils.lambdas.UnsafeFunction; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -61,9 +62,9 @@ class MemoizationTest extends BaseTest memoization.setMaxSize(3); memoization.setTimeout(Duration.ofMillis(100)); - assertThat(memoization.getResult("one")).isEmpty(); + assertThat(memoization.getMemoizedResult("one")).isEmpty(); memoization.storeResult("one", 1); - assertThat(memoization.getResult("one")).isPresent().get().isEqualTo(1); + assertThat(memoization.getMemoizedResult("one")).isPresent().get().extracting("result").isEqualTo(1); //////////////////////////////////////////////////// // store 3 more results - this should force 1 out // @@ -71,22 +72,22 @@ class MemoizationTest extends BaseTest memoization.storeResult("two", 2); memoization.storeResult("three", 3); memoization.storeResult("four", 4); - assertThat(memoization.getResult("one")).isEmpty(); + assertThat(memoization.getMemoizedResult("one")).isEmpty(); ////////////////////////////////// // make sure others are present // ////////////////////////////////// - assertThat(memoization.getResult("two")).isPresent().get().isEqualTo(2); - assertThat(memoization.getResult("three")).isPresent().get().isEqualTo(3); - assertThat(memoization.getResult("four")).isPresent().get().isEqualTo(4); + assertThat(memoization.getMemoizedResult("two")).isPresent().get().extracting("result").isEqualTo(2); + assertThat(memoization.getMemoizedResult("three")).isPresent().get().extracting("result").isEqualTo(3); + assertThat(memoization.getMemoizedResult("four")).isPresent().get().extracting("result").isEqualTo(4); ///////////////////////////////////////////////////////////// // wait more than the timeout, then make sure all are gone // ///////////////////////////////////////////////////////////// SleepUtils.sleep(150, TimeUnit.MILLISECONDS); - assertThat(memoization.getResult("two")).isEmpty(); - assertThat(memoization.getResult("three")).isEmpty(); - assertThat(memoization.getResult("four")).isEmpty(); + assertThat(memoization.getMemoizedResult("two")).isEmpty(); + assertThat(memoization.getMemoizedResult("three")).isEmpty(); + assertThat(memoization.getMemoizedResult("four")).isEmpty(); } @@ -100,24 +101,17 @@ class MemoizationTest extends BaseTest Memoization memoization = new Memoization<>(); memoization.storeResult("null", null); - /////////////////////////////////////////////////////////////////////////////////////////// - // note - we can't tell a stored null apart from a non-stored value by calling getResult // - /////////////////////////////////////////////////////////////////////////////////////////// - Optional optionalNull = memoization.getResult("null"); - assertNotNull(optionalNull); - assertTrue(optionalNull.isEmpty()); - - //////////////////////////////////////////// - // instead, we must use getMemoizedResult // - //////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////// + // the memoizedResult should never be null, and should be present if we memoized/stored a null value // + /////////////////////////////////////////////////////////////////////////////////////////////////////// Optional> optionalMemoizedResult = memoization.getMemoizedResult("null"); assertNotNull(optionalMemoizedResult); assertTrue(optionalMemoizedResult.isPresent()); assertNull(optionalMemoizedResult.get().getResult()); - ///////////////////////////////////////////////////////////////// - // make sure getMemoizedResult returns empty for an un-set key // - ///////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + // make sure getMemoizedResult returns non-null and empty for an un-set key // + ////////////////////////////////////////////////////////////////////////////// optionalMemoizedResult = memoization.getMemoizedResult("never-stored"); assertNotNull(optionalMemoizedResult); assertTrue(optionalMemoizedResult.isEmpty()); @@ -177,6 +171,23 @@ class MemoizationTest extends BaseTest + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testGetResultThrowing() throws Exception + { + Memoization memoization = new Memoization<>(); + + UnsafeFunction lookupFunction = Integer::parseInt; + + assertEquals(Optional.of(1), memoization.getResultThrowing("1", lookupFunction)); + assertThatThrownBy(() -> memoization.getResultThrowing(null, lookupFunction)).hasMessageContaining("null"); + assertThatThrownBy(() -> memoization.getResultThrowing("two", lookupFunction)).hasMessageContaining("For input string:"); + } + + + /******************************************************************************* ** *******************************************************************************/ @@ -198,7 +209,7 @@ class MemoizationTest extends BaseTest for(int n = 0; n < 1_000_000; n++) { memoization.storeResult(String.valueOf(n), n); - memoization.getResult(String.valueOf(n)); + memoization.getMemoizedResult(String.valueOf(n)); if(n % 100_000 == 0) {