I've been researching how to fix this issue for sometime but can't seem to find a proper solution.
Here's the issue:
I have a Java EE application where many users can login, they are presented with an item list, and they can select and edit any one of those.
All users see the same item list.
As mentioned, they can edit an item but I'd like to restrict the editing function to one user. That is, many users can edit different items simultaneously but only one user can edit a particular item.
When one user is editing an item, an message should appear to any other user trying to edit that item.
I have implemented this by setting a flag on the item, inUse, to true and then check for that. When the user is done editing the item, either by clicking save or cancel, the flag is set to false.
Problem with this approach is to account for cases when the user leaves his browser open or the browser is closed.
I tried setting a session timeout but can't seem to make that work because when the session times out, I don't have access to that item. I only have access to the httprequest session id.
Perhaps this is the wrong approach since it seems it's an issues that many applications would have and a less hackie solution should exist.
I looked into using threads and synchronized methods but don't know how that would work because once the user enters into the edit item screen, the method exits and releases the lock.
I found this solution Only one user allowed to edit content at a time but not sure if that's the way to go in Java.
Is there a more elegant/java solution? If so can you point me in the right direction please? How would you implement this?
Thanks!
The solution:
Although originally I thought optimistic locking was the way to go, I quickly realized that it wouldn't really work for my environment. I decided to go with a combination of pessimistic locking (http://www.agiledata.org/essays/concurrencyControl.html#PessimisticLocking) and timeouts.
When an item is accessed, I set an inUse field to true and the object's last accessed field to the current time.
Everytime when somebody tries to edit the object, I check the inUse field and the lastAccessed field + 5 mins. So basically, I give 5 mins to edit the user.
Do it like they do in a database, where a timestamp is used. The timestamp is kept with the record and when a person submits his edit, the edit does not go through unless the timestamp is the same (meaning 'no edits have occurred since I read this record'). Then when the edit does go through, a new timestamp is created.
First of all, in your persistence layer, you really should be doing optimistic locking, using a version/timestamp field.
At a UI level, to handle your use case I would do resource leasing:
Add two fields to your table:
LAST_LEASE_TIME: Time of the last lease
LAST_LEASE_USER: User that leased the record for the last time.
When a user tries to edit your record, first check that the record is not leased, or that the lease has expired (that is, the lease is not older that the specified lease time) or that the user is the one that was granted the lease.
From your web browser, periodically renew the lease, for example with an AJAX call.
When the user ends editing the record, explicitly expire the lease.
By doing leasing, you solve the "closed browser" problem: after the lease period expires without any lease renovation, the algorithm automatically "releases" the resource.
Sounds like you could use : Session Beans Quote:
In general, you should use a session bean if the following circumstances hold:
At any given time, only one client has access to the bean instance.
The state of the bean is not persistent, existing only for a short period of time (perhaps a few hours).
Martin Fowler describes 4 patterns for such a problem:
Online Optimistic Locking
Online Pessimistic Locking
Offline Optimistic Locking
Offline Pessimistic Locking
You should decide which one to use according to your problem.
JPA, JDO and Hibernate provide 1 and 2 out of the box.
Hibernate can handle 3 too, (I'm not sure about JPA and JDO).
None handle 4 out of the box and you shall implement it yourself.
Related
I have a question about concurrent access in java.
The project context is as follows: JSF 2.1; Richfaces 4, hibernate 3.6, spring 3.2, java 7.
I have a table that displays hundreds of thousands of folders (each folder is a java object), each table line is a dynamic link to access the consultation or modification of the object(folder).
My question is: how can I prevent more than one user from viewing or editing a folder?
In other words, how do you detect access to a folder (object java) by more than one user at the same time?
I know that with the word Synchronized on methods makes it possible to prevent concurrent access, but I want to detect concurrent access to warn the other user or the other users that the folder (object java) is already open and we have to wait for it closed.
Thank you
Synchronization and all the other locking / blocking methods is only preventing concurrent access from within the code.
It does not prevent 2 users from editing the same "thing" at the same time. This is something you have to put into your code on your own.
There are two most common ways to achieve this:
Optimistic Locking:
You assume, that even if multiple users view the same item, there are not multiple changes at the same time. Hence, you only check during SAVING if there is a concurrent modification. To Achieve that:
The Object in Question needs a Timestamp, when it was last modified. (By anyone)
Once a user opens an item to view, you store that timestamp in the session context.
If the user clicks "save", you compare the session-context-timestamp with the timestamp of the actual object. If they are the same, changes can be saved. If the Object has been modified in between, you need to display an error, that it has been modified, and changes can't be saved without loading the new base-values prior.
Pessimistic Locking:
You assume, that whenever a user views an Object, that there will be a change. To Achieve that:
The Object in Question needs a Int-Flag, that it is currently beeing edited (By anyone - Flag = UserID)
Once a second user tries to load that object, you deny cause someone is editing currently.
Tricky part here is to clean up "locks", if the editing user just quits the page without "aborting" through application methods. To handle this, you need to define a Lock-Timestamp, define some timeout value (for example 30min) and do two more checks:
Editing user exceeded the timeout value upon save? he can't save anymore.
(As an extra you can allow later saving, if the object has not been locked by someone else in between, use the lock-user-id to figure out)
Object is locked, but lock is older than 30 minutes? Second User can open again, claiming the lock-user-id for himself.
I have a situation like in a Java application like , "if an there is an entry in a table and simultaneously there is a delete request also how will we handle such a scenario".
Could anybody suggest me on how to deal with issues like these which could work on small as well as large applications?
I think the question is how the UI/UX should be handled when such scenario occurs. Besides the concurrency issue described in question, there can be other scenarios like user 1 opens edit person page, in the mean time, user2 deletes that record from another login. What should happen when user1 tries to save the record?
You should probably return an error message to the user stating the details of the error(record deleted, updated by someone else etc.).
Your question is very wide and such is the aswer.
I will narrow the problem a bit, by assuming you are using spring(boot). If so then It is very easy to answer.
Use the #Transactional annotation above the methods that contain the logic to either save or delete. And include required libraries ofc.
With the methods annontated in such a way, the spring(boot) application will guarantee that both operations will occur in the order that is required to maintain a consistent database.
If an error occurs, you can handle this in higher levels of your application or just show an error to the user.
I have designed a Web based project.
I am using Mysql database. I will perform all persistence logic in java using hibernate. All the client side actions will be done in javascript.
Here my problem is,
If two users are trying to update same record simultaneously at different places.
Initially User-1 updates by giving full information related to a single object and called save information method.
At the other end User-2 updates same record by giving partial information and called save information method.
If User-1 information is saved first User-2 information will overwrite first given information. Hence some information might loss which user-1 given but he doesn't know some thing is loosed.
Please give some suggestions to overcome this problem.
I recommend you use the Optimistic Lock. Basically this technique is to have a field in the table to tell Hibernate which your version and thus whether an object with smaller version try to overwrite the data in a larger version hibernate will throw an exception. This versioning field is usually a numeric field that hibernate increases with every update or date field. The flow is something like:
1 - The record is inserted into the base. At this point the "version" field is set to zero.
2 - The X user query the record with version 0.
3 - The Y user query the record with version 0.
4 - The Y user updates the registry information. At that moment the hibernate automatically increments the version of record for 1.
5 - The X user updates the information on the version 0 and try to save. At that moment the hibernate finds that the record is already in version 1 that is greater than the version that the user X is using, in that it throws an exception stating the problem and not allowing overwriting the most current information.
To implement this strategy simply create a numeric field in your table and then apply #Version:
#Version
#Column(name = "version")
private Integer version;
What you need to consider, is a locking strategy for your data. Using Hibernate, by default you have no locking (a.k.a. Ostrich locking or "last save wins"). Roughly, the other two options are optimistic locking and pessimistic locking.
Optimistic locking means that you do not prevent users editing data concurrently, but you will inform a user if his edit failed because the data was saved from elsewhere after it was loaded from the DB.
Pessimistic locking means that you prevent multiple users for editing the data concurrently. This is a bit more complicated form of locking and is usually neither practical nor required.
More info on implementing a locking strategy can be found from Hibernate documentation. Which strategy you should choose depends a lot on your application and whether many users are expected to frequently edit the same information.
Before user 2 updates the DB, you can check if the information in the DB (e.g. the row) is the same as it was when user 2 reached the update/edit page. e.g. you could do a SELECT on the row when the user reaches the page and again after the user has made the changes (i.e. before the row is updated) and compare these before you update the DB.
If the row is the same, there was no changes. If the row is different, someone else had edited it.
The scenario I have is this.
User does a search
Handler finds results, stores in session
User see results, decides to click one of them to view
After viewing, user clicks to "Back to Search"
Handler detects its a back to search, skips search and instead retrieves from session
User sees the same results as expected
At #5, if there was a new item created and fits the user's search criteria, thus it should be part of the results. But since in #5 I'm just retrieving from session it will not detect it.
My question is, should I be doing an extra step of checking? If so, how to check effectively without doing an actual retrieve (which would defeat the purpose)? Maybe do select count(*) .... and compare that with count of resultset in session?
Caching something search results in a session is something I strongly advise against. Web apps should strive to have the smallest session state possible. Putting in blanket logic to cache search results (presumably several kb at least) against user session state is really asking for memory problems down the road.
Instead, you should have a singleton search service which manages its own cache. Although this appears similar in strategy to caching inside the session, it has several advantages:
you can re-use common search results among users; depending on the types of searches this could be significant
you can manage cache size in the service layer; something like ehcache is easy to implement and gives you lots of configurability (and protection against out of memory issues)
you can manage cache validity in the service layer; i.e. if the "update item" service has had its save() method triggered, it can tell the search service to invalidate either its entire cache or just the cached results that correspond with the newly updated/created item.
The third point above addresses your main question.
It depends on your business needs. If it's imperative that the user have the latest up to date results then you'll have to repull them.
A count wouldn't be 100% because there could be corresponding deletions.
You might be able to compare timestamps or something but I suspect all the complexity involved would just introduce further issues.
Keep it simple and rerun your search.
In order to see if there are new items, you likely will have to rerun your search - even just to get a count.
You are effectively caching the search results. The normal answer is therefore either to expire the results after a set amount of time (eg. the results are only valid for 1 minute) or have a system that when the data is changed, the cache is invalidated, causing the search to have to run again.
Are there likely to be any new results by the time the user gets back there? You could just put a 'refresh' button on the search results pages to cause the search to be run again.
What kind of refresh rate are you expecting in the DB items? Would the search results change drastically even for short intervals, because I am not aware of such a scenario but you might have a different case.
Assuming that you have a scenario where your DB is populated by a separate thread or threads and you have another independent thread to search for results, keep track of the timestamp of the latest item inserted into the DB in your cache.
Now, when user wants to see search results again compare the timestamps i.e. compare your cache timestamp with that of the last item inserted into the DB. If there is no match then re-query else show from your cache.
If your scenario confirms to my assumption that the DB is not getting updated too frequently (w.r.t. to a specific search term or criteria) then this could save you from querying the DB too often.
Actually, the problem is like this:-
i am having a table, say Payments. I have provided few GUI for searching, viewing and editing the record. Now if the user is trying to edit a record perform some particular action, which takes say 1 minute to process. Then another user should not be able to perform that particular operation on the same record.
Traditional approach of doing this is, have a column in the table, say _isLocked_. and whenever user is performing that action it should change the value of said column to, say, true. Once the process is completed it should reset the value of the column. Also, just before someone tries to perform the action, it should check the value of the column and notifies the user if the record is locked.
What other approached are there to do the same?
SELECT FOR UPDATE is the way. We get this kinda behaviour by using this.
As Vinegar said, SELECT FOR UPDATE - accept his answer, not mine :)
Make sure to know when the user really wants to edit a row, and when he/she is done. Depending on your application, you could open a separate window where user does the modification and then confirms/cancels, so you can do your COMMIT/ROLLBACK.
And make sure that another user who attempts to change the same row will not face a frozen application and have to wait for the lock to be released.