Spring Cache Abstraction with Hazelcast and time consuming method - spring

How using the Spring Cache Abstraction handle the case, when I want to call method getNameTimeConsumingMethod but if the result is not in cache then I do not want to wait for execution this method and return the result method getNameIfNotInCache.
#Cacheable(value = "cacheName", key = "#key")
public String getNameTimeConsumingMethod(String key) {
//time consuming method
}
public String getNameIfNotInCache(String key) {
//fast method if cacheName does not have a key
}

Well, if the key is in cache, getNameTimeConsumingMethod won't be executed, because the result will be returned from cache.
If the key is not in cache, you want to execute getNameIfNotInCache.
What I conclude is, you don't want to execute anything inside getNameTimeConsumingMethod.
Then why don't you just call getNameIfNotInCache from getNameTimeConsumingMethod and return it?

Related

Springboot cache miss when caching a object

While using Spring cache in my project, It results in cache miss when I am caching a complex java object (Tinker Graph) but works fine when caching a String. I am invoking these methods over rest thru a controller layer. Any ideas why this is the behavior ? My code is mostly similar to this tutorial.
For eg:
SomeService.java
#Cacheable(NAME_OF_CACHE_1)
public TinkerGraph getGraph(String graphCode) {
// do something
// this methods results in cache miss everytime.
}
#Cacheable(NAME_OF_CACHE_2)
public String getGraphAsString(String graphCode) {
// this is working fine and the string result is getting cached.
return convertToString(getGraph(graphCode));
}
After some debugging I also tried setting up the caches during the startup and could see that the values are cached into the Concurrent Hashmap but still causes the cache miss when returning the Graph instance but works perfectly fine when returning String instance.
CacheService.java
#CachePut("NAME_OF_CACHE_1")
public TinkerGraph getGraph(String graphCode) {
return (TinkerGraph)someService.getGraph(graphCode);
}
#CachePut("NAME_OF_CACHE_2")
public String getGraphAsString(String graphCode) {
return someService.getGraphAsString(graphCode);
}
#EventListner(ApplicationReadyEvent.class)
public void initCache() {
getGraph("G001");
getGraphAsString("G001");
}

Mutiny Uni Convert to Primitive Type

Up until now I have done very basic things with smallrye Mutiny in Quarkus. Basically, I have one or two very small web services which only interact with a web application. These services return a Uni<Response>.
Now I'm writing a logging service I want my others to pass information to. In this logging service, I need to return a value to calling services. The logging service will return this value as a Uni<Integer>. What I'm struggling with is how to extract the return value in the calling service as an int.
Here is the function in the logging service
#GET
#Path("/requestid")
#Produces(MediaType.TEXT_PLAIN)
public Uni<Integer> getMaxRequestId(){
return service.getMaxRequestId();
}
public Uni<Integer> getMaxRequestId() {
Integer result = Integer.valueOf(em.createQuery("select MAX(request_id) from service_requests").getFirstResult());
if(result == null) {
result = 0;
}
return Uni.createFrom().item(result += 1);
}
And here is the client side code in the calling service
#Path("/requests")
public class RequestIdResource {
#RestClient
RequestIdServices service;
#GET
#Path("/requestid")
#Produces(MediaType.TEXT_PLAIN)
public Uni<Integer> getMaxRequestId(){
return service.getMaxRequestId();
}
}
public void filter(ContainerRequestContext requestContext) throws IOException {
int requestid = client.getMaxRequestId();
rm.name = ConfigProvider.getConfig().getValue("quarkus.application.name", String.class);
rm.server = requestContext.getUriInfo().getBaseUri().getHost();
rm.text = requestContext.getUriInfo().getPath(true);
rm.requestid = requestid;
}
Basically everything I have tried creates another Uni. Maybe I am simply using the concept all wrong. But how do I get the Integer out of the Uni so I can get the intValue?
You need to invoke a terminal operation, or use the value and continue the chain.
If you want to invoke a terminal operator you can invoke the await operation to make your code blocking and wait for the response.
If you want to merge this reactive invocation with another that is present in your client code, you can join or combine your actual Mutiny stream with the on coming from the response by using the combine method.
If you just want to use the value and do not retrieve it, you can suscribe and get the result.
If you have a multi you can call directly the method toList
Assuming that you want to have some timeouts involved and you want to get the actual Integer, you can go with the await method and a timeout.

How to get spring cache size in spring boot?

