Unable to load value from Redis alone even if value is present - java

I am using Reactive Redis where I am trying to use Redis as cache for database. I am checking if value is present in the cache or not? If it is present then return it otherwise query database if result comes back; store the result cache it and return it.
However, even if value is present in Redis it is still querying the database all the time.
public Mono<User> getUser(String email) {
return reactiveRedisOperation.opsForValue().get("tango").switchIfEmpty(
// Always getting into this block (for breakpoint) :(
queryDatabase().flatMap(it -> {
reactiveRedisOperation.opsForValue().set("tango", it, Duration.ofSeconds(3600)).then(Mono.just(it));
})
);
}
private Mono<User> queryDatabase() {
return Mono.just(new User(2L,"test","test","test","test","test",true,"test","test","test"));
}
But call is always hitting the database even if value is present in Redis. What am I doing wrong here?

Base on this answer you can try with Mono.defer:
public Mono<User> getUser(String email) {
return reactiveRedisOperation.opsForValue().get("tango").switchIfEmpty(Mono.defer(() -> {
// Always getting into this block (for breakpoint) :(
queryDatabase().flatMap(it -> {
reactiveRedisOperation.opsForValue().set("tango", it, Duration.ofSeconds(3600)).then(Mono.just(it));
})})
);
}
UPDATE:
I don't have much experience with Mono. The answer that I pointed explain it:
... computation was already triggered at the point when we start composing our Mono types. To prevent unwanted computations we can wrap our future into a defered evaluation:
... is trapped in a lazy supplier and is scheduled for execution only when it will be requested.

Related

Getting a value from an inside an Optional with isPresent()

I have a User and associated time-limited Roles. I want to know if a User has a particular UserRole and it is unexpired. I can turn the user's roles into a stream, filter() it and findFirst(), giving me an Optional.
Role
public class Role {
private UserRole role;
private Date expiry;
public boolean isUnexpired () {
return (expiry == null) ? true : expiry.after(new Date());
}
}
User
public class User {
//...
private Collection<Role> roles
public boolean hasRole (UserRole userRole) {
return roles.stream()
.filter(r -> r.getRole().equals(userRole))
.findFirst()
.ifPresent(ur -> { /* ... herein the problem ... */ ur.isUnexpired(); } );
}
}
The problem in that last line is that ifPresent() has a void signature; as such, I can't return ur.isUnexpired() from it. Whatever I put in the lambda expression or anonymous inner class at that point can't do anything meaningfully with the value it finds.
I tried declaring a boolean before filtering the stream, and assigning it, but get the (code validation) error: local variables referenced from a lambda expression must be final or effectively final.
(I know, there's a bit more to handle when it's not present; if I can sort this, I can swap to ifPresentOrElse().)
I could do the following:
public boolean hasRole (UserRole userRole) {
Optional<Role> o = roles.stream()
.filter(r -> r.getRole().equals(userRole))
.findFirst();
return o.isPresent() ? o.get().isUnexpired() : false;
}
However, I would rather do it with a cleaner, chained function.
Is there some way to extract and use my isUnexpired() boolean with a chained function? Or must I assign the Optional then operate on it separately?
You should use Optional::map to retrieve value of isUnexpired, and orElse to return false:
public boolean hasRole (UserRole userRole) {
return roles.stream()
.filter(r -> r.getRole().equals(userRole))
.findFirst()
.map(Role::isUnexpired)
.orElse(false);
}
However, I would rather do it with a cleaner, chained function.
Why would a chained function be 'cleaner'? As you can see, it makes it much more difficult to adapt code to changing requirements, and enforces weird style choices on your other code in order to dance around the fact that you can't use mutable local variables and don't get control flow or checked exception transparency. I don't know what definition of 'cleaner' you're working on, but clearly it's not "code that leads to easier to modify, easier to test, easier to read, easier to fit inside code with other requirements", which seems like a much more sensible definition to me. Perhaps your definition is based in aesthetics. Well, as they say, you can't argue with taste, perhaps.
At any rate, you have two options:
Map optional.NONE onto a sentinel that then fails the test.
You can simply use .orElse() here:
...
.findFirst()
.orElse(dummyRoleThatIsDefinitelyExpired)
.isUnexpired();
Map the optional.SOME
...
.findFirst()
.map(r -> r.isUnexpired())
.orElse(false);
The map call will return either an Optional.NONE, or an Optional of Boolean.TRUE or an optional of Boolean.FALSE. We then orElse the NONE case onto FALSE and we now have a boolean to return.
I'd say the first snippet is easier to follow, but it requires having a dummy role that is definitely expired.
NB: If you care about clean functions, sticking negatives in boolean method names is not a good idea for obvious reasons. Instead of Unexpired, perhaps isLive() or isValid() is a better idea.

