Conditional memoization in Guava - java

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.

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);
}

Could this be what you are looking for?
private void createCache() {
this.cache = Suppliers.memoizeWithExpiration(
Suppliers.compose(
response -> (response.isSuccess() ? response : null),
myService::call
),
timeout,
TimeUnit.MINUTES
);
}
Here, it will cache the response, or null, depending on whether it was successful.
More info on compose here https://github.com/google/guava/blob/master/guava/src/com/google/common/base/Suppliers.java#L45
EDIT:
If you need to cache the value on success, and leave the cache empty on failure, while returning the failed request, then you are almost there yourself, just change return logic a bit in getResponse, like this:
public MyResponse getResponse() {
final MyResponse myResponse = cache.get();
if (!myResponse.isSuccess()) {
this.createCache(); // clear cache
}
return myResponse; // don't call .get() again!
}

Related

Caching reponse method results

I have a interface for getting responses from an API server, the result is a future containing the response for a request string, my question is what is better and efficient way without repeating code to cache the results, currently in each implementation of the method I am doing map.get() from in memory cache if is cached, but it is repetitive since in each method I am writing the same. If it's useful, I'm using OkHttp, just in case it's possible to use some kind of interceptor or some other way, thanks..
interface SodyApi {
ListenableFuture<FriendsListResponse> fetchFriendsList(String repo);
ListenableFuture<GuildResponse> fetchGuild(String repo);
ListenableFuture<UsersListResponse> fetchAllUsers(String repo);
}
class SodyApiImpl implements SodyApi {
final Map<String, ListenableFuture<Response>> map = new HashMap();
public ListenableFuture<FriendsList> fetchFriendsList(String repo) {
if (map.containsKey(repo))
return map.get(repo);
// .... get response with okHttp
}
public ListenableFuture<GuildResponse> fetchGuild(String repo) {
if (map.containsKey(repo))
return map.get(repo);
// .... get response with okHttp
}
public ListenableFuture<UsersListResponse> fetchAllUsers(String repo) {
if (map.containsKey(repo))
return map.get(repo);
// .... get response with okHttp
}
}

Asynchronous sequential calls based on condition checks in reactor

