Transactions. Avoid collisions when pasting.

I am using EF6 in my asp.net application and I have a problem, it is a little annoying and I cannot find a suitable solution for it.

My code looks like this:

using (var scope = TransactionScopeUtil.RepeatableReadMaxTimeoutRequired())
{
    bool hasConflict = await BookingService.HasConflictAsync(newBooking);
    if (!hasConflict)
    {
        await BookingRepository.InsertAsync(newBooking);
        return Json(AjaxPayload.Success());
    }
    else
    {
        return Json(AjaxPayload.Error());
    }
}

    // The transaction scope builder:
    public static TransactionScope ReadCommittedMaxTimeoutRequired()
    {
        return new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
        {

            IsolationLevel = IsolationLevel.ReadCommitted,
            Timeout = TransactionManager.MaximumTimeout
        }, TransactionScopeAsyncFlowOption.Enabled);
    }

      

The problem is that if two customers click the same booking time, a conflict must be logged. And one of the calls should return a message that the time slot has already been reserved. But this is not the case if they hit the server exactly to the right (with the same miles). Both reservations are saved without problems.

I can fix this by doing Serializable hardcore lock operation, but I'm sure there is a better way and am I too blind to see this?

What are the best practices in such situations?

+3


source to share


1 answer


if two customers click the same booking time, the conflict must be logged

If I understand correctly, you don't want to prohibit two orders at the same time. (You told Stefan that the "superuser" can force him.) Do you just want to log a conflict?

It's easy to do, but you have to use a database. At the very least, there must be some arbiter of truth, some place where there is only once and one final meaning of the state of things. This is usually a database. The logic looks like this:



insert into T values (X, time, priority = 1) where X not in T
if rows_affected = 1
    hurrah
else 
    while rows_affected < 1
       priority = max(priority) + 1
       insert into T values (X, time, priority) where (X, priority) not in T
   register conflict, you are $priority in line

      

Convert that to SQL or whatever you are using, go to {X, time, priority} as parameters and you're done.

By the way, in case it helps, this approach has a name: optimistic concurrency. With any luck, this term may appear in the documentation for your environment.

+1


source







All Articles