I have two methods as follows:
#Cacheable(cacheNames = "foos")
public List<FooDto> getAllFoos() {
return this.fooRepository.findAll().stream()
.map(FooEntityDomainToDtoMapper::mapDomainToDto) // mapping entity to dto
.collect(Collectors.toList());
}
#Cacheable(cacheNames = "foos",key = "#fooDto.Id")
public FooDto getFooById(FooDto fooDto) {
return this.fooRepository.findById(fooDto.getId()).stream()
.map(FooEntityDomainToDtoMapper::mapDomainToDto) // mapping entity to dto
.collect(Collectors.toList());
}
First getAllFoos() will be called during the System startup and second will be called after starting the system when user request object by particular id. I wanted to know whether second method will occupy any separate cache space or it'll simply add keys in the cache obtained in the first method? I want to confirm that even if i comment the second getFooById() whether or not the size of the cache will be the same? Is there any way to get the size of cache?
P.S: we are not using any implementation of cache, just using spring-boot-starter-cache
Although, there is no direct method through which you can see cache data and get size but you can do that by using reflection. Here is what I've done.
public Object getAllCache() {
//Autowire your cache manager first and then process the request using cache manager
ConcurrentMapCache cache = (ConcurrentMapCache) manager.getCache("yourCachename");
Object store1 = null;
try {
Field store = ConcurrentMapCache.class.getDeclaredField("store");
store.setAccessible(true);
store1 = store.get(cache);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return store1.toString();
}

How to limit number of results in ReactiveMongoRepository

I am looking for a way to pass the limit to the mongo query in ReactiveCrudRepository
I tried adding "First2" to the method name but I'm still getting all the results.
What I'm really looking for is a way to pass the value of the 'limit' to the method, passing it in request as #RequestParam int limit
This is my code for the repository
public interface ReactiveUserRepository
extends ReactiveCrudRepository<User, String> {
#Query("{ 'roles': ?0 }")
Flux<User> findFirst2ByRole(String role);
}
And this is controller method:
#GetMapping(path = "/byrole", produces = "application/stream+json")
Flux<User> getByRole(#RequestParam String role) {
return users.findFirst2ByRole(role).doOnNext(next -> {
System.out.println("Next user=" + next.getAssocId());
}).switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND, String.format("No users found with role=%s", role))));
}
limitRequest(long) on a Flux may also be worth to have a look at: https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html#limitRequest-long-
it can be used as a stricter form of take(long)
As it does cap the request amount that goes to the upstream source. This may prevent the reactive MongoDB driver from requesting/loading a huge batch of data when only a limited number is needed.
When using limitRequest, make sure that the provided long is "> 0" (<0 makes no sense and =0 results in a never completing Flux)
try to use reactor method users.findFirst2ByRole(role).take(2)
as well you can use skip() if needed

Non-Blocking Endpoint: Returning an operation ID to the caller - Would like to get your opinion on my implementation?