Here, I am trying to make asynchronous and non-blocking calls using reactor and for each request, I may have to call two services in sequence (in my case below, getAccountInfoFromAAA and getAccountInfoFromBBB).
Here is my ItemRequest object:
public class ItemRequest {
private Account account;
private Result firstServiceResult;
private Result secondServiceResult;
private PostingParameterCode postingParameterCode; //enum
//...
//...
//getters and setters
}
So, my request input will contain multiple itemRequests and for each itemRequest, I am doing asynchronous calls as:
public void getAccountData(List<ItemRequest> itemRequests) {
ImmutableList<ItemRequest> list = ImmutableList.copyOf(itemRequests);
Flux.fromIterable(list).flatMap(this::callBothSors).blockLast();
}
public Mono<ItemRequest> callBothSors(ItemRequest itemRequest) {
return getAccountDataService.getAccountDataFromAAAandBBB(itemRequest);
//here, it will enter into a sequential call for each itemRequest
}
This is my first service call interface:
public Mono<ItemRequest> getAccountDataFromAAA(ItemRequest itemRequest);
This is my second service call interface:
public Mono<ItemRequest> getAccountDataFromBBB(ItemRequest itemRequest);
This method will have upto two calls in sequence based on the condition:
public Mono<ItemRequest> getAccountDataFromAAAandBBB(ItemRequest itemRequest){
Mono<ItemRequest> firstCallResult = Mono.empty();
Mono<ItemRequest> secondCallResult = Mono.empty();
if(isFirstServiceCallRequired(itemRequest)){
firstCallResult = this.firstServiceCallImpl.getAccountDataFromAAA(itemRequest);
//basically, firstService call will update the accountKey information and
//will also set the result status to OK which is required to decide
//whether to make secondService call.
} else {
//Account key is already present, so just update the result status which I need later.
Result result = new Result();
result.setStatus(Result.Status.OK);
result.setMessageText("First call not required as account info is set for item request");
itemRequest.setFirstServiceResult(result);
}
//Now, before calling the second service, I need to check the following:
if(null!= itemRequest.getFirstServiceResult() &&
itemRequest.getFirstServiceResult().getStatus().equals(Result.Status.OK) &&
itemRequest.getPostingParameterCode().equals(PostingParameterCode.MOBILECREDIT)){
secondCallResult = this.secondServiceCallImpl.getAccountDataFromBBB(itemRequest);
}
return firstCallResult.then(secondCallResult); //attaching the
//firstCallResult and secondCallResult to produce a single Mono
}
This is working fine when firstCallResult is not required. But when the first call is required, this condition check will not pass since I won't have first call result object updated:
if(null != itemRequest.getFirstServiceResult() &&
itemRequest.getFirstServiceResult().getStatus().equals(Result.Status.OK) &&
itemRequest.getPostingParameterCode().equals(PostingParameterCode.MOBILECREDIT))) { ... }
//this condition check will not pass because first service call is not actually executing
Both cases works fine if I put the following statement:
if(isFirstServiceCallRequired(itemRequest)){
firstCallResult = this.firstServiceCallImpl.getAccountDataFromAAA(itemRequest);
firstCallResult.block(); //adding this case will work on both cases
}
But, I don't think I will get the reactors benefit this way.
I was thinking to have the logic like this:
Mono<ItemRequest> result = firstService.call(...)
.doOnNext(/*do something */)
.then( ... secondService.call())
But couldn't figure out the way to chain the secondService with firstService to get the mono result and have those condition checks too.
Condition check is important since I don't always want to execute the second service. Is there any way to chain the secondService with firstService to get the result and have those condition checks too?
Apologies for the long question. Any suggestions/help would be greatly appreciated.
After offering the bounty points to this question, I was really excited and expecting some answers.
But anyways, I am able to improve my initial solution and have those condition checks too.
I did the following:
I changed the return type from Mono<ItemRequest> to Mono<Void> in both service calls since I am basically updating the data to ItemRequest list:
Handling the parallel call here (each parallel call has a sequential call):
public void getAccountData(List<ItemRequest> itemRequests) {
ImmutableList<ItemRequest> list = ImmutableList.copyOf(itemRequests);
Flux.fromIterable(list).flatMap(this::callBothSors).blockLast();
}
public Mono<Void> callBothSors(ItemRequest itemRequest) {
return getAccountDataService.getAccountDataFromAAAandBBB(itemRequest);
//here, it will enter into a sequential call for each itemRequest
}
and these are my firstServiceCall and secondServiceCall interface changes:
public Mono<Void> getAccountDataFromAAA(ItemRequest itemRequest);
public Mono<Void> getAccountDataFromBBB(ItemRequest itemRequest);
and I chained the secondServiceCall with firstServiceCall to get the mono result and have those condition checks too as:
public Mono<Void> getAccountDataFromAAAandBBB(ItemRequest itemRequest){
Mono<Void> callSequence = Mono.empty();
if(isFirstServiceCallRequired(itemRequest)){
callSequence = this.firstServiceCallImpl.getAccountDataFromAAA(itemRequest);
} else {
//Account key is already present, so just update the result status which I need later.
Result result = new Result();
result.setStatus(Result.Status.OK);
result.setMessageText("First call not required as account info is set for item request");
itemRequest.setFirstServiceResult(result);
}
return callSequence.thenEmpty(Mono.defer(() -> {
//note: Mono.defer ==>> Create a Mono provider that will supply a target Mono to subscribe to
//for each subscriber downstream.
//only if the firstServiceCall result is successful & other condition check successful,
// I am calling secondServiceCall:
if(shouldCallSecondService(itemRequest)){
return this.secondServiceCallImpl.getAccountDataFromAAAandBBB(itemRequest);
} else {
return Mono.empty();
}
}))
Here are some news: A Reactor is not a silver bullet! :)
Whenever you need the response of a call to determine if you need to do something else, this will never be able to be fully parallelized. E.g. you could always do you last suggestion. However, this doesn't mean that using the Reactor doesn't give you any benefits!
Some of the benefits you get:
You are using Netty under the hood instead of Servlet, which helps to avoid locking on I/O operations. This can lead to better allocation of resources, making your system more resilient.
You can do other operations while waiting for a response. If you have things to do where the order doesn't matter, you can always put them there (e.g. auditing, logging etc).
I hope this answers your question :)
public Mono<ItemRequest> getAccountDataFromAAAandBBB(ItemRequest itemRequest) {
Mono<ItemRequest> firstCallResult = Mono.empty();
Mono<ItemRequest> secondCallResult = Mono.empty();
if (isFirstServiceCallRequired(itemRequest)) {
firstCallResult = this.firstServiceCallImpl.getAccountDataFromAAA(itemRequest);
//basically, firstService call will update the accountKey information and
//will also set the result status to OK which is required to decide
//whether to make secondService call.
} else {
/*Account key is already present, so just update the result status which I need
later.*/
firstCallResult = Mono.defer(() -> {
Result result = new Result();
result.setStatus(Result.Status.OK);
result.setMessageText("First call not required as account info is set for item request");
itemRequest.setFirstServiceResult(result);
return Mono.just(itemRequest);
});
}
return firstCallResult.flatMap(itReq -> {
//Now, before calling the second service, I need to check the following:
if (null != itemRequest.getFirstServiceResult() &&
itemRequest.getFirstServiceResult().getStatus().equals(Result.Status.OK) &&
itemRequest.getPostingParameterCode().equals(PostingParameterCode.MOBILECREDIT)) {
return secondCallResult = this.secondServiceCallImpl.getAccountDataFromBBB(itemRequest);
} else {
return itReq;
}
});
}
The next simple example can help you with flatMap understanding:
public static void main(String[] args) {
callExternalServiceA.flatMap(response -> {
if(response.equals("200")){
return Mono.just(response);
} else {
return callExtertnalServiceB();
}
}).block();
}
public static Mono<String> callExtertnalServiceA() {
return Mono.defer(() -> {
System.out.println("Call external service A");
return Mono.just("400");
});
}
public static Mono<String> callExtertnalServiceB() {
return Mono.defer(() -> {
System.out.println("Call external service B");
return Mono.just("200");
});
}

