Spring with JPA: Queue concurrent access to JPA entity from different requests - java

I have a Spring Web Application with concurrent access to a certain resource. This resource holds a list of a certain numer of objects which might be fetched by a request. If the list is empty, no SomeClass Object should be returned any more
The resource looks like this:
public class Resource {
private List<SomeClass> someList;
public List<SomeClass> fetch() {
List<SomeClass> fetched = new ArrayList<SomeClass>();
int max = someList.size();
if(max<=0) {
return fetched;
}
int added = 0;
while(added<max) {
int randomIndex = Math.random(max-1);
SomeClass someClass = someList.get(randomIndex);
if(!fetched.contains(someClass)) {
fetched.add(someClass);
++added;
}
}
someList.remove(fetched);
return fetched;
}
}
This resource is loaded in the service layer, then accessed and saved back to the database:
#Service
public class ResourceService {
#Autowired
private ResourceRepository repo;
public List<SomeClass> fetch(long id) {
Resource resource = repo.findOne(id);
List<SomeClass> fetched = resource.fetch();
repo.save(resource);
return fetched;
}
}
I tried to use #Transactional on the ResourceService#fetch method to avoid the problem that two concurrent requests might fetch a SomeClass object from the list although the list was already emptied by the first request but I'm not sure if this is the right approach... Do I have to use #Synchronized on the Resource#fetch method or introduce an explicit Lock in the service layer?
I need to make sure that only one request is accessing the Resource (fetching a list of SomeClass objects) without throwing an exception. Instead, subsequent requests should be queued and try to access the resource after the current request has finished fetching the list of SomeClass objects.

My final solution was to introduce a Blocking Queue in the #Service and add all incoming requests to it. A seperate thread is then taking an element from the queue as soon as one was added and processing it.
I think this is the cleanest solution, as adding an ReentrantLock would block the request processing.

Related

How to handle state, concurrency and JMS inside Spring controllers?

I'm building a Spring Boot 2.1.6 REST API in which one of the controllers receives frequent requests. Each request contains data pertaining to some client event. The client is a video player which sends events each time a user viewed a video, viewed 25%, 50% etc. of the video, clicked etc. There may around 20 events per each client cycle.
I need to save each event in the database. As far as I understand inserting an event into the database inside the controller is not the best solution because inserting in the db is expensive, especially if this will happen on each call to the controller.
Therefore I thought to accumulate the events inside the controller inside some data structure, perhaps a queue. Then when the queue reached certain size I'd send all the events via JMS service to a consumer which will update the db with all the events (in a separate thread).
Controllers in Spring are singletons by default (and I want to keep them that way). Therefore I will need to synchronize the controller queue because I don't think that even if I'll declare the queue static it will prevent synch issues. In addition if I use synchronized inside the controller then I'm not taking an advantage of the multi-threading model that Tomcat uses.
What is the best practice to save controller state/perform synchronization is such case?
This is my controller:
#RestController
public class TrackingEventController {
#Autowired
private DBHandler db;
#Autowired
private JmsTemplate jmsTemplate;
private Queue<TrackingEvent> queue;
public TrackingEventController() {
this.queue = new ArrayBlockingQueue<>(30);
}
#CrossOrigin(origins = "*")
#RequestMapping(method=GET, path=trackingEventPath)
public ResponseEntity<Object> handleTrackingEvent(
#RequestParam(name = Routes.event) String event,
#RequestParam(name = Routes.pubId) String pubId,
#RequestParam(name = Routes.advId) String advId) {
TrackingEvent tr = new TrackingEvent(event, pubId, advId);
this.trySendEventsMessage(tr);
return new ResponseEntity<>(null, new HttpHeaders(), HttpStatus.OK);
}
private synchronized void trySendEventsMessage() {
this.queue.add(tr);
if (this.queue.size() >= eventsMapMaxMessageSize) {
jmsTemplate.convertAndSend(jmsTopicName, this.queue);
queue.clear();
}
}
}

