[ Miroslav Ćurčić @ 20.05.2012. 12:54 ] @
Šta se u praksi pokazalo kao najbolje rešenje za sprečavanje 'race condition' situacije kod editovanja formulara ?

Primer:

Administrator 1 je otvorio formu stranicu kojom posmatra sadržaj nekog zapisa iz baze, i počinje da ga edituje, jer mu se recimo ne sviđa naslov artikla.

Administrator 2 zatim otvara na svom računaru istu tu stranicu i gleda sadržaj istog tog zapisa iz baze, jer recimo želi da cenzuriše psovku iz teksta artikla.

Admin 1 je izmenio naslov i pritiska dugme 'Save' i snima nov sadržaj formulara u bazu.

Admin 2 završava cenzurisanje teksta i pritiska 'Save'. Time u stvari on pregazi naslov teme originalnim sadržajem i tako slučajno anulira efekat prvog admina.

Kako je to rešeno u nekim većim sistemima ?
Zaključavanje editovanja zapisa ne dolazi u obzir.
[ PHPovac @ 20.05.2012. 13:14 ] @
moje mišljenje je da se upiše u bazu da admin 1 edituje to i to i onda na save adminu 2 kaže da je admin 1 menjao ili menja stranicu i pita ga da li sigurno želi da pregazi promene. tako radi wordpress
[ nesh @ 16.07.2012. 18:08 ] @
Citat:
Miroslav Ćurčić:
Šta se u praksi pokazalo kao najbolje rešenje za sprečavanje 'race condition' situacije kod editovanja formulara ?
Kako je to rešeno u nekim većim sistemima ?
Zaključavanje editovanja zapisa ne dolazi u obzir.


http://en.wikipedia.org/wiki/Database_transaction
[ cabrilo @ 18.07.2012. 15:10 ] @
Imaš nekoliko varijanti:

1) Prva je ovo što je PHPovac predložio, tipa da napišeš poruku "Korisnik X edituje ovaj formular", pa pustiš čoveka da se dovikuje sa X-om o tome šta da se radi.
2) Ono što ja prefereriram da uradim je sledeće:

Svaka tabela ima "modified" polje (ON UPDATE CURRENT_TIMESTAMP varijanta). Kada čovek otvori stranicu, tj. kada imaš GET poziv (ako je tako rešeno), sačuvaš vreme tog događaja. Kada klika na Save, proveriš "modified" polje. Ako je $modified > $otvaranje_formulara, prikažeš stranicu koja opisuje konflikt. Npr. prikažeš paralelno dva formulara, jedan sa izmenama koje je čovek hteo da izmeni, drugi sa aktuelnim podacima (koji su oni koje je drugi admin u međuvremenu sačuvao).

Onda čoveku daš opcije koje bi ti dao bilo koji versioning system:

- mogućnost da koristi postojeće izmene, tj. da odbaci svoje
- mogućnost da koristi svoje izmene i mogućnost da ih menja, da bi prilagodio onome što vidi kao aktuelne informacije.

Ova varijanta je dobra jer ne moraš da diraš bazu. Sve izmene čuvaš u sesiji (tj. samo ih prebacuješ iz poziva u poziv) i porediš sa stvarima u bazi. Možeš i u nedogled da vrtiš, tj. ako 5 ljudi radi na istom formularu, ti uvek za svakog proveravaš kada je poslednji put otvorio formular i kako se poredi sa "modified" poljem.

3) Konačno, možeš da napraviš ceo versioning sistem. Umesto jednog reda za jedan unos u bazi, imaš N redova, svaki sa timestampom i najnoviji timestamp je aktuelna verzija. Onda korisnici mogu da roll-backuju izmene, da ih uporede itd. Ako očekuješ puno takvih konflikta, onda je to prava stvar, ali i najkomplikovanija.
[ VladaSu @ 18.07.2012. 18:44 ] @
@nesh
Kako? Moze li simple primer koda?
[ cabrilo @ 19.07.2012. 12:05 ] @
Citat:
VladaSu:
@nesh
Kako? Moze li simple primer koda?


Prilično sam siguran da se to ne može rešiti transakcijama. One imaju drugu svrhu. Npr:

Hoćeš da updateuješ tabelu A i onda u tabelu B da uneseš, npr. informaciju da si updateovao nešto. Ako uradiš čisto (presudocod):

Code:
1) mysql_query(UPDATE a SET x = 1 WHERE id = N)
2) mysql_query(INSERT INTO b (poruka) VALUE (N je updateovan))


Ovo pod 2) će se izvršiti čak i ako ovo pod 1) nije uspelo. Ako je ovo pod 1) uspelo, onda će postojati trenutak u vremenu kada će tabela A biti ažurirana, ali tabela B neće.
Onda to rešiš sa (opet pseudokod)

Code:
mysql_query(START TRANSACTION)
mysql_query(UPDATE a SET x = 1 WHERE id = N)
mysql_query(INSERT INTO b (poruka) VALUE (N je updateovan))
if (mysql_affected_rows()) { // Vrati 1, tj. TRUE ako je prethodan query nešto uspeo
  mysql_query(COMMIT)
} else {
  mysql_query(ROLLBACK)
}


Kada uradiš COMMIT, OBA querya će biti sačuvani u bazi. Kada uradiš ROLLBACK, kao da se ništa nije desilo...

Dakle, neprimenjivo za zadati problem :)
[ VladaSu @ 20.07.2012. 07:57 ] @
@cabrilo
Znam sta su transakcije i znam da ne moze ovaj problem da se resi transakcijam, zato i pitam za primer koda a ne samo povrsno procitana teorija :)
[ bantu @ 21.07.2012. 17:27 ] @
Imas u tabeli koju editujes polje version koje svaki put kada update-ujes inkrementiras za jedan.

npr. kada popunjavaš formu uradi sledeći upit.

Code:

SELECT username, ime, prezime, version FROM korisnici


Dobiješ neki odgovor od baze
username: pero
ime: petar
prezime: peric
version: 21

Kad završiš editovanje i klikneš na Save uradiš sledeće:
Code:

UPDATE korisnici SET
username = '.......',
ime = '.......',
prezime  = '.......',
verison = verison +1
WHERE username = 'pero' AND version = 21;


Nakon ovoga provjeriš koliko je kolona updateovano, ukoliko je to 0 onda znači da je neko u međuvremenu to ažurirao i da je verzija uvećana, da tvoj WHERE uslov ne pije vode i obavjesti korisnika o tome. A evo kako to možeš da provjeriš:
http://php.net/manual/en/mysqli.affected-rows.php

Inače ova metoda se zove optimistic locking.