RestTemplate & ResponseErrorHandler: Elegant means of handling errors given an indeterminate return object

Using a RestTemplate, I am querying a remote API to return an object either of expected type (if HTTP 2xx) or an APIError (if HTTP 4xx / 5xx).
Because the response object is indeterminate, I have implemented a custom ResponseErrorHandler and overridden handleError(ClientHttpResponse clientHttpResponse) in order to extract the APIError when it occurs. So far so good:
#Component
public class RemoteAPI {
public UserOrders getUserOrders(User user) {
addAuthorizationHeader(httpHeaders, user.getAccessToken());
HttpEntity<TokenRequest> request = new HttpEntity<>(HEADERS);
return restTemplate.postForObject(CUSTOMER_ORDERS_URI, request, UserOrders.class);
}
private class APIResponseErrorHandler implements ResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response) {
try {
APIError apiError = new ObjectMapper().readValue(response.getBody(), APIError.class);
} catch ...
}
}
private void refreshAccessToken(User user) {
addAuthorizationHeader(httpHeaders, user.getAccessSecret());
HttpEntity<TokenRequest> request = new HttpEntity<>(HEADERS);
user.setAccessToken(restTemplate.postForObject(TOKEN_REFRESH_URI, request, AccessToken.class));
}
}
The challenge is that getUserOrders(), or a similar API call, will occasionally fail with a 'recoverable' error; for instance, the API access token may have expired. We should then make an API call to refreshAccessToken() before re-attempting getUserOrders(). Recoverable errors such as these should be hidden from the user until the same ones have occurred multiple times, at which point they are are deemed non-recoverable / critical.
Any errors which are 'critical' (e.g.: second failures, complete authentication failure, or transport layer failures) should be reported to the user as there is no automatic recovery available.
What is the most elegant and robust way managing the error handling logic, bearing in mind that the type of object being returned is not known until runtime?
Option 1: Error object as a class variable with try / catch in each API call method:
#Component
public class RemoteAPI {
private APIError apiError;
private class APIResponseErrorHandler implements ResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response) {
try {
this.apiError = new ObjectMapper().readValue(response.getBody(), APIError.class);
} catch ...
}
}
public UserOrders getUserOrders(User user) {
try {
userOrders = restTemplate.postForObject(CUSTOMER_ORDERS_URI, request, UserOrders.class);
} catch (RestClientException ex) {
// Check this.apiError for type of error
// Check how many times this API call has been attempted; compare against maximum
// Try again, or report back as a failure
}
return userOrders;
}
}
Pros: Clarity on which method originally made the call
Cons: Use of a class variable for a transient value. Lots of boilerplate code for each method that calls the API. Error handling logic spread around multiple methods.
Option 2: User object as a class variable / Error management logic in the ResponseErrorHandler
#Component
public class RemoteAPI {
private User user;
private class APIResponseErrorHandler implements ResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response) {
try {
APIError apiError = new ObjectMapper().readValue(response.getBody(), APIError.class);
// Check this.apiError for type of error
// Check how many times this API call has been attempted; compare against maximum
// Try again...
getUserOrders();
...or report back as a failure
} catch ...
}
}
Pros: Error management logic is in one place.
Cons: User object must now be a class variable and handled gracefully, because the User object cannot otherwise be accessible within the ResponseErrorHandler and so cannot pass it to getUserOrders(User) as before. Need to keep track of how many times each method has been called.
Option 3: Error management logic outside of the RemoteAPI class
Pros: Separates error handling from business logic
Cons: API logic is now in another class
Thank you for any advice.
Answering my own question: it turns out that there were fallacies in the question itself.
I was implementing a ResponseErrorHandler because I thought I needed it to parse the response even when that response was returned with a HTTP error code. In fact, that isn't the case.
This answer demonstrates that the response can be parsed into an object by catching a HttpStatusCodeException and otherwise using a standard RestTemplate. That negates the need for a custom ResponseErrorHandler and therefore the need to return an object of ambiguous type. The method that is handed the error can catch the HttpStatusCodeException, try to refresh the access token, and then call itself again via recursion. A counter is required to prevent endless recursion but that can be passed through rather than being a class variable.
The downside is that it still requires error management logic spread around the class, along with plenty of boilerplate code, but it's a lot tidier than the other options.
public UserOrders getUserOrders(User user, Integer methodCallCount) {
methodCallCount++;
UserOrders userOrders;
try {
userOrders = restTemplate.postForObject(USER_ORDERS_URI, request, UserOrders.class);
} catch (RestClientException ex) {
APIError apiError = new ObjectMapper().readValue(response.getBody(), APIError.class);
if (methodCallCount < MAX_METHOD_CALLS) {
if (apiError.isType(ACCESS_TOKEN_EXPIRED)) {
refreshVendorAccessTokenInfo(user);
userOrders = getUserOrders(user, methodCallCount);
}
}
}
return userOrders;
}