How does Spring-Boot handle requests that are put through a singleton class?

I'm currently developing a class structure that uses singleton classes for each RequestMapping. I understand that Spring (by default) handles requests in a multi-threading capacity, but I just want to make sure what I'm laying-out now will eventually work in the way I'm intending.
In addition, if, for instance, I make a request and the singleton class creates an in-memory database. On a successive request from the same user/client, will the next request be able to use any of the information previously stored, or is the Spring container for requests destroyed (of sorts) when the response is send back, and re-created on a new request?
Finally, if this is bad practice in any way to create a singleton class for this purpose, I'm open to any suggestions. The original intent of creating the singleton class, was to simply make the ScheduleManager class responsible for creating the object in its entirety, that way if later, if I need to have certain steps taken beforehand to create a full ScheduleManager object (say for instance, establishing a connection to a database), then I can do that without having to create an extra parameter to the constructor and making the implementing class (RequestController) responsible for creating such a database connection.
// Partial of RequestController.java
#RestController
public class RequestController {
private static final String DATA_DIR = System.getProperty("user.dir") + "/data/";
private static final Logger LOGGER = Logger.getLogger(RequestController.class);
#PostMapping(value = "/generateSchedules")
public JsonObject scheduleRequest(#RequestBody JsonObject requestedClasses) {
JSONObject request = gsonToJson(requestedClasses);
JSONObject response = ScheduleManager.getInstance().generateSchedules(request);
return jsonToGson(response);
}
}
// Partial of singleton class ScheduleManager
public class ScheduleManager implements RequestManager {
private static ScheduleHandler handler;
private ScheduleManager() {}
private static class ScheduleManagerHolder {
private static final ScheduleManager INSTANCE = new ScheduleManager();
}
public static ScheduleManager getInstance() {
handler = ScheduleHandler.getInstance();
return ScheduleManagerHolder.INSTANCE;
}
public JSONObject generateSchedules(JSONObject request) {
handler.handlePost(request);
List<Schedule> schedules = GoldPan.getInstance().generate();
JSONArray schedulesAsJson = listToJsonArray(schedules);
JSONObject output = new JSONObject();
output.put("schedules", schedulesAsJson);
return output;
}
}
So, what I'm expecting, is that each unique client sending a request to the RequestController will have their own separate entity/container of sorts of the ScheduleManager singleton class, rather than sharing a single ScheduleManager amongst all requests (kinda like the label "singleton" implies in my mind).

Spring Service instance variable in a multi-threaded environment

I recently ran into a race condition issue because of declaring an instance variable inside a default scoped (Singleton scope) Service class. The purpose of the instance variable was to make my code more readable and avoid the constant passing of the same variable to different private methods within the Service class. The example goes:
#Service
public class SomeServiceImpl implements SomeService {
private final StatusRepository statusRepository;
private Predicate<Status> statusPredicate;
#Autowired
public SomeServiceImpl(StatusRepository statusRepository) {
this.statusRepository = statusRepository;
}
#Override
public List<Status> getAllowedStatuses(String userId) {
statuses = statusRepository.getAll();
initPredicate();
appendPredicateA();
appendPredicateB();
List<Status> results = statuses.stream()
.filter(statusPredicate)
.collect(Collectors.toList());
return results;
}
private void initPredicate() {
statusPredicate = p -> p.getDefault().equals("default");
}
private void appendPredicateA() {
statusPredicate.and(p -> p.getA().equals("A"));
}
private void appendPredicateB() {
statusPredicate.and(p -> p.getB().equals("B"));
}
}
This is a very simple example of the kind of things I want to achieve. This is clearly not thread-safe because now the service class is stateful. I could simply resolve this by turning the statusPredicate variable into a local variable and have the void methods return the predicate after it has been appended new conditions, but that would become cluttered like this:
#Override
public List<Status> getAllowedStatuses(String userId) {
statuses = statusRepository.getAll();
Predicate<Status> statusPredicate = p -> p.getDefault().equals("default");
statusPredicate = appendPredicateA(statusPredicate);
statusPredicate = appendPredicateB(statusPredicate);
List<Status> results = statuses.stream()
.filter(statusPredicate)
.collect(Collectors.toList());
return results;
}
It'd be constantly calling to modify the variable and return the variable.
I know a few solutions that can resolve this such as adding #RequestScope on the Service class to ensure each request from the HTTP will get a new instance of the
Service object, or use ThreadLocal on the Predicate variable. However, I'm not quite certain what is the best approach and whether declaring an instance variable in a Service class is even okay to begin with. If it is bad to make Service class stateful to begin with, how should I structure my code to make it cleaner and still keeping it stateless?
Please advise! Thanks in advance :D
Spring Service can be stateful and this is why scopes for.
Default scope of Spring Service is Singleton because this is the widest use case. But you shouldn't use mutable local variables.
Simply, try for a stateless design and use scopes to solve the issue only if the class instantiation is fast otherwise TreadLocal will perform better.

