Проблемы параллелизма с несколькими независимыми транзакциями базы данных? - PullRequest
5 голосов
/ 14 февраля 2012

Как бы вы решили проблему параллелизма с помощью следующего кода? В этом примере мы хотели бы узнать , почему пользователь не прошел аутентификацию. Проблема в том, что этот код выполняет два отдельных вызова базы данных, но мы бы хотели, чтобы весь метод происходил внутри концептуальной транзакции. В частности, мы заинтересованы в изоляции . Мы не хотим, чтобы одновременные записи во время выполнения этого метода влияли на наши чтения до того, как мы определили причину сбоя аутентификации.

На ум приходит несколько решений: Блокировка потоков , TransactionScope и Оптимистическая блокировка . Мне действительно нравится идея «Оптимистичной блокировки», так как я думаю, что конфликты, скорее всего, будут редкими, но в .NET нет ничего, чтобы сделать это, верно?

Кроме того - действительно ли это чем-то действительно нужно заниматься в этом случае? Когда такие вопросы параллелизма, как это важно рассмотреть, а когда нет? Что необходимо учитывать при реализации решения? Спектакль? Продолжительность блокировки? Насколько вероятны конфликты?

Редактировать: После рассмотрения ответа Аристоса, я думаю, что на самом деле мне нужен какой-то уровень изоляции snapshot для метода Authenticate.

public MembershipStatus Authenticate(string username, string password)
    {
        MembershipUser user = Membership.GetUser(username);
        if (user == null)
        {
            // user did not exist as of Membership.GetUser
            return MembershipStatus.InvalidUsername;
        }

        if (user.IsLockedOut)
        {
            // user was locked out as of Membership.GetUser
            return MembershipStatus.AccountLockedOut;
        }

        if (Membership.ValidateUser(username, password))
        {
            // user was valid as of Membership.ValidateUser
            return MembershipStatus.Valid;
        }

        // user was not valid as of Membership.ValidateUser BUT we don't really
        // know why because we don't have ISOLATION.  The user's status may have changed
        // between the call to Membership.GetUser and Membership.ValidateUser.
        return MembershipStatus.InvalidPassword;
    }

Ответы [ 2 ]

1 голос
/ 14 февраля 2012

Основываясь на моем чтении здесь и здесь , кажется, что System.Transactions.TransactionScope охватывает весь ваш метод должен автоматически подключить вызовы вашей базы данных в общей транзакции, что обеспечивает безопасность транзакций во всей области транзакций.

Вы хотели бы сделать что-то вроде этого:

public MembershipStatus Authenticate(string username, string password)
{
    using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.Snapshot }))
    {
    MembershipUser user = Membership.GetUser(username);
        if (user == null)
        {
            // user did not exist as of Membership.GetUser
            return MembershipStatus.InvalidUsername;
        }

        if (user.IsLockedOut)
        {
            // user was locked out as of Membership.GetUser
            return MembershipStatus.AccountLockedOut;
        }

        if (Membership.ValidateUser(username, password))
        {
            // user was valid as of Membership.ValidateUser
            return MembershipStatus.Valid;
        }

        // user was not valid as of Membership.ValidateUser BUT we don't really
        // know why because we don't have ISOLATION.  The user's status may have changed
        // between the call to Membership.GetUser and Membership.ValidateUser.
        return MembershipStatus.InvalidPassword;
    }
}
1 голос
/ 14 февраля 2012

Я буду использовать mutex, используя имя в качестве параметра блокировки, поэтому только один и тот же пользователь может заблокировать на некоторое время. Для меня это более безопасно для одного компьютера, потому что с мьютексом я могу захватывать все возможные потоки из разных пулов или веб-вызовов.

public MembershipStatus AuthenticateLock(string username, string password)
{
    if(string.IsNullOrEmpty(username))
      return MembershipStatus.InvalidUsername;

    // TODO: Here you must check and clear for non valid characters on mutex name
    using (var mutex = new Mutex (false, username))
    {
         // possible lock and wait, more than 16 seconds and the user can go...
         mutex.WaitOne (TimeSpan.FromSeconds(16), false);

         // here I call your function anyway ! and what ever done...
         //  at least I get a result
         return Authenticate(username, password)
    }
}

Дополнительные комментарии: и Membership.ValidateUser, и Membership.GetUser делают вызов в базу данных.

Но если вы используете стандартную сессию asp.net для страниц, которые выполняют эти вызовы и влияют на эти параметры, , тогда все страницы готовы к блокировке друг друга, так что я думаю, что есть нет никакого шанса нуждаться в этом вызове мьютекса. Поскольку блокировки сеанса достаточно для синхронизации и этой части. Напоминаю, что сеанс блокирует страницу от начала до конца для всех пользователей.

О блокировке сеанса: Полная замена сессии ASP.Net

jQuery Ajax-вызовы веб-службы выглядят синхронными

...