How to set RequestConfiguration per request using RestTemplate?

I have a library which is being used by customer and they are passing DataRequest object which has userid, timeout and some other fields in it. Now I use this DataRequest object to make a URL and then I make an HTTP call using RestTemplate and my service returns back a JSON response which I use it to make a DataResponse object and return this DataResponse object back to them.
Below is my DataClient class used by customer by passing DataRequest object to it. I am using timeout value passed by customer in DataRequest to timeout the request if it is taking too much time in getSyncData method.
public class DataClient implements Client {
private final RestTemplate restTemplate = new RestTemplate();
private final ExecutorService service = Executors.newFixedThreadPool(10);
// this constructor will be called only once through my factory
// so initializing here
public DataClient() {
try {
restTemplate.setRequestFactory(clientHttpRequestFactory());
} catch (Exception ex) {
// log exception
}
}
#Override
public DataResponse getSyncData(DataRequest key) {
DataResponse response = null;
Future<DataResponse> responseFuture = null;
try {
responseFuture = getAsyncData(key);
response = responseFuture.get(key.getTimeout(), key.getTimeoutUnit());
} catch (TimeoutException ex) {
response = new DataResponse(DataErrorEnum.CLIENT_TIMEOUT, DataStatusEnum.ERROR);
responseFuture.cancel(true);
// logging exception here
}
return response;
}
#Override
public Future<DataResponse> getAsyncData(DataRequest key) {
DataFetcherTask task = new DataFetcherTask(key, restTemplate);
Future<DataResponse> future = service.submit(task);
return future;
}
// how to set socket timeout value by using `key.getSocketTimeout()` instead of using hard coded 400
private ClientHttpRequestFactory clientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
RequestConfig requestConfig =
RequestConfig.custom().setConnectionRequestTimeout(400).setConnectTimeout(400)
.setSocketTimeout(400).setStaleConnectionCheckEnabled(false).build();
SocketConfig socketConfig =
SocketConfig.custom().setSoKeepAlive(true).setTcpNoDelay(true).build();
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager =
new PoolingHttpClientConnectionManager();
poolingHttpClientConnectionManager.setMaxTotal(300);
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(200);
CloseableHttpClient httpClientBuilder =
HttpClientBuilder.create().setConnectionManager(poolingHttpClientConnectionManager)
.setDefaultRequestConfig(requestConfig).setDefaultSocketConfig(socketConfig).build();
requestFactory.setHttpClient(httpClientBuilder);
return requestFactory;
}
}
DataFetcherTask class:
public class DataFetcherTask implements Callable<DataResponse> {
private final DataRequest key;
private final RestTemplate restTemplate;
public DataFetcherTask(DataRequest key, RestTemplate restTemplate) {
this.key = key;
this.restTemplate = restTemplate;
}
#Override
public DataResponse call() throws Exception {
// In a nutshell below is what I am doing here.
// 1. Make an url using DataRequest key.
// 2. And then execute the url RestTemplate.
// 3. Make a DataResponse object and return it.
}
}
Customer within our company will use my library like this as shown below by using my factory in their code base -
// if they are calling `getSyncData()` method
DataResponse response = DataClientFactory.getInstance().getSyncData(key);
// and if they want to call `getAsyncData()` method
Future<DataResponse> response = DataClientFactory.getInstance().getAsyncData(key);
I am implementing sync call as async + waiting since I want to throttle them with the number of threads otherwise they can bombard our service without any control.
Problem Statement:-
I am going to add another timeout variable called socket timeout in my DataRequest class and I want to use that variable value (key.getSocketTimeout()) in my clientHttpRequestFactory() method instead of using hard coded 400 value. What is the best and efficient way to do that?
Right now I am using Inversion of Control and passing RestTemplate in a constructor to share the RestTemplate between all my Task objects. I am confuse now how to use key.getSocketTimeout() value in my clientHttpRequestFactory() method. I think this is mostly design question of how to use RestTemplate efficiently here so that I can use key.getSocketTimeout() value in my clientHttpRequestFactory() method.
I have simplified the code so that idea gets clear what I am trying to do and I am on Java 7. Using ThreadLocal is the only option I have here or there is any better and optimized way?
As Peter explains, using ThreadLocal is not a good idea here.
But I also could not find a way to "pass the value up the chain of method calls".
If you use plain "Apache HttpClient", you can create an HttpGet/Put/etc. and simply call
httpRequest.setConfig(myRequestConfig). In other words: set a request configuration per request
(if nothing is set in the request, the request configuration from the HttpClient which executes the request is used).
In contrast, the RestTemplate
calls createRequest(URI, HttpMethod) (defined in HttpAccessor)
which uses the ClientHttpRequestFactory. In other words: there is no option to set a request configuration per request.
I'm not sure why Spring left this option out, it seems a reasonable functional requirement (or maybe I'm still missing something).
Some notes about the "they can bombard our service without any control":
This is one of the reasons to use the PoolingHttpClientConnectionManager:
by setting the appropriate maximum values, there can never be more than the specified maximum connections in use (and thus requests running) at the same time. The assumption here is that you re-use the same RestTemplate instance (and thus connection manager) for each request.
To catch a flood earlier, specify a maximum amount of waiting tasks in the threadpool and set a proper error-handler
(use the workQueue and handler in this constructor).
ThreadLocal is a way to pass dynamic value which normally you would pass via method properties, but you are using an API you can't/don't want to change.
You set the ThreadLocal (possible a data structure containing multiple values) at some level in the thread stack and you can use it further up the stack.
Is this the best approach? NO, you should really pass the value up the chain of method calls, but sometimes this is not practical.
Can you provide an example of how my code will look like with ThreadLocal
You might start with
static final ThreadLocal<Long> SOCKET_TIMEOUT = new ThreadLocal<>();
To set it you can do
SOCKET_TIMEOUT .set(key.getSocketTimeout());
and to get the value you can do
long socketTimeout = SOCKET_TIMEOUT.get();