Spring 4 #Service with #RequestScope

In order to optimize sql request, I've made a service that aggregate other services consumptions to avoid unecessary calls.
(Some pages of my webapp are called millions times by day, so I want to reuse the results of database queries as many times as possible on each request)
The solution I create is this one :
My service has #RequestScope instead of default scope (Singleton)
In MyService
#Service
#RequestScope
public MyService {
private int param;
#Autowired
private OtherService otherService;
#Autowired
private OtherService2 otherService2;
private List<Elements> elements;
private List<OtherElements> otherElements;
public void init(int param) {
this.param = param;
}
public List<Elements> getElements() {
if(this.elements == null) {
//Init elements
this.elements = otherService.getElements(param);
}
return this.elements;
}
public List<OtherElements> getOtherElements() {
if(this.otherElements == null) {
//Init otherElements
this.otherElements = otherService2.getOtherElements(param);
}
return this.otherElements;
}
public String getMainTextPres() {
//Need to use lElements;
List<Elements> elts = this.getElements();
....
return myString;
}
public String getSecondTextPres() {
//Need to use lElements;
List<Elements> elts = this.getElements();
//Also Need to use lElements;
List<OtherElements> otherElts = this.getOtherElements();
....
return myString;
}
}
In my controller :
public class myController {
#Autowired MyService myService;
#RequestMapping...
public ModelAndView myFunction(int param) {
myService.init(param);
String mainTextPres = myService.getMainTextPres();
String secondTextPres = myService.getSecondTextPres();
}
#OtherRequestMapping...
public ModelAndView myFunction(int param) {
myService.init(param);
String secondTextPres = myService.getSecondTextPres();
}
}
Of course, I've simplified my example, because myService use lots of other elements, and i protect the initialization of his members attributes
This method has the advantage of doing lazy loading of the attributes only when I need them.
If somewhere in my project (in same or other controller) I only need the SecondTextPres, then calling "getSecondTextPres" will initialize both lists which is not the case in my example beacuse the first list has been initialized when "getMainTextPres" was called.
My question are :
What do you think of this way of doing things ?
May I have performance issues because I instantiate my service on each request ?
Thanks a lot !
Julien
I think that your idea is not going to fly. I you call the same or different controller this is will be different request - in that case new bean will be created (elements and other elements are empty again).
Have you been thinking about caching? Spring has nice support where you can define cache expiration, etc
It's not quite clear to me what exactly you want to optimise instantiating Service in request scope? If you are bothered about memory foot print, you could easily measure it by JMX or VisualVM.
On the other hand, you could make all Service calls pure, i.e. depending on function parameters and (ofc) database state only and instantiate the Service with default scope as Singleton.
This decision will save you reasonable amount of resources as you will not instantiate possible large object graph on each call and will not require GC to clean the thing after Request is done.
The rule of thumb is to think why exactly you need the specific Class instantiated on every call and if it doesn't keep any state specific to call, make it Singleton.
Speaking about lazy loading, it always helps to think about worst case repeated like 100 times. Will it really save you something comparing to be loaded once and for the whole Container lifetime.

