The best practice for multi-thread test to catch race condition - spring

I have small rest controller for parking service.
#PostMapping("/entry")
public ResponseEntity<ResponseObject> carEntry(#Valid #RequestBody CarAtParkingGateRequest carAtParkingGateRequest) {
return parkingService.carEntry(carAtParkingGateRequest);
}
In CarAtParkingGateRequest I am sending pair of ID's - car and parking.
Method parkingService.carEntry looks almost like:
public ResponseEntity<ResponseObject> carEntry(CarAtParkingGateRequest carAtParkingGateRequest) {
ParkingEntity parking = findParking(carAtParkingGateRequest.getParkingID());
CarEntity car = carService.findCar(carAtParkingGateRequest.getCarID());
if (car.getPowerType().equals(PowerType.ELECTRIC) &&
carRepository.getNumberOfElectricCarsOnParking(parking.getIdParking()) == parking.getNumberOfChargers()) {
return new ResponseEntity<>(new ResponseObject("Every charger is taken"), HttpStatus.BAD_REQUEST);
} else if (!car.getPowerType().equals(PowerType.ELECTRIC) &&
carRepository.getNumberOfNotElectricCarsOnParking(parking.getIdParking()) == parking.getNumberOfParkingSlots()) {
return new ResponseEntity<>(new ResponseObject("Every slot is taken"), HttpStatus.BAD_REQUEST);
} else {
parking.addCar(car);
return new ResponseEntity<>(
new ResponseObject("Gate up"), HttpStatus.OK);
}
}
Now I want to work on race condition and prepare test for it. I think, I have to use 2 threads with different requests which will be starting at the same time, but I do not know exacly how should it looks - how to send 2 requests in the same time for unit case

Related

Spring Webflux - R2dbc : How to run a child query and update value while iterating a result set

I am new to Reactive repositories and webflux. I am fetching a list of data from DB, iterating it using map() to build a DTO class object, in this process I need to run another query to get the count value and update the same DTO object. When I try as follows, the count is set to null
#Repository
public class CandidateGroupCustomRepo {
public Flux<CandidateGroupListDTO> getList(BigInteger userId){
final String sql = "SELECT gp.CANDIDATE_GROUP_ID,gp.NAME ,gp.GROUP_TYPE \n" +
" ,gp.CREATED_DATE ,cd.DESCRIPTION STATUS ,COUNT(con.CANDIDATE_GROUP_ID)\n" +
" FROM ........" +
" WHERE gp.CREATED_BY_USER_ID = :userId GROUP BY gp.CANDIDATE_GROUP_ID,gp.NAME ,gp.GROUP_TYPE \n" +
" ,gp.CREATED_DATE ,cd.DESCRIPTION";
return dbClient.execute(sql)
.bind("userId", userId)
.map(row ->{
CandidateGroupListDTO info = new CandidateGroupListDTO();
info.setGroupId(row.get(0, BigInteger.class));
info.setGroupName(row.get(1, String.class)) ;
info.setGroupType(row.get(2, String.class));
info.setCreatedDate( row.get(3, LocalDateTime.class));
info.setStatus(row.get(4, String.class));
if(info.getGroupType().equalsIgnoreCase("static")){
info.setContactsCount(row.get(5, BigInteger.class));
}else{
getGroupContactCount(info.getGroupId()).subscribe(count ->{
System.out.println(">>>>>"+count);
info.setContactsCount(count);
});
}
return info;
}
)
.all() ;
}
Mono<BigInteger> getGroupContactCount(BigInteger groupId){
final String sql = "SELECT 3 WHERE :groupId IS NOT NULL;";
return dbClient.execute(sql)
.bind("groupId", groupId)
.map(row -> {
System.out.println(row.get(0, BigInteger.class));
return row.get(0, BigInteger.class);
} ).one();
}
}
When I call getGroupContactCount, I am trying to extract count from Mono<BigInteger> and set it in my DTO.... sys out prints the count value correctly but still I get null for count in response.
You are calling subscribe in the middle which in turn is essentially blocking. The one subscribing is usually the final consumer, which im guessing your spring application is not, most likely the final consumer is the webpage that initiated the call. Your server is the producer.
call the database, flatMap and return.
return dbClient.execute(sql)
.bind("userId", userId)
.flatMap(row ->{
CandidateGroupListDTO info = new CandidateGroupListDTO();
info.setGroupId(row.get(0, BigInteger.class));
info.setGroupName(row.get(1, String.class)) ;
info.setGroupType(row.get(2, String.class));
info.setCreatedDate( row.get(3, LocalDateTime.class));
info.setStatus(row.get(4, String.class));
if(info.getGroupType().equalsIgnoreCase("static")){
return Mono.just(info.setContactsCount(row.get(5, BigInteger.class)));
} else {
return getGroupContactCount(info.getGroupId()).flatMap(count -> {
info.setContactsCount(count);
return Mono.just(info)
});
}
}).all();
Use map if order matters, otherwise try to use flatMap to do async work.