Obtain the JsonNode from a Promise[JsonNode] in Java and return that value

I'm writing a Play 2.3.2 application in Java.
In my application I make a call to a method of an other module, written in Scala.
This method returns a Json response and I try to obtain that using WS.
This is my method implementation:
public static JsonNode getCorrelationData() {
WSRequestHolder holder = WS.url(ConfigFactory.load().getString("host") + "/recommendation/correlation");
Promise<JsonNode> jsonPromise = holder.get().map(
new Function<WSResponse, JsonNode>() {
public JsonNode apply(WSResponse response) {
if (response.getStatus() != 200) {
Logger.error("Error on get correlation data");
Logger.error("Response status code: " + response.getStatus());
Logger.error("Response status text: " + response.getStatusText());
}
return response.asJson();
}
});
//here I want to obtain the JsonNode inside the jsonPromise object, and return it.
}
But the problem is that the callback returns a Promise, and my method need to return a JsonNode.
How can I obtain the JsonNode inside the Promise?? I can'f find any solution to my problem.
In Scala I know that I can use the flatMap on a Future[T].
Your method performs an asynchronous operation and thus should not return a JsonNode but rather a Promise<JsonNode>.
Conceptually - this makes sense - your method does not immediately fetch the data - rather it dispatches a task relating to the said data that will finish some time in the future. You can access the response by unwrapping the promise. You can return the Promise<JsonNode> and then call .map on it at the caller site to unwrap the value.
You can also call .get() on the promise which would force the data to wait for the result (that is - returning jsonPromise.get() but that negates the benefit of using promises to begin with. Note that get() here is different from the get() on holder which is fine since it just returns the promise.
Return promise of result instead:
public static Promise<Result> getPromise() {
WSRequestHolder url = WS.url("url");
Promise<Result> promise = url.get().map((r) -> {
if (r.getStatus() == 200) {
return ok(r.asJson());
} else {
return badRequest("Bad request");
}
});
return promise;
}

Categories