Boot Pros,
I recently started to program in spring-boot and I stumbled upon a question where I would like to get your opinion on.
What I try to achieve:
I created a Controller that exposes a GET endpoint, named nonBlockingEndpoint. This nonBlockingEndpoint executes a pretty long operation that is resource heavy and can run between 20 and 40 seconds.(in the attached code, it is mocked by a Thread.sleep())
Whenever the nonBlockingEndpoint is called, the spring application should register that call and immediatelly return an Operation ID to the caller.
The caller can then use this ID to query on another endpoint queryOpStatus the status of this operation. At the beginning it will be started, and once the controller is done serving the reuqest it will be to a code such as SERVICE_OK. The caller then knows that his request was successfully completed on the server.
The solution that I found:
I have the following controller (note that it is explicitely not tagged with #Async)
It uses an APIOperationsManager to register that a new operation was started
I use the CompletableFuture java construct to supply the long running code as a new asynch process by using CompletableFuture.supplyAsync(() -> {}
I immdiatelly return a response to the caller, telling that the operation is in progress
Once the Async Task has finished, i use cf.thenRun() to update the Operation status via the API Operations Manager
Here is the code:
#GetMapping(path="/nonBlockingEndpoint")
public #ResponseBody ResponseOperation nonBlocking() {
// Register a new operation
APIOperationsManager apiOpsManager = APIOperationsManager.getInstance();
final int operationID = apiOpsManager.registerNewOperation(Constants.OpStatus.PROCESSING);
ResponseOperation response = new ResponseOperation();
response.setMessage("Triggered non-blocking call, use the operation id to check status");
response.setOperationID(operationID);
response.setOpRes(Constants.OpStatus.PROCESSING);
CompletableFuture<Boolean> cf = CompletableFuture.supplyAsync(() -> {
try {
// Here we will
Thread.sleep(10000L);
} catch (InterruptedException e) {}
// whatever the return value was
return true;
});
cf.thenRun(() ->{
// We are done with the super long process, so update our Operations Manager
APIOperationsManager a = APIOperationsManager.getInstance();
boolean asyncSuccess = false;
try {asyncSuccess = cf.get();}
catch (Exception e) {}
if(true == asyncSuccess) {
a.updateOperationStatus(operationID, Constants.OpStatus.OK);
a.updateOperationMessage(operationID, "success: The long running process has finished and this is your result: SOME RESULT" );
}
else {
a.updateOperationStatus(operationID, Constants.OpStatus.INTERNAL_ERROR);
a.updateOperationMessage(operationID, "error: The long running process has failed.");
}
});
return response;
}
Here is also the APIOperationsManager.java for completness:
public class APIOperationsManager {
private static APIOperationsManager instance = null;
private Vector<Operation> operations;
private int currentOperationId;
private static final Logger log = LoggerFactory.getLogger(Application.class);
protected APIOperationsManager() {}
public static APIOperationsManager getInstance() {
if(instance == null) {
synchronized(APIOperationsManager.class) {
if(instance == null) {
instance = new APIOperationsManager();
instance.operations = new Vector<Operation>();
instance.currentOperationId = 1;
}
}
}
return instance;
}
public synchronized int registerNewOperation(OpStatus status) {
cleanOperationsList();
currentOperationId = currentOperationId + 1;
Operation newOperation = new Operation(currentOperationId, status);
operations.add(newOperation);
log.info("Registered new Operation to watch: " + newOperation.toString());
return newOperation.getId();
}
public synchronized Operation getOperation(int id) {
for(Iterator<Operation> iterator = operations.iterator(); iterator.hasNext();) {
Operation op = iterator.next();
if(op.getId() == id) {
return op;
}
}
Operation notFound = new Operation(-1, OpStatus.INTERNAL_ERROR);
notFound.setCrated(null);
return notFound;
}
public synchronized void updateOperationStatus (int id, OpStatus newStatus) {
iteration : for(Iterator<Operation> iterator = operations.iterator(); iterator.hasNext();) {
Operation op = iterator.next();
if(op.getId() == id) {
op.setStatus(newStatus);
log.info("Updated Operation status: " + op.toString());
break iteration;
}
}
}
public synchronized void updateOperationMessage (int id, String message) {
iteration : for(Iterator<Operation> iterator = operations.iterator(); iterator.hasNext();) {
Operation op = iterator.next();
if(op.getId() == id) {
op.setMessage(message);
log.info("Updated Operation status: " + op.toString());
break iteration;
}
}
}
private synchronized void cleanOperationsList() {
Date now = new Date();
for(Iterator<Operation> iterator = operations.iterator(); iterator.hasNext();) {
Operation op = iterator.next();
if((now.getTime() - op.getCrated().getTime()) >= Constants.MIN_HOLD_DURATION_OPERATIONS ) {
log.info("Removed operation from watchlist: " + op.toString());
iterator.remove();
}
}
}
}
The questions that I have
Is that concept a valid one that also scales? What could be improved?
Will i run into concurrency issues / race conditions?
Is there a better way to achieve the same in boot spring, but I just didn't find that yet? (maybe with the #Async directive?)
I would be very happy to get your feedback.
Thank you so much,
Peter P
It is a valid pattern to submit a long running task with one request, returning an id that allows the client to ask for the result later.
But there are some things I would suggest to reconsider :
do not use an Integer as id, as it allows an attacker to guess ids and to get the results for those ids. Instead use a random UUID.
if you need to restart your application, all ids and their results will be lost. You should persist them to a database.
Your solution will not work in a cluster with many instances of your application, as each instance would only know its 'own' ids and results. This could also be solved by persisting them to a database or Reddis store.
The way you are using CompletableFuture gives you no control over the number of threads used for the asynchronous operation. It is possible to do this with standard Java, but I would suggest to use Spring to configure the thread pool
Annotating the controller method with #Async is not an option, this does not work no way. Instead put all asynchronous operations into a simple service and annotate this with #Async. This has some advantages :
You can use this service also synchronously, which makes testing a lot easier
You can configure the thread pool with Spring
The /nonBlockingEndpoint should not return the id, but a complete link to the queryOpStatus, including id. The client than can directly use this link without any additional information.
Additionally there are some low level implementation issues which you may also want to change :
Do not use Vector, it synchronizes on every operation. Use a List instead. Iterating over a List is also much easier, you can use for-loops or streams.
If you need to lookup a value, do not iterate over a Vector or List, use a Map instead.
APIOperationsManager is a singleton. That makes no sense in a Spring application. Make it a normal PoJo and create a bean of it, get it autowired into the controller. Spring beans by default are singletons.
You should avoid to do complicated operations in a controller method. Instead move anything into a service (which may be annotated with #Async). This makes testing easier, as you can test this service without a web context
Hope this helps.
Do I need to make database access transactional ?
As long as you write/update only one row, there is no need to make this transactional as this is indeed 'atomic'.
If you write/update many rows at once you should make it transactional to guarantee, that either all rows are updated or none.
However, if two operations (may be from two clients) update the same row, always the last one will win.

Resources