Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> Integralność danych przy jednoczesnym dostępie
Kikert
post 18.07.2012, 10:41:04
Post #1





Grupa: Zarejestrowani
Postów: 45
Pomógł: 0
Dołączył: 15.09.2008

Ostrzeżenie: (0%)
-----


Cześć,
stworzyłem stronę z konkursami online, jest oparta o chat, w którym użytkownicy wpisują odpowiedzi. Jeżeli trafią jedną z trzech opcji, użytkownik wygrywa. W takiej sytuacji do tabeli z wiadomościami była dodawania nowa, a później wykonywał się UPDATE w tabeli z konkursami, żeby ustawić zwycięzcę, jeżeli odpowiedź jest prawidłowa. Niestety, ostatnio są problemy - skrypt dwa razy dodał prawidłową odpowiedź do chatu, ale nie ustawił zwycięzcy i wygrała osoba, która podała odpowiedź jako trzecia. Niestety kto inny zajmuje się administracją - nie mogę założyć dodatkowych logów itp.

Zastanawiam się, jak zabezpieczyć skrypt przed taką sytuacją. Korzystam z mysqli, obydwie tabele na silniku InnoDB. Uwarunkowałem dodanie wiadomości do chatu od tego, czy wykonano UPDATE, ale obawiam się, że to za mało. Poniżej fragment pliku odbierającego ajaxowe zapytania:
  1. $update = $db->query(
  2. "UPDATE {$DBP_}contests SET `winner`='{$_SESSION['id']}' ".
  3. "WHERE `id`='{$contest['id']}' AND winner IS NULL AND '{$message_lowercase}' IN (answer, answer2, answer3)"
  4. );
  5.  
  6. if ($update) {
  7.  
  8. if ($db->affected_rows === 1)
  9. $json['won'] = array(
  10. 'answers' => array($message),
  11. 'winner_login' => $_SESSION['username'],
  12. 'winner_gravatar' => $_SESSION['gravatar']
  13. );
  14.  
  15. // ADD MESSAGE
  16. $db->query("INSERT INTO {$DBP_}chat SET `user_id`='{$_SESSION['id']}', `date`=NOW(), `text`='{$message}'");
  17.  
  18. }

Czy ktoś mógłby podać jakąś wskazówkę co jeszcze można zrobić?
Go to the top of the page
+Quote Post
Crozin
post 18.07.2012, 11:18:26
Post #2





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


1. Wszelkie dane modyfikujące stan bazy danych powinny być objęte jawną transakcją.
2. W tej chwili IF sprawdzający czy zmodyfikowano dane odnosi się wyłącznie do $json['won'] = ... - zapytanie INSERT wykona się właściwie zawsze.
Go to the top of the page
+Quote Post
Kikert
post 18.07.2012, 11:34:03
Post #3





Grupa: Zarejestrowani
Postów: 45
Pomógł: 0
Dołączył: 15.09.2008

Ostrzeżenie: (0%)
-----


Tak, wiadomość ma się wysyłać za każdym razem, bo to chat wink.gif Jednak jeżeli ktoś trafi z odpowiedzią, to przy okazji baza danych zapisuje informację, że ten user jest zwycięzcą.

Wcześniej wyglądało to w taki sposób (wyciąłem treści zapytań, są takie same):
  1. // INSERT INTO chat...
  2. // UPDATE contests...
  3. if ($update && $db->affected_rows === 1)
  4. $json['won'] = array(...);

Problem był, taki, że jeżeli wiadomość była odpowiedzią, to dodawała się do tabeli z wiadomościamu z chatu, ale nie aktualizowała kolumny contests.winner. Efekt był taki, że podano poprawną odpowiedź, która nie wygrywała konkursu.

Nie znam się za bardzo na transakcjach, ale z tego, co mi wiadomo, transakcja korzysta z tabeli w takim stanie, w jakim była w momencie rozpoczęcia transakcji. Czyli w sytuacji kiedy w minimalnych odstępach czasu przychodzą dwa wpisy z wygrywającą odpowiedzią, to wygra ten późniejszy, bo w momencie rozpoczęcia transakcji konkurs nie był jeszcze wygrany.

Ten post edytował Kikert 18.07.2012, 11:34:40
Go to the top of the page
+Quote Post
Crozin
post 18.07.2012, 12:46:50
Post #4





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


  1. LOCK TABLE tbl_contests READ;
  2. START TRANSACTION;
  3. INSERT INTO tbl_chat ...;
  4. UPDATE tbl_contents ...;
  5.  
  6. -- PHP if affectedRows
  7.  
  8. COMMIT;
  9. UNLOCK TABLES;
Go to the top of the page
+Quote Post
Kikert
post 18.07.2012, 12:52:23
Post #5





Grupa: Zarejestrowani
Postów: 45
Pomógł: 0
Dołączył: 15.09.2008

Ostrzeżenie: (0%)
-----


Wielkie dzięki, zaraz zobaczę, jak to śmiga smile.gif

Jeszcze drobne pytanie - co stanie się w sytuacji, kiedy ktoś inny też będzie wykonywał kod w tym samym czasie? Czy jego żądanie zostanie po prostu zignorowane, czy wykonane z opóźnieniem?

Ten post edytował Kikert 18.07.2012, 12:52:39
Go to the top of the page
+Quote Post
Crozin
post 18.07.2012, 23:12:58
Post #6





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


W przypadku gdy na tabelę jest założona blokada inne zapytania będą musiał poczekać ze swoim wykonaniem do czasu zdjęcia tej blokady, czyli wykonają się z opóźnieniem.

Swoją drogą, jeżeli w mechanizmie sprawdzania czy dany wpis jest zwycięski korzystasz z zapytania UPDATE oraz affectedRows możesz nawet zrezygnować z blokady odczytu tabeli.
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 3.08.2025 - 21:01