2012-02-13 13 views
5

Jak rozwiązać zagadnienie współbieżności za pomocą poniższego kodu? W tym przykładzie chcielibyśmy wiedzieć, , dlaczego użytkownik nie uwierzytelnił się. Problem polega na tym, że ten kod wykonuje dwa oddzielne wywołania do bazy danych, ale chcielibyśmy, aby cała metoda miała miejsce w ramach transakcji pojęciowej. W szczególności interesuje nas isolation. Nie chcemy, aby zapisy współbieżne podczas wykonywania tej metody wpłynęły na nasze odczyty, zanim nie określimy przyczyny niepowodzenia uwierzytelnienia.Kwestie współbieżności z wieloma niezależnymi transakcjami bazy danych?

Kilka rozwiązań przychodzi na myśl: Thread Locking, TransactionScope i Optimistic Locking. Bardzo podoba mi się pomysł Optymistycznego blokowania, ponieważ wydaje mi się, że konflikty są rzadkie, ale nie ma nic wbudowanego w .NET, aby to zrobić, prawda?

Również - czy jest to coś, na co NAPRAWDĘ się martwisz w tym przypadku? Kiedy kwestie współbieżności, takie jak to ważne, należy rozważyć, a kiedy nie są? Co należy wziąć pod uwagę przy wdrażaniu rozwiązania? Wydajność? Czas trwania blokady? Jak prawdopodobne są konflikty?

Edit: Po przeanalizowaniu odpowiedzi Aristos', myślę, że to, co jestem naprawdę po to jakiś «snapshot» poziom izolacji dla metody uwierzytelnić.

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; 
    } 
+1

nie jestem pewien, że naprawdę trzeba się martwić o izolacji tutaj. Jakie są szanse, że użytkownik zostanie usunięty lub zostanie zablokowany między wywołaniem "GetUser" i "ValidateUser"? Co więcej, nie powinno się ujawniać użytkownikom tych informacji. Jeśli wyświetlasz różne komunikaty o "złym haśle" kontra "użytkownik nie istnieje" kontra "konto zablokowane", osoba próbująca włamać się do witryny może wykorzystać te informacje, aby uzyskać listę prawidłowych nazw użytkowników.Nie chcesz tego, ponieważ te nazwy użytkowników mogą być pomocne w wykorzystaniu innej luki w twojej infrastrukturze. –

+0

+1 za odpowiedź. Prawdopodobnie użyłem tutaj słabego przykładu (chociaż planowałem rozróżniać "nieprawidłowe dane uwierzytelniające" i "konto zablokowane" i być może muszę to jeszcze raz przemyśleć). Po prostu nie podoba mi się fakt, że dane mogą się zmienić pode mną! Współbieżność jest trudna! Co, jeśli POTRZEBUJĘ, aby dane były spójne, co byś zasugerował? – Brandon

Odpowiedz

1

podstawie lektury here i here, wydaje się, że System.Transactions.TransactionScope owijania całą swoją metodę powinny automatycznie zaciągnąć połączeń bazy danych we wspólnej operacji, w wyniku w bezpieczeństwie transakcyjnym w całym zakresie transakcji.

że chcesz zrobić coś takiego:

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; 
    } 
} 
+0

+1 za poświęcenie czasu na odpowiedź. Dlaczego potrzebowałbym wdrożyć mojego własnego dostawcę członkostwa? Czy nie mógłbym po prostu użyć klasy TransactionScope ", w której transakcje są automatycznie zarządzane przez infrastrukturę"? – Brandon

+0

@Brandon przepraszam, że tak długo trzeba było odpowiedzieć- chciałem zrobić kilka badań, zanim odpowiem na to. Zaktualizowałem swoją odpowiedź, aby odzwierciedlić użycie TransactionScope. –

+0

dzięki za odpowiedź. Zaznaczam to jako akceptowaną odpowiedź, głównie za pierwszy komentarz do mojego pytania (prawdopodobnie nie powinienem tego robić na początku, a nawet gdybym chciał, to naprawdę nie jest problem). – Brandon

1

użyję mutex używając nazwy jako blokujący parametre, więc tylko ten sam użytkownik może można zablokować na chwilę. Dla mnie jest to bezpieczniejsze dla jednego komputera, ponieważ dzięki muteksowi mogę przechwytywać wszystkie możliwe wątki z różnych pul lub połączeń internetowych.

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) 
    } 
} 

Więcej komentarzy: Zarówno Membership.ValidateUser i Membership.GetUser Zadzwonić do bazy danych.

Ale jeśli używasz standardowego asp.net sesji dla stron, które to połączenia i wpływa to parametry, następnie strony gotowe blokady jeden drugiego tak myślę, że nie ma szans na to potrzebne wywołanie mutex. Ponieważ blokada sesji wystarcza do synchronizacji i tej części. Przypominam, że sesja blokuje stronę od początku do końca dla wszystkich użytkowników.

O blokady sesji: Replacing ASP.Net's session entirely

jQuery Ajax calls to web service seem to be synchronous

+0

+1 za poświęcenie czasu na udzielenie odpowiedzi na to pytanie i na informacje o sesji. Sugerujesz blokowanie nici. Blokowanie wątków zapewnia rodzaj "miękkiej" izolacji, ale nie będzie pomocne, jeśli masz wiele procesów (nie wątków) próbujących uzyskać dostęp do udostępnionego zasobu. Wydaje mi się, że to, o czym naprawdę myślę, jest pewnego rodzaju izolacją "migawki" dla metody Authenticate. – Brandon

+1

@Brandon Blokada mutex, której używam, jest przydatna w wielu procesach! Ale mówię, że nie potrzebujesz tego nawet, jeśli używasz sesji we wszystkich twoich wywołaniach do tej funkcji. Blokada sesji wszystko gotowe strony, więc nie potrzebujesz drugiej blokady. – Aristos

+0

Interesujące! Nie wiedziałem, że "muteksy systemu nazwanego są widoczne w całym systemie operacyjnym i mogą być używane do synchronizowania działań procesów." Domyślam się, że granica to PC/OS, np. nie można zsynchronizować procesów z muteksem w wielu komputerach/systemach operacyjnych? – Brandon