I’ve been developing a PHP/MySQL web application that involves multiple users opening and editing records in the database. There is a very real possibility that two or more people will open the same record, make changes, and attempt to save these changes.
The most common concurrent execution problem that I expect to see is the lost update. This would occur when two users open the same record, make changes, and then save the record.
User A opens record “A”.
User B opens record “A”.
User A saves changes to record “A”.
User B saves changes to record “A”.
In this example, User A’s changes are lost - overwritten by User B’s changes.
Initially, I wanted to lock the record using a sort of check-in/check-out system. User A would check-out record “A”, and then have exclusive write access to that record until it was checked back in. The problem with this method is that User A may decide to not make any changes and not save the record, which would leave the record in a checked-out state until further administrative action was taken to unlock it.
I tried to come up with some ingenious way around this, which usually boiled down to somehow automatically unlocking the record after a period of time. But this is not a satisfactory solution. For one thing, a user may have a legitimate reason to keep the record checked-out for longer periods.
So what I eventually came up with is a method of checking whether a record has been changed since it was accessed by the user. My particular way of doing this involves comparing timestamps, but other techniques exist.
Here’s how I’m implementing it:
User A creates record “A” and saves it at 9:00 AM. A “last-saved timestamp” of 9:00 AM is generated and saved to the record.
User A opens record “A” at 10:00 AM. An “opened timestamp” of 10:00 AM is generated and written to a hidden (or readonly) input field on the html page.
User B opens record “A” at 10:00 AM. An “opened timestamp” of 10:00 AM is generated and written to a hidden (or readonly) input field on the html page.
At 10:30 AM, User A attempts to save the record. The “last-saved timestamp” is retrieved from the record. The “opened timestamp” of 10:00 AM is compared to the “last-saved timestamp” of 9:00 AM. Because the record has not been changed since it was opened, the record is saved. A new “last-saved timestamp” of 10:30 AM is generated and saved to the record.
At 11:00 AM, User B attempts to save the record. The “last-saved timestamp” is retrieved from the record. The “opened timestamp” of 10:00 AM is compared to the “last-saved timestamp” of 10:30 AM (User A’s timestamp). Because the record has been changed since it was opened by User B, User B is not allowed to save the record.
User B will have to re-open record “A”, consider the effect that User A’s changes may have, and then make any desired changes.
Unless I’m missing something, this assures that the data from the earlier save will not be overwritten by the later save. To keep things consistent, I’m using PHP to generate all of my timestamps from the server clock, as JavaScript time is based on the user’s system time and is therefore wholly unreliable.
The only drawback that I see is extra work for User B, who has to now review the record as saved by User A before deciding what changes to make.
The strange thing is that I haven’t seen this offered as a solution on any of the pages I found while Googling for solutions to the access control, lost update and other concurrency-related data loss problems.
