I have a GWT (+GAE) webapp that allows users to edit Customer entities. When a user starts editing, the lockedByUser attribute is set on the Customer entity. When the user finishes editing the Customer, the lockedByUser attribute is cleared.
No Customer entity can be modified by 2 users at the same time. If a user tries to open the Customer screen which is already opened by a different user, he get’s a “Customer XYZ is being modified by user ABC”.
The question is what is the most pragmatic and robust way to handle the case where the user forcefully closes the browser and hence the lockedByUser attribute is not cleared.
My first thought is a timer on the user side that would update the lockRefreshedTime each 30 seconds or so. A different user trying to modify the Customer would then look at the lockRefreshedTime and if the if the refresh happened more then say 35 seconds ago, it would acquire the lock by setting the lockedByUser and updating the lockRefreshedTime.
Thanks,
Matyas
FWIW, your lock with expiry approach is the one used by WebDAV (and implemented in tools like Microsoft Word, for instance).
To cope for network latency, you should renew your lock at least half-way through the lock lifetime (e.g. the lock expires after 2 minutes, and you renew it every minute).
Have a look there for much more details on how clients and servers should behave: https://www.rfc-editor.org/rfc/rfc4918#section-6 (note that, for example, they always assume failure is possible: "a client MUST NOT assume that just because the timeout has not expired, the lock still exists"; see https://www.rfc-editor.org/rfc/rfc4918#section-6.6 )
Another approach is to have an explicit lock/unlock flow, rather than an implicit one.
Alternatively, you could allow several users to update the customer at the same time, using a "one field at a time" approach: send an RPC to update a specific field on each
ValueChangeEventon that field. Handling conflicts (another user has updated the field) is then made a bit easier, or could be simply ignored: if user A changed the customers address from "foo" to "bar", it really means to set "bar" in the field, not to change _from "foo" to "bar", so if the actual value on the server has already be updated by user B from "foo" to "baz", that wouldn’t be a problem, user A would have probably still set the value to "bar", changing it from "foo" or from "baz" doesn’t really matter.Using a per-field approach, "implicit locks" (the time it takes to edit and send the changes to the server) are much shorter, because they’re reduced to a single field.
The "challenge" then is to update the form in near real-time when another user saved a change to the edited customer; or you could choose to not do that (not try to do it in near real-time).