Why does JPA Repository save method does not update entity details on production?

I am using spring data JPA I am trying to update the entity using the JPARepository method save() to update the entity details. Code I have written to update the entity works fine in development as expected. But the same code does not work on the production server it does not give any error only the code written inside the map() does not work. Below is my code
public Long updateQrCodeUrlByBusinessDetails(Long businessId, String menuUrl, MENU_TYPE menuType) {
return qrCodeRepo.findByBusinessId(businessId).businessQrCode.stream().map(qr -> {
qr.setMenuUrl(menuUrl);
qr.setMenuType(menuType);
return qrCodeRepo.save(qr);
}).count();
}
The problem is that count
may choose to not execute the stream pipeline (either sequentially or in parallel) if it is capable of computing the count directly from the stream source. In such cases no source elements will be traversed and no intermediate operations will be evaluated. Behavioral parameters with side-effects, which are strongly discouraged except for harmless cases such as debugging, may be affected
And exactly that happens in your case. The solution is to neither use map or count at all since you do not need / use them for what they are supposed to be used for and instead use a single forEach instead:
public void updateQrCodeUrlByBusinessDetails(Long businessId, String menuUrl, MENU_TYPE menuType) {
List<BusinessQRCode> businessQrCode = qrCodeRepo.findByBusinessId(businessId);
businessQrCode.stream().forEach(qr -> {
qr.setMenuUrl(menuUrl);
qr.setMenuType(menuType);
qrCodeRepo.save(qr);
});
}
Note that you could just do the exact same thing with a for loop with less calls, same amount of lines and without unnecessarily using streams:
public void updateQrCodeUrlByBusinessDetails(Long businessId, String menuUrl, MENU_TYPE menuType) {
List<BusinessQRCode> businessQrCode = qrCodeRepo.findByBusinessId(businessId);
for (BusinessQRCode qr : businessQrCode) {
qr.setMenuUrl(menuUrl);
qr.setMenuType(menuType);
qrCodeRepo.save(qr);
};
}
just my two cents.
You should always return Optional from query results. You may have it as:
In repository:
Optional<List<BusinessQRCode>> findByBusinessId(Long businessId);
In Service:
public void updateQrCodeUrlByBusinessDetails(Long businessId, String menuUrl, MENU_TYPE menuType) {
// Fetch iterate, if exist
qrCodeRepo.findByBusinessId(businessId).ifPresent(qrCodes_ -> {
qrCodes_.forEach(code_-> {
qr.setMenuUrl(menuUrl);
qr.setMenuType(menuType);
});
qrCodeRepo.saveAll(qrCodes_);
});
Why use map when you are returning same qr. You can skip that part and also the collect function. We already have the list of qrCodes, just iterate them and set the values then save all at once.
After making some changes in code it work as expected on development and production. I just change the object value in the map() and return after that collect the object to list using the Collectors.toList() method and again use forEach() loop and update the entity data. Now code works as expected.
public void updateQrCodeUrlByBusinessDetails(Long businessId, String menuUrl, MENU_TYPE menuType) {
List<BusinessQRCode> businessQrCode = qrCodeRepo.findByBusinessId(businessId);
businessQrCode.stream().map(qr -> {
qr.setMenuUrl(menuUrl);
qr.setMenuType(menuType);
return qr;
}).collect(Collectors.toList()).forEach(qr -> qrCodeRepo.save(qr));
}

Java Reactive stream how to map an object when the object being mapped is also needed on the next step of the stream

I am using Java 11 and project Reactor (from Spring). I need to make a http call to a rest api (I can only make it once in the whole flow).
With the response I need to compute two things:
Check if a document exists in the database (mongodb). If it does not exists then create it and return it. Otherwise just return it.
Compute some logic on the response and we are done.
In pseudo code it is something like this:
public void computeData(String id) {
httpClient.getData(id) // Returns a Mono<Data>
.flatMap(data -> getDocument(data.getDocumenId()))
// Issue here is we need access to the data object consumed in the previous flatMap but at the same time we also need the document object we get from the previous flatMap
.flatMap(document -> calculateValue(document, data))
.subscribe();
}
public Mono<Document> getDocument(String id) {
// Check if document exists
// If not create document
return document;
}
public Mono<Value> calculateValue(Document doc, Data data) {
// Do something...
return value;
}
The issue is that calculateValue needs the return value from http.getData but this was already consumed on the first flatMap but we also need the document object we get from the previous flatMap.
I tried to solve this issue using Mono.zip like below:
public void computeData(String id) {
final Mono<Data> dataMono = httpClient.getData(id);
Mono.zip(
new Mono<Mono<Document>>() {
#Override
public void subscribe(CoreSubscriber<? super Mono<Document>> actual) {
final Mono<Document> documentMono = dataMono.flatMap(data -> getDocument(data.getDocumentId()))
actual.onNext(documentMono);
}
},
new Mono<Mono<Value>>() {
#Override
public void subscribe(CoreSubscriber<? super Mono<Value>> actual) {
actual.onNext(dataMono);
}
}
)
.flatMap(objects -> {
final Mono<Document> documentMono = objects.getT1();
final Mono<Data> dataMono = objects.getT2();
return Mono.zip(documentMono, dataMono, (document, data) -> calculateValue(document, data))
})
}
But this is executing the httpClient.getData(id) twice which goes against my constrain of only calling it once. I understand why it is being executed twice (I subscribe to it twice).
Maybe my solution design can be improved somewhere but I do not see where. To me this sounds like a "normal" issue when designing reactive code but I could not find a suitable solution to it so far.
My question is, how can accomplish this flow in a reactive and non blocking way and only making one call to the rest api?
PS; I could add all the logic inside one single map but that would force me to subscribe to one of the Mono inside the map which is not recommended and I want to avoid following this approach.
EDIT regarding #caco3 comment
I need to subscribe inside the map because both getDocument and calculateValue methods return a Mono.
So, if I wanted to put all the logic inside one single map it would be something like:
public void computeData(String id) {
httpClient.getData(id)
.map(data -> getDocument(data).subscribe(s -> calculateValue(s, data)))
.subscribe();
}
You do not have to subscribe inside map, just continue building the reactive chain inside the flatMap:
getData(id) // Mono<Data>
.flatMap(data -> getDocument(data.getDocumentId()) // Mono<Document>
.switchIfEmpty(createDocument(data.getDocumentId())) // Mono<Document>
.flatMap(document -> calculateValue(document, data)) // Mono<Value>
)
.subscribe()
Boiling it down, your problem is analogous to:
Mono.just(1)
.flatMap(original -> process(original))
.flatMap(processed -> I need access to the original value and the processed value!
System.out.println(original); //Won't work
);
private static Mono<String> process(int in) {
return Mono.just(in + " is an integer").delayElement(Duration.ofSeconds(2));
}
(Silly example, I know.)
The problem is that map() (and by extension, flatMap()) are transformations - you get access to the new value, and the old one goes away. So in your second flatMap() call, you've got access to 1 is an integer, but not the original value (1.)
The solution here is to, instead of mapping to the new value, map to some kind of merged result that contains both the original and new values. Reactor provides a built in type for that - a Tuple. So editing our original example, we'd have:
Mono.just(1)
.flatMap(original -> operation(original))
.flatMap(processed -> //Help - I need access to the original value and the processed value!
System.out.println(processed.getT1()); //Original
System.out.println(processed.getT2()); //Processed
///etc.
);
private static Mono<Tuple2<Integer, String>> operation(int in) {
return Mono.just(in + " is an integer").delayElement(Duration.ofSeconds(2))
.map(newValue -> Tuples.of(in, newValue));
}
You can use the same strategy to "hold on" to both document and data - no need for inner subscribes or anything of the sort :-)