Do I need to deal with concurrency?

I have an application where I am managing documents. I would like to ask you whether I need to deal with concurrency.
Lets say, I will have the method below (which is in the class with #Service and #Transactional) and more requests would come which would require to use this function.
Will spring and database handle concurrency without synchronization? (my db is MySQL and JPA). So the first request to use this method will be executed, but another request will wait till the previous request will be done... so it would not happen that something would be overwritten in the database...
Thanks for help
public void updateSharing(long userId, long documentId, int approval) {
Optional<Document> optionalDocument = documentRepository.findById(documentId);
User user = userService.findUserById(userId);
if(optionalDocument.isPresent()){
Document document = optionalDocument.get();
if(document.getDocumentState().getId() == 2){
documentRepository.updateSharing(userId, documentId, approval);
if(approval == 0){
List<User> users = userService.getUsersForApprovingDocument(documentId);
Map<String, String> map = emailService.createMessage(2, user, document);
if(document.getUser().isActive()){
users.add(document.getUser());
}
setDocumentType(documentId, 3);
sendEmail(users, map.get("subject"), map.get("message"));
} else if(isDocumentApproved(documentId)){
setDocumentType(documentId, 1);
List<User> users = userService.getUsersForApprovingDocument(documentId);
if(document.getUser().isActive()){
users.add(document.getUser());
}
Map<String, String> map = emailService.createMessage(5, user, document);
sendEmail(users, map.get("subject"), map.get("message"));
}
} else if(document.getDocumentState().getId() == 1){
documentRepository.updateSharing(userId, documentId, approval);
} else {
return;
}
}
}
You don't need to deal with concurrency in this situation.
For every request, the container creates a new Thread and each Thread has it's own stack.

Hibernate queries getting slower and slower

I'm working on a process that checks and updates data from Oracle database. I'm using hibernate and spring framework in my application.
The application reads a csv file, processes the content, then persiste entities :
public class Main() {
Input input = ReadCSV(path);
EntityList resultList = Process.process(input);
WriteResult.write(resultList);
...
}
// Process class that loops over input
public class Process{
public EntityList process(Input input) :
EntityList results = ...;
...
for(Line line : input.readLine()){
results.add(ProcessLine.process(line))
...
}
return results;
}
// retrieving and updating entities
Class ProcessLine {
#Autowired
DomaineRepository domaineRepository;
#Autowired
CompanyDomaineService companydomaineService
#Transactional
public MyEntity process(Line line){
// getcompanyByXX is CrudRepository method with #Query that returns an entity object
MyEntity companyToAttach = domaineRepository.getCompanyByCode(line.getCode());
MyEntity companyToDetach = domaineRepository.getCompanyBySiret(line.getSiret());
if(companyToDetach == null || companyToAttach == null){
throw new CustomException("Custom Exception");
}
// AttachCompany retrieves some entity relationEntity, then removes companyToDetach and adds CompanyToAttach. this updates relationEntity.company attribute.
companydomaineService.attachCompany(companyToAttach, companyToDetach);
return companyToAttach;
}
}
public class WriteResult{
#Autowired
DomaineRepository domaineRepository;
#Transactional
public void write(EntityList results) {
for (MyEntity result : results){
domaineRepository.save(result)
}
}
}
The application works well on files with few lines, but when i try to process large files (200 000 lines), the performance slows drastically, and i get a SQL timeout.
I suspect cache issues, but i'm wondering if saving all the entities at the end of the processing isn't a bad practice ?
The problem is your for loop which is doing individual saves on the result and thus does single inserts slowing it down. Hibernate and spring support batch inserts and should be done when ever possible.
something like domaineRepository.saveAll(results)
Since you are processing lot of data it might be better to do things in batches so instead of getting one company to attach you should get a list of companies to attach processes those then get a list of companies to detach and process those
public EntityList process(Input input) :
EntityList results;
List<Code> companiesToAdd = new ArrayList<>();
List<Siret> companiesToRemove = new ArrayList<>();
for(Line line : input.readLine()){
companiesToAdd.add(line.getCode());
companiesToRemove.add(line.getSiret());
...
}
results = process(companiesToAdd, companiesToRemove);
return results;
}
public MyEntity process(List<Code> companiesToAdd, List<Siret> companiesToRemove) {
List<MyEntity> attachList = domaineRepository.getCompanyByCodeIn(companiesToAdd);
List<MyEntity> detachList = domaineRepository.getCompanyBySiretIn(companiesToRemove);
if (attachList.isEmpty() || detachList.isEmpty()) {
throw new CustomException("Custom Exception");
}
companydomaineService.attachCompany(attachList, detachList);
return attachList;
}
The above code is just sudo code to point you in the right direction, will need to work out what works for you.
For every line you read you are doing 2 read operations here
MyEntity companyToAttach = domaineRepository.getCompanyByCode(line.getCode());
MyEntity companyToDetach = domaineRepository.getCompanyBySiret(line.getSiret());
You can read more than one line and us the in query and then process that list of companies

