Reputation: 413
I am facing a problem with locking an asp.net page. We have a user profile page in which we need to lock that page for the user who opened it first. The details are as follows, There are many user profile records in database and we are passing the record number to querystring to open a particular page. The user clicks a linkbutton in a grid and opens the record in readonly mode. There is an edit button which enables all the controls and makes it available to user once he clicks it. The task is to lock the record to the user who clicks the edit button first.
Apart from this there are many scenarios like the user may navigate from the page or he may close the page in between. In these cases the record should be available to the other users. Please give me some possible approaches or examples of how to solve the scenario.
Thanks in advance
Upvotes: 3
Views: 2979
Reputation: 877
@Brian Driscoll says they set a bit on the database record. Then they tell their users they MUST click save or cancel. Really? There are so many things wrong with this.
Rule 1 of interaction design "users" will not do what you expect - ever.
Rule 1 of locking stuff - if the actor responsible for releasing the lock can fail it will. Even if you managed, through shock therapy, to train your user base their computer might fail, the database might fail, the connection might fail, the network might fail, the user might die in the middle of the edit.
Rule 2 of locking stuff - when rule 1 of locking stuff is in effect you need an out - specifically a TIMEout or some other way of releasing orphaned locks.
There's a lot written about locking on WikiPedia that's worth reading. File locking and thread related locking (mutexes, semaphores, etc) are really similar to this problem and understanding how those work is a good starting place. A user really is just another external parallel processing unit right?
@Michael Yoon gives a very interesting answer in my opinion. We're in the process of implementing exactly this type of page based locking and this is what we have the system doing now, but we'll change it to be more like Yoon's idea to constantly ping the server to extend the lock.
Specifically we would replace item 6 with Yoon's idea to not bother the user with notification, but to use "micro-lock durations" of perhaps 30 seconds and keep the lock active by extending the lock duration from the client more often than 30 seconds.
Upvotes: 1
Reputation: 1606
For a good user experience, I'd setup a heartbeat on the page after a user clicks "edit" using some javascript. Every 5 seconds or whatever is more reasonable I'd ping the server. So if the user disconnects for whatever reason you can release the lock pretty quickly by spinning off a thread that checks to see the last time the user pinged the page. You'd have to store the ping time somewhere, like the server cache or maybe the session, but I'd prefer a distributed cache like memcache for load balancing (although it might not be important in your environment).
The lock itself should be fairly straightforward to implement, but I'd favor either distributed cache solution like memcache or a timestamp column in the database. I'd still include a failsafe expiration in case it doesn't expire through the heartbeat.
Upvotes: 2
Reputation: 841
It's not possible because it is not possible to determine whether the user's browser has closed. You can do silly things like infer that the browser has closed by doing something like having the browser poll your website every minute (like a ping), but I wouldn't go that route. You could also use a slideExpiration on the session, but to me the session should be used for session and not for 'locking' pages.
Maybe keep cached value of the page name in the Cache that keeps record of the user that has locked the file with an expiry of say 10 minutes. If another user requests the file, your code first checks to see if the page name is in the cache. If the same user requests the same file (i.e. does a postback or a refresh) then update the cache (i.e. reset the timer) and they'll have an extra 10 minutes. Let's say the user is writing a long winded letter and they haven't posted back or refreshed the page and 10 minutes is up, you could remedy this by having a client side timer that triggers after 9 minutes and alerts the user "your time with the page will end in 1 minute, click okay to extend your time for another 10 minutes".
I'm not saying this is a great solution, but it's an idea. What I do know is that because you cannot tell if the browser is closed you'll need to think outside the box.
Upvotes: 1
Reputation: 37480
If you try to lock a record, you are 100% going to get stale locks, which means you'll have to have a process to remove these stale locks.
If you are just trying to prevent concurrent modification, you can do this with a timestamp or a version field. When editing a record, put the version/timestamp into a hidden field. Then, when trying to apply updates, the first thing you do is re-read the data corresponding to the ID, then check the version that comes back from the database against the version in the hidden field. If they match, then it's safe to go ahead and apply the changes, and if it's not then you can apply some logic to determine how to proceed.
Upvotes: 2
Reputation: 23796
I think this is a really bad idea for all the reasons you mentioned, but if I had to do this, what I would do is use the ASP.NET Cache.
So, something like this:
Cache.Add(someUniqueKeyForAUserProfile, theUserThatLockedTheRecord, null,
DateTime.Now.AddSeconds(120), Cache.NoSlidingExpiration, CacheItemPriority.Normal,
UnlockRecord)
private static void UnlockRecord(string key, object value, CacheItemRemovedReason reason) {
//This particular record went longer than 2 minutes without
//the user doing anything, do any additional cleanup here you like
}
Then in the page you can do something like:
if (Cache[someUniqueKeyForAUserProfile] != theUserThatLockedTheRecord){
//Tell the user they can't access the page
}
The nice thing here is that you automatically "unlock" the record after two minutes by using the built in ASP.NET cache. So you get all that for free.
Upvotes: 2
Reputation: 21112
That is a hard thing to do on the web. I recommend using a TimeStamp in the db when User1 accesses the data. Then give User1 a way to unlock on the page manually and also use a timeout value utilizing the TimeStamp to automatically un-lock the records for User2, if User1 abandons the page(navigates away, closes browser, looses internet connection...etc).
Upvotes: 0
Reputation: 19635
I've worked on a similar system and in our case we set a bit on the record in the database to indicate that it was being edited. However, unlike your case we were able to set the expectation with the end users of our system that they MUST click either 'Save' or 'Cancel' to flip the bit back over and allow editing of the record by other users. I'm honestly not totally sure how to handle the case in which a user abandons the page, though what comes to mind is to have a scheduled task that runs a sproc that will free the record if it has been locked for a certain period of time (this means you'd need a datetime field as well to indicate when the record was locked).
Upvotes: 1