Search code examples
javacachingguavamemoization

Conditional memoization in Guava


I know how to memoize a single object. However, I'd like to memoize only if some condition is met. I'm calling a service that sometimes returns a response that is not successful. I'd like to memoize only if the service's response if successful.

MyResponse myResponse = myService.call()
boolean success = myResponse.isSuccessful();

And my cache is created like so:

private Supplier<MyResponse> cache;

private void createCache() {
    this.cache = Suppliers
        .memoizeWithExpiration(myService::call, timeout,
            TimeUnit.MINUTES);
}

Question: Is it possible to somehow cache the response only if the response is successful using the Supplier passed to the memoizeWithExpiration method?


The only workaround I found to do this is to, when retrieving the value, call cache.get() first, check if the object stored in cache is successful, and if it's not, call createCache() again to clear it and then get the value again. This way if the subsequent service call returns a valid object, it will get stored, and if not, every subsequent call will clear the cache and call the service again.

 public MyResponse getResponse() {
    MyResponse myResponse = cache.get();
    if (myResponse.isSuccess()) {
      return myResponse;
    } else {
      createCache();
      return cache.get();
    }
  }

However, in this solution, if the cache is empty and the service returns unsuccessful response, it will get called again immediately.


Solution

  • You can create a method callUntilSuccess in Service class or in any other suitable place (here I'm assuming it is in your Service). You could also define a maximum number of tries in this method and after that it will return null, so you could avoid calling your service indefinitely (this suggestion isn't implemented in the code supplied bellow but it is very easy to do so). As the Guava method expects a Supplier, you can even create a lambda with this logic and pass it directly to the memoizeWithExpiration method.

    public MyResponse callUntilSuccess() {
    
        MyResponse response = myService.call();
        while (!response.isSuccessful()) {
            response = myService.call();
        }
        return response;
    }   
    

    Then do the memoization in this way:

    private void createCache() {
        this.cache = Suppliers
             .memoizeWithExpiration(myService::callUntilSuccess, timeout,
                    TimeUnit.MINUTES);
    }