How to check if Mono is empty?

I'm developing a app with Spring Boot 2.0 and Kotlin using the WebFlux framework.
I want to check if a user id exits before save a transaction. I'm stucked in a simple thing like validate if a Mono is empty.
fun createTransaction(serverRequest: ServerRequest) : Mono<ServerResponse> {
val transaction = serverRequest.body(BodyExtractors.toMono(Transaction::class.java))
transaction.flatMap {
val user = userRepository.findById(it.userId)
// If it's empty, return badRequest()
}
return transaction.flatMap { transactionRepository.save(it).then(created(URI.create("/transaction/" + it.id)).build()) }
}
It is possible to do what I want?
The techniques that allow checking whether Flux/Mono is empty
Using operators .switchIfEmpty/.defaultIfEmpty/Mono.repeatWhenEmpty
Using mentioned operators you will be able to react to the case when Stream has been completed without emitting any elements.
First of all, remember that operators such .map, .flatMap, .filter and many others will not be invoked at all if there no onNext has been invoked.
That means that in your case next code
transaction.flatMap {
val user = userRepository.findById(it.userId)
// If it's empty, return badRequest()
}
return transaction.flatMap { transactionRepository.save(it).then(created(URI.create("/transaction/" + it.id)).build()) }
will not be invoked at all, if transaction will be empty.
In case if there is a requirement for handling cases when your flow is empty, you should consider operators like next in the following manner:
transaction
.flatMap(it -> {
val user = userRepository.findById(it.userId)
})
.swithIfEmpty(Flux.defer(() -> Flux.just(badRequest())));
Actual solution
Also, I have noted that you created two sub-flows from the main transaction. Actually, following code will not be executed at all:
transaction.flatMap {
val user = userRepository.findById(it.userId)
// If it's empty, return badRequest()
}
and will be only executed the last one, which is returned from the method. That happens because you ain't subscribed using operator .subscribe(...).
The second point, you can't subscribe to the same request body more the one time (kind of limitation for WebClient's reponse). Thus you are required to share your request body in the next way, so completed example will be:
fun createTransaction(serverRequest: ServerRequest): Mono<ServerResponse> {
val transaction = serverRequest.body(BodyExtractors.toMono(Transaction::class.java)).cache()
transaction
.flatMap { userRepository.findById(it.userId) }
.flatMap { transaction.flatMap { transactionRepository.save(it) } }
.flatMap { ServerResponse.created(URI.create("/transaction/" + it.id)).build() }
.switchIfEmpty(transaction.flatMap { ServerResponse.badRequest().syncBody("missed User for transaction " + it.id) })
}
Or more simple case without sharing transaction flow but using Tuple:
fun createTransaction(serverRequest: ServerRequest): Mono<ServerResponse> {
val emptyUser = !User()
val transaction = serverRequest.body<Mono<Transaction>>(BodyExtractors.toMono(Transaction::class.java))
transaction
.flatMap { t ->
userRepository.findById(t.userId)
.map { Tuples.of(t, it) }
.defaultIfEmpty(Tuples.of(t, emptyUser))
}
.flatMap {
if (it.t2 != emptyUser) {
transactionRepository.save(it.t1)
.flatMap { ServerResponse.created(URI.create("/transaction/" + it.id)).build() }
} else {
ServerResponse.badRequest().syncBody("missed User for transaction " + it.t1.id)
}
}
}
You can check it using the Mono's provided method hasElement() which is analogous to Optional's isPresent(). The method definition is :
Mono<Boolean> hasElement()
for more details checkout : project reactor documentation
In case you have to perform some action based on this value you can further use switchIfEmpty() to provide with alternate publisher.
Let me start by saying I am a newbie on reactive (java) and on this forum.
I think you cannot really check in this code if a mono is empty because a mono represents code that will be executed later on, so in this code body you won't know yet if its is empty. Does that make sense?
I just wrote something similar in Java which seems to work (but not 100% this is the best approach either):
public Mono<ServerResponse> queryStore(ServerRequest request) {
Optional<String> postalCode = request.queryParam("postalCode");
Mono<ServerResponse> badQuery = ServerResponse.badRequest().build();
Mono<ServerResponse> notFound = ServerResponse.notFound().build();
if (!postalCode.isPresent()) { return badQuery; }
Flux<Store> stores = this.repository
.getNearByStores(postalCode.get(), 5);
return ServerResponse.ok().contentType(APPLICATION_JSON)
.body(stores, Store.class)
.switchIfEmpty(notFound);
}
We can use switchIfEmpty method for this
Below example, I'm checking if the user exists with email if not then add it
userRepository.findByEmail(user.getEmail())
.switchIfEmpty(s -> {
user.setStatus("InActive");
String encodedPassword = DigestUtils.sha256Hex(user.getPassword());
user.setPassword(encodedPassword);
userRepository.save(user).subscribe();
s.onComplete();
}).then(Mono.just(user));
Use Mono with Optional:
return findExistingUserMono
.map(Optional::of)
.defaultIfEmpty(Optional.empty())
.flatMap(optionalUser -> {
if(optionalUser.isPresent()) {
return Mono.error('xxxx');
}
return this.userService.create(optionalUser.get());
});
This way it will always emit Optional value so that the stream will never break.

