views:

397

answers:

6

I have a web application at work that is similar to a ticket working system. Some users enter new issues. Other workers choose and resolve issues. All of the data is maintained in MS SQL server 2005.

The users working to resolve issues go to a page where they can view open issues. Because up to twenty people can be looking at this page at the same time, one potential problem I had to address was what happens if someone picks an issue that someone else picked just after their page loaded.

To address this, I did two things. First, the gridview displaying the issues to select uses an AJAX timer to update every second. Once an issue has been selected, it disappears one second later at most. In case they select one within this second, they get a message asking them to choose another.

The problem is that the AJAX part of this is sending too many updates (this is what I am assuming) and it is affecting the performance of the page and database. In addition, the updates are not performing every second. I find the timer to be unreliable when working to trigger stored procedures.

There has to be a better way, but I can't seem to find one. Does anyone have experience with a situation like this or have suggestions to keep multiple users from selecting the same record to maintain? I really do not want to disable the AJAX part entirely because I feel the message alone would make the application frustrating to use.

Thanks,

+2  A: 

Have you tried increasing the time between refreshes. I would expect that once per 30 seconds would be sufficient. 40 requests/minute is a lot less load than 1200/minute. Your users may not even notice the difference.

If they do, how about providing a refresh button on the page so the users can manually refresh the list just prior to selecting an item to avoid the annoying message if they choose.

tvanfosson
I've come to think that AJAX used in this manner is not a good solution at all - be it every second or every 30 seconds.The refresh is a good idea. I have not thought of that. A small link above the gridview would work nicely. Still, it does lose some of its usability.
K Richard
I typically use the refresh to notify users when new information has been added when they haven't navigated for awhile. Although, my refresh rate is more like 5 minutes.
tvanfosson
Correct me if I've missed something, but this seems like advocating solving a concurrency issue with a `sleep()` call instead of using a lock. It won't solve the issue of concurrent edits, just mask it.
asveikau
@asveikau - I'm not suggesting that you handle database concurrency issues by making the time between updates less frequent. I'm suggesting that doing fewer checks can make the application and database more responsive when you do the check. You still have to handle concurrency -- i.e., not let two people choose the same issue, but that should be handled using a transaction and timestamps when selecting an issue. With less frequent updates it will, perhaps, be more likely that two people choose the same issue but at least the check and message will be snappier since there are fewer queries.
tvanfosson
+2  A: 

If possible limit the system so that they just get the next open issue off the work queue as opposed having them be able choose from all open issues.

If that isn't possible, I suppose you could check upon the choosing of an issue to see if it is still available. If it's not available, then make it disappear after the user clicks on it. This way you are only requesting when they actually click on something as opposed to constant polling of the data.

Daniel Auger
Unfortunately, due to the nature of work, limiting to the next in queue is not an option.I like the idea of having it disappear when the user clicks on it if it has been selected already, but I worry that it will just frustrate the users.
K Richard
I think possible user frustration is a very valid concern. You'd definitely want to prototype for user acceptance if you go this route. I think you are definitely in a tight spot here with concurrency vs usability.
Daniel Auger
Thank you for the feedback. You know, when I first started working with AJAX I thought, "Finally, this will let users all work in sync with data." I'm not exactly disappointed, but I definitely selectively forgot how performance would factor in.
K Richard
+5  A: 

Put a lock timestamp field on the row in the database. Write a stored proc that returns true or false if the expiration timsetamp is older than a specific time. Set your sessions on your web app to expire in the same time, a minute or two. When a user select a row they hit the stored proc which helps the app to decide if it should let the user to modify it.

Hope that makes sense....

craigmoliver
I haven't much experience with your method, but I think I understand it. Would the users then potentially try to modify records still that have already been selected? What I'd really like to do, if possible, is keep them from troubling through the selection of unavailable records.
K Richard
Well, how can you hide them if there has not been a postback to the server?
K Richard
ajax using some jquery baby
craigmoliver
That seems to put me right back where I started from
K Richard
If you check the timestamp and do the SQL update in two different transactions which are not atomic to each other, this is a race condition... Although my guess is this would probably work with something like `UPDATE ... WHERE Timestamp < CurrentTimeStamp ...`.
asveikau
+3  A: 

I did something similar where once a user opened a ticket (row) it assigned that ticket to that user and set a value on that record, like and FK to that particular user, so if anyone else tried to open that ticket (row) it would let them know it has already been assigned to someone else.

Slee
That's actually a piece of what we are doing now. We change the status code of the issue to indicate it is being maintained. We continuously update a timestamp that keeps the record in this status as long as it is being updated.This alone would still have users selecting records they can't work.
K Richard
+3  A: 
Jacob Proffitt
+1 for the randomisation, exactly what I was going to suggest!
Ed Daniel
Random order will not work. The tickets are prioritized by service level expiration.
K Richard
What's the basis of the service level expiration? Is that a time-based determination? Depending on your issue counts, you may be able to group them to the nearest minute (or fifteen minutes) and have it be "good enough". Anyway, I know that asking for an exception to the prioritization an be politically problematic, but if you *can*, it may prove worth it.
Jacob Proffitt
When issues are entered, several fields determine a priority level. These levels each have rules to calculate the service time - end of business, two business hours, 48 hours, etc. Basically, we apply this and store the datetime calculated with the record. Since the gridview updates each second to remove rows that were selected by users, I keep a field that is basically Now() - SeriveDeadline. The AJAX update gives it the appearance of a countdown. The team is measured by % in service, so having a close focus on that time remaining is important to them.
K Richard
Yeah, that's something you don't want to mess with.
Jacob Proffitt
+1  A: 

I'm missing to see the issue, specially after you mentioned you are already flagging tickets as in progress/being maintained and have a timestamp/version of the item.

Isn't the following enough:

  1. User browses the tickets and sees a list of available tickets i.e. this excludes ones that are in the db as in progress. If you want the users to also see tickets in progress, you indicate it clearly in the ticket status and disable the option to take it.
  2. User either flags a ticket as in progress explicitly or implicitly by opening the ticket (depends on the user experience / how its presented to the users).
  3. User explicitly moves the ticket to a different status i.e. completed, invalid, awaiting for feedback, etc.

When the items are retrieved at 1, you include a timestamp/version. When 2 happens, you use a optimistic concurrency approach to make sure that if 2 persons try to update the take the ticket at the same time only the first one will be successful.

What will happen is that for the second person, the update ... where ... timestamp = @timestamp will not find any records to update and you will report back that the ticket was already taken.

If you want, you can build on top of the above to update the UI as tickets are grabbed. This could be by just doing a full refresh of the current page of tickets after x time (maybe alerting/prompting the user), or even by retrieving a list of tickets changed for the page of tickets being showed with ajax. You still have the earlier steps in place, as this modification its just a convenience for the users.

eglasius
All of what you described is happening now. The problem is that updating the gridview with AJAX is inefficient. If I set the update timer to a longer interval, the users begin selecting the same record. Optimistic concurrency keeps the data in check, but it wastes user's time. You have to imagine a list of 300 items sorted by priority and up to 20 users selecting the top one.
K Richard