There’s a project I’m working on that is looking to produce a web application that will manage a task list between multiple users. This is a master task list whose task items get distributed by an authorized user. Each user has his/her own account to login and view the tasks they are assigned; it is possible for multiple users to have a single task in-common.
I am trying to leave details of the project out of this as I’m grappling more with the overall concept of how to handle the following situations, but if it helps, I’m using Java, EclipseLink, and GWT with RequestFactory implemented. The database is PostgreSQL.
So the conceptual problems I am attempting to reconcile are the following:
-
If a single task that is common to multiple users changes in any way e.g. task completed, deleted, etc., the task list of all the users who have this task will be updated. What design patterns are there that assist in implementing this function?
- Some patterns I have looked at are Observer and Mediator – are there others that should be considered over these?
-
Say there are two users changing the same task at the same time.
-
First, should I allow that situation to happen or should I put a lock on it until one or the other person is done making changes?
-
Second, if I don’t put a lock on it, how do I reconcile whose changes to accept? This involves the situation in 1 because user 1 could submit the data and before user 2 receives the updated data, he/she may have gone ahead and submitted his/her’s changes.
-
I am really looking for any guiding points, advice, or tips you can provide on how to properly sync data between multiple instances of this web app. I would very much appreciate it!
I think Whiteboard will be your pattern of choice for #1, you should post changes to tasks (or other shared data) in a common place, so all interested parties can see them and DTRT.
For #2, you need to look at optimistic locking. Basically, you need to timestamp all your editable records with the last update time. When you try to save the record, you first verify that the record in the database has the same last-updated timestamp as your record. If not, then someone has updated the record and you now have to either get the updated record and inform the user that they need to enter their changes again, or you can try to merge the user’s changes into the updated record (which usually turns out to be either simple or impossible).
1
I came up with a design for a desktop application (that has yet to be fully tested) with similar requirements that might be helpful.
My solution was to employ the MVC pattern (with a single Model but multiple Controllers and Views) where each Controller made changes to the Model using transactions (using STM) and when a transaction committed, the Model broadcasted an update notification to the View(s).
Each client also all kept track of anything being updated locally, but when those local updates were finished (i.e. sent to be committed) it reverted to using the underlying Model’s info.
I also had an undo stack with all the changes the users made so that things could be reverted.
This might not be the best model for a web app though since the Model had to broadcast changes to the Views, which might not be the easiest w/ a web client.
for 1. you should see if the publish/subscribe pattern is better suited.
for 2. it depends on your situation:
- how frequent will this situation be?
- how bad is a situation in which one of your users will be unable to update a task because it is locked or someone else has changed it in the meantime?
personally I prefer an approach (used e.g. in pivotaltracker) in which there are:- no locks,
- you see all changes in real-time, and
- the UI invites doing frequent minor updates instead of bigger ones on multiple attributes.
- you keep a history of all changes that have been made. if the history is visible to the users, eventually arising conflicts or overwrites can be resolved with comments, annotations or messages.
2
- Lock record.
This is not preferred as it will decrease concurrency and the record may be locked for more time than needed. - Last to submit overwrites other concurrent changes.
Users do not want this. Some software like JIRA does this. https://community.atlassian.com/t5/Jira-questions/Edit-a-JIRA-Issue-at-the-same-time-by-two-Users-result-in-last/qaq-p/389243 - Concurrent edits.
The application should submit only the fields of the record that are changed. That way you prevent conflicts.
Most wiki software can merge changes to the same text field if there are no conflicts: https://stackoverflow.com/questions/3411888/how-does-a-wiki-handle-multiple-simultaneous-edits
See for example MediaWiki as it allows concurrent edits and has a good UI in case of conflicts:
https://www.mediawiki.org/wiki/Extension:TwoColConflict
My recommendation is to never lock and report a conflict if it happens.
Please, take a look at:
https://github.com/spring-projects/spring-petclinic/issues/433
You can see a video and sample code.
Will that meet your requirements?