Spring Singleton Thread Safety

If I have a Java class defined below that is injected in my web application via dependency injection:
public AccountDao
{
private NamedParameterJdbcTemplate njt;
private List<Account> accounts;
public AccountDao(Datasource ds)
{
this.njt = new NamedParameterJdbcTemplate(ds);
refreshAccounts();
}
/*called at creation, and then via API calls to inform service new users have
been added to the database by a separate program*/
public void refreshAccounts()
{
this.accounts = /*call to database to get list of accounts*/
}
//called by every request to web service
public boolean isActiveAccount(String accountId)
{
Account a = map.get(accountId);
return a == null ? false : a.isActive();
}
}
I am concerned about thread safety. Does the Spring framework not handle cases where one request is reading from the list and it is currently being updated by another? I have used read/write locks before in other applications, but I have never thought about a case such as above before.
I was planning on using the bean as a singleton so I could reduce database load.
By the way, this is a follow up of the below question:
Java Memory Storage to Reduce Database Load - Safe?
EDIT:
So would code like this solve this problem:
/*called at creation, and then via API calls to inform service new users have
been added to the database by a separate program*/
public void refreshAccounts()
{
//java.util.concurrent.locks.Lock
final Lock w = lock.writeLock();
w.lock();
try{
this.accounts = /*call to database to get list of accounts*/
}
finally{
w.unlock();
}
}
//called by every request to web service
public boolean isActiveAccount(String accountId)
{
final Lock r = lock.readLock();
r.lock();
try{
Account a = map.get(accountId);
}
finally{
r.unlock();
}
return a == null ? false : a.isActive();
}
Spring framework does not do anything under the hood concerning the multithreaded behavior of a singleton bean. It is the developer's responsibility to deal with concurrency issue and thread safety of the singleton bean.
I would suggest reading the below article: Spring Singleton, Request, Session Beans and Thread Safety
You could have asked for clarification on my initial answer. Spring does not synchronize access to a bean. If you have a bean in the default scope (singleton), there will only be a single object for that bean, and all concurrent requests will access that object, requiring that object to the thread safe.
Most spring beans have no mutable state, and as such are trivially thread safe. Your bean has mutable state, so you need to ensure no thread sees a list of accounts the other thread is currently assembling.
The easiest way to do that is to make the accounts field volatile. That assumes that you assign the new list to the field after having filled it (as you appear to be doing).
private volatile List<Accounts> accounts;
As a singleton and non-synchronized, Spring will allow any number of threads to concurrently invoke isActiveAccount and refreshAccounts. So, no this class is not going to be thread-safe and will not reduce the database load.
we have many such meta data and have some 11 nodes running. on each app node we have static maps for such data so its one instance only, init from db at start up once in off peak hour every day or when support person triggers it. have an interal simple http post based API to send updates from 1 node to others for some of the data which we need updates in real time.
public AccountDao
{
private static List<Account> accounts;
private static List<String> activeAccounts;
private NamedParameterJdbcTemplate njt;
static {
try{
refreshAccounts();
}catch(Exception e){
//log but do not throw. any uncaught exceptions in static means your class is un-usable
}
}
public AccountDao(Datasource ds)
{
this.njt = new NamedParameterJdbcTemplate(ds);
//refreshAccounts();
}
/*called at creation, and then via API calls to inform service new users have
been added to the database by a separate program*/
public void refreshAccounts()
{
this.accounts = /*call to database to get list of accounts*/
}
public void addAccount(Account acEditedOrAdded)
{
//add or reove from map onr row
//can be called from this node or other node
//meaning if you have 2 nodes, keep IP port of each or use a internal web service or the like to tell
//node B when a account id added or changed in node A ...
}
//called by every request to web service
public static boolean isActiveAccount(String accountId)
{
Account a = map.get(accountId);
return a == null ? false : a.isActive();
}
}

Categories

Resources