How to handle jpa entity

I have a table client and from retrieving results I use this way
public ClientParent getClient(Long clientId,Long parentId){
String queryString="SELECT cp FROM Client cp where cp.cid.id=:clientId " +
"and cp.pid.id=:parentId ";
Query query=entityManagerUtil.getQuery(queryString);
query.setParameter("clientId", clientId);
query.setParameter("parentId", parentId);
return (ClientParent)query.getSingleResult();
}
This is the DAO method.
Actually for getting client at 1st control goes to controller class then to service and then DAO class
Now lets say that the client table is empty so in this case return (ClientParent)query.getSingleResult(); will throw me error.
I can handle this in by wrting in try catch block in service class as well as in controller class.But wanted to know if I can do with out throwing any exception.I mean do I have change the query or what should I return so that it will never throw exception even if the table is empty
you can use the getResultList() method
public ClientParent getClient(Long clientId,Long parentId){
String queryString="SELECT cp FROM Client cp where cp.cid.id=:clientId " +
"and cp.pid.id=:parentId ";
Query query=entityManagerUtil.getQuery(queryString);
query.setParameter("clientId", clientId);
query.setParameter("parentId", parentId);
List<ClientParent> result = query.getResultList();
if (result != null && result.size() >0){
return result.get(0);
} else {
return null;
}
}
I suggest you to surround your code with try-catch block. So will sure that the data is correct.
try {
// ... your code goes here
// getSingleResult()
return XXX;
} catch(NonUniqueResultException e) {
// here you know there is some bad data
// so you can ignore it or do something
} catch(NoResultException e){
return null;
}

Resources