2

I am using a simple form to allow people to sign up for an event. Their details are saved to a Sharepoint list. I have a quota of people who can sign up for an event (say 100 people).

How can I prevent the 100th and the 101st person from signing up concurrently, causing the quota check to allow the 101st person to sign up (because the 100th person isn't in the list yet)?

Ryan Berger
  • 9,644
  • 6
  • 44
  • 56
TanyaB
  • 21
  • 1
  • 3
  • What do you mean by *because the 100th person isn't in the list yet* and *concurrency of adding items*? Person 100 starts the process at 11:54:00 and submits the form at 11:54:49; person 101 at 11:54:30 and submits the form at 11:54:48; who should be signed up? – Marek Grzenkowicz Nov 16 '11 at 10:56
  • person 101 should be able to sign up. person 100 should get a notification. – TanyaB Nov 16 '11 at 11:13
  • 1
    Are you actually experiencing this problem? Don't you try to prematurely solve a problem that doesn't exist? What the maximum number of users that try to sign up for a event at the same time? – Marek Grzenkowicz Nov 16 '11 at 19:13
  • I posted a [general question about this problem](http://stackoverflow.com/q/8189439/95). – Marek Grzenkowicz Nov 18 '11 at 21:36

3 Answers3

2

Place the ItemAdding code inside a lock statement to make sure that only one thread at a time can enter the critical section of code:

private Object _lock = new Object();

public override void ItemAdding(SPItemEventProperties properties)
{
    lock(_lock)
    {
        // check number of the list items and cancel the event if necessary
    }
}

I came up with this idea of a solution for a farm with multiple WFEs - a shared resource (a row in a table in pseudo-code above) gets locked during the time the item is added to the list:

private Object _lock = new Object();

public override void ItemAdding(SPItemEventProperties properties)
{
    try
    {
        // 1. begin a SQL Server transaction
        // 2. UPDATE dbo.SEMAPHORE
        //    SET STATUS = 'Busy'
        //    WHERE PROCESS = 'EventSignup'

        lock(_lock)
        {
            // 3. check number of the list items and cancel the event if necessary
        }
    }
    finally
    {
        // 4. UPDATE dbo.SEMAPHORE
        //    SET STATUS = ''
        //    WHERE PROCESS = 'EventSignup'
        // 5. commit a SQL Server transaction
    }
}

I left the lock statement because I'm not sure what will happen if the same front-end server tries to add the item #100 and #101 - will the transaction lock the row or will it not because the same connection to SQL Server will be used?

Marek Grzenkowicz
  • 17,024
  • 9
  • 81
  • 111
  • 1
    This won't necessarily work on a farm with multiple WFE's (you're talking about a fairly extraordinary set of circumstances to hit this though, hope they are not running ticketmasters or some ultra high volume site!) – Ryan Nov 16 '11 at 13:17
  • @Ryan I'm not sure why the OP is so afraid of the *concurrency of adding items*. What are the users doing? Sitting next to each other and counting down? *3... 2... 1... click!* Even if they do, I still think this issue is improbable. – Marek Grzenkowicz Nov 16 '11 at 13:52
  • Sure its a unlikely (hence the +1) but thought I should mention it anyways. – Ryan Nov 16 '11 at 14:33
  • @Ryan I have just updated my answer but **do not treat the 2nd code snippet as a complete solution**. It's only an idea that I'd like to discuss. Does it have a chance to work? What's the name of the general problem we're trying to solve here? Is it a [distributed semaphore](http://en.wikipedia.org/wiki/Paxos_%28computer_science%29)? – Marek Grzenkowicz Nov 16 '11 at 19:10
  • The lock doesn't get you anywhere really (apart from stopping competing transactions on the same server which is an optimization) so probably best to rely on the db tran and semaphore. I personally would put the whole lot in an SP but its still the same basic idea that you have. You know I think this would probably make a great SO question on its own (without SharePoint tag as at this stage its actually nothing to do with SP). Do you want to ask? (if so please put link here, would love to know how it worked out and might miss otherwise!) – Ryan Nov 18 '11 at 08:28
  • 1
    @Ryan [What to use instead of the "lock" statement when the code is running on multiple machines?](http://stackoverflow.com/q/8189439/95) – Marek Grzenkowicz Nov 18 '11 at 21:34
1

So then you can use event receivers item adding method. at item adding, your item is not created, you can calculate the current count of signed up people. if it is bigger then 100 you can cancel item adding.

but sure, more than one item adding method can be fired, to prevent that you can calculate the current count of people and increase the count +1, and keep that value somewhere else (on a field on event item perhaps) and all item adding methods can check that value before adding the item.

item added method is too late for these operations.

this would be the solution i would use.

megusta
  • 134
  • 10
  • The problem is when item adding is fired simultaneously when two different people are trying to sign up at the same time. For both of them, the people count is 99, and both of them will be able to sign up. – TanyaB Nov 16 '11 at 10:58
  • 1
    that's why i say keep the count value somewhere else, you will match the counts of the item and the value that you are holding somewhere else. it can not be that simultaneous as miliseconds and with this method even a few miliseconds can save you. – megusta Nov 16 '11 at 11:13
0

I guess if you are updating a column, lets say - "SignUp Count", then one of the users will get the Save Conflict issue. Whoever updated the value for the first time wins and the second one will fail.

Regards, Nitin Rastogi

Nitin Rastogi
  • 1,446
  • 16
  • 30