I'm new to Spring MVC and I didn't find anything about this in doc.
Let's say I have a controller /accounts which accepts POST requests to create an account.
Two requests comes (almost) in the same time with the same account id.
ASFAIK dispatcherServlet
manages the requests.
Is the 2nd request put in queue until the first one has been completed? Or there would be two threads working with two requests simultaneously?
UPDATE:
Check the official Spring tutorial: Building a REST service.
There is controller with method add
:
@RequestMapping(method = RequestMethod.POST)
ResponseEntity<?> add(@PathVariable String userId, @RequestBody Bookmark input) {
this.validateUser(userId);
return this.accountRepository
.findByUsername(userId)
.map(account -> {
Bookmark result = bookmarkRepository.save(new Bookmark(account,
input.uri, input.description));
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setLocation(ServletUriComponentsBuilder
.fromCurrentRequest().path("/{id}")
.buildAndExpand(result.getId()).toUri());
return new ResponseEntity<>(null, httpHeaders, HttpStatus.CREATED);
}).get();
}
Controllers are just beans, by default they are singleton. When two simultaneous requests are received the same instance of the controller is used by two threads. Let's imagine the 1st thread already saved new bookmark and executing
httpHeaders.setLocation(ServletUriComponentsBuilder
.fromCurrentRequest().path("/{id}")
.buildAndExpand(result.getId()).toUri());
Meanwhile the 2nd thread just executed
Bookmark result = bookmarkRepository.save(new Bookmark(account,
input.uri, input.description));
In this case the first thread will return result.getId()).toUri()
which is created by the 2nd thread.
Is it correct workflow and is this controller thread-safe?
Most servlets start a separate thread for each incoming request and Spring isnt an exception to that. You need to make sure that the shared beans are thread safe. Otherwise Spring takes care of the rest.
With all frameworks of that type, it is safe to assume that the controller methods will be processed simultaneously (i.e. in multiple concurrent threads). Furthermore, it is a serious performance hit to do it in any other way (yes, there are frameworks designed very differently from the ground-up, but we're not talking about those now).
Spring itself has no way of knowing that the account id part of the URL is somehow special to synchronize on it. If it would synchronize on URL level, it would bring the performance down to a crawl. You could always implement this logic yourself by, for example, synchronizing on a session-scoped object that this controller method operates on (or make the entire controller session-scoped as Lev's answer suggests). This would then serialize the calls for that user only (that HTTP session). You could have a globally shared lock if you'd want to synchronize between all users, but that's a really bad idea for reasons mentioned above.
Instead, design your application in a way that all controllers are completely stateless and make methods either idempotent if possible or have a correct precondition checking logic. If the method is writing to a DB, you could implement an optimistic-locking strategy. Hibernate already has that feature and some DBs/drivers have it as well, so look into what's applicable to your case.
UPDATE
In the provided example, both threads are saving an instance (I'm assuming save means insert/update as needed) and the returned object is the result of the current save operation, thus no strange inter-thread behavior. This is assuming the save is transactional with a reasonable transaction isolation level (every other assumption would be highly unusual). As it is, the second thread would simply overwrite the values written by the first, but that is probably what you'd expect. That said, you're only reading the id in the following line, and that presumably never changes, so the URL building seems unaffected by concurrency anyway.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With