What is a simple way to Cache a List (or other Collection) in Java

I have a Collection in the form of a List pulled out of the database that will never change once it is pulled; every user in the system will see the same thing. I have been trying to figure out the simplest way to cache it. I know I can use CacheBuilder from Guava, but it seems like overkill to create a cached map with 1 item in it that never changes.
From #BenManes above, I used Suppliers.memoize:
private Supplier<Collection<Person>> cache = Suppliers.memoizeWithExpiration(
new Supplier<Collection<Employee>>() {
public Collection<Employee> get() {
return getAllEmployees();
}
}, 1, TimeUnit.DAYS);
public Collection<Employee> getAllEmployees() {
return cache.get();
}

F.Promise<Result> and nested async promises

I am using F.Promise in my async action in Playframework 2.4.6. I am able to get async calls to my DAO to work. I am able to use "map" and "flatMap" but I am not sure about the following situation.
I have an async call to find and object from the database. If that object is found, I then want to use parts of that object to then issue another async request to update that object in the database. This is part of an async update call in my controller. I am not doing something right though.
I am starting off with the following.
F.Promise<User> findUserPromise = userService.findAsync(id);
F.Promise<User> updateUserPromise = userService.updateAsync(updatedUser);
F.Promise<Result> resultPromise = findUserPromise.flatMap((foundUser){
// update foundUser with passed in Json criteria
foundUser.firstName = firstName; // etc...
return updateUserPromise.map((updatedUser) -> {
return ok(Json.toJson(u));
});
});
return resultPromise;
This doesn't seem to work. I do not want to define my updatedUserPromise until I get a response back from my findUserPromise, because maybe a user will not be found. If a user is not found, I will return F.Promise.pure(notFound("some json result")). If a user is found, then I want to update that user with the Json criteria that gets posted to my controller action. With my approach, it seems that I have to define the updateUserPromise's async call with an object (updatedUser) before I get back any results.
Can I define another promise inside the result of a promise?
I was thinking something like this, but this fails with compilation errors. inference variable B has incompatible bounds, equality constraints: play.mvc.Result, lower bounds: Play.libs.F.Promise.
F.Promise<User> findUserPromise = userService.findAsync(id);
F.Promise<Result> resultPromise = findUserPromise.map((foundUser) -> {
// foundUser is returned, now update it, or return not found...
// define our next promise with an updated foundUser object
F.Promise<User> userPromise = userService.updateAsync(foundUser);
F.Promise<Result> resultPromise2 = userPromise.map((u) -> {
return jsonResult(ok(Json.toJson(u)));
});
return resultPromise2;
});
return resultPromise;
Thanks for any help.
I am not sure if this is best way to do this, but this is working for my situation above.
F.Promise<User> findUserPromise = userService.findAsync(id);
return findUserPromise.flatMap((fu) -> {
if (fu == null) {
ObjectNode result = Json.newObject();
result.put("error", "Not found " + id);
return F.Promise.pure(jsonResult(notFound(result)));
}
// dynamically update fields on found user
fu.userName = userName;
fu.firstName = firstName;
// more updates...
// make a new promise that will use the updated foundUser object
F.Promise<User> updateUserPromise = userService.updateAsync(fu);
return updateUserPromise.map((uu) -> {
return jsonResult(ok(Json.toJson(uu)));
});
}
All the examples I found for chaining promises or nested promises, they seem to use web services as an example, and they all appear to be known at design time. In my situation, I do not know what my foundUser object will be until it is returned, so I need to create my inner promise inside the outer promise.

Categories