Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> [inny][laravel 5.1] Dublowanie rekordów przy szybkim odświeżaniu strony
Looozak
post 16.11.2015, 21:50:50
Post #1





Grupa: Zarejestrowani
Postów: 38
Pomógł: 0
Dołączył: 2.04.2010
Skąd: Częstochowa

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


Witam,

wydaje mi się, że moje pytanie jest banalnie proste, ale mój mózg już chyba dzisiaj przestał pracować, nawet nie wiem jak szukać rozwiązania. Praca inżynierska goni...

Mianowicie:
Sprawdzam czy istnieje rekord w bazie, jeżeli nie istnieje to tworzę nowy. Podczas przytrzymania F5 rekordów dodaje się kilka. Klucz unique w bazie nie wchodzi w grę.

  1. if (!Auth::user()->jakis_item()) {
  2. JakisItem::firstOrCreate([
  3. 'user_id' => Auth::user()->id
  4. ]);
  5. }


Dodam tylko, że User i JakisItem są połączone relacją One to Many, a metoda jakis_item() klasy User zwraca pojedynczy obiekt Eloquenta.

Chodzi mi o to żeby każdy użytkownik miał ZAWSZE minimum jeden JakisItem, który może, np zmienić właściciela i wtedy chcę utworzyć nowy. Kod powyżej wykonuje się po każdym odświeżeniu strony i sprawdza czy ten item nie zniknął (jest w konstruktorze kontrolera).

Podczas tworzenia użytkownika w metodzie postRegister klasy kontrolera AuthController tworzę rekord tak jak powyżej. Jeśli jest na to jakiś lepszy sposób bardzo proszę o nakierowanie na niego.

Pozdrawiam!
Go to the top of the page
+Quote Post
phpion
post 16.11.2015, 22:11:15
Post #2





Grupa: Moderatorzy
Postów: 6 070
Pomógł: 860
Dołączył: 10.12.2003
Skąd: Dąbrowa Górnicza




Ja w tym przypadku zrezygnowałbym z zapisywania tego w PHP. Narzut będzie stosunkowo duży (odpalaniem za każdym razem), a w przypadku ingerencji z poziomu bazy danych będziesz musiał ręcznie dodawać te itemy. Dlatego proponowałbym napisanie triggera. Nie znam logiki Twojej aplikacji, ale po tym co piszesz to byłby on odpalany w co najmniej 2 przypadkach:
1. Tworzenie użytkownika.
2. Przekazywanie itemu innemu użytkownikowi.
Według mnie to co opisujesz to integralność danych w bazie: każdy użytkownik musi mieć co najmniej 1 dany item. Niech więc baza sama o to dba, a nie aplikacja.
Go to the top of the page
+Quote Post
Looozak
post 28.11.2015, 14:23:45
Post #3





Grupa: Zarejestrowani
Postów: 38
Pomógł: 0
Dołączył: 2.04.2010
Skąd: Częstochowa

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


Piszę tu ponieważ nie chciałem zakładać nowego podobnego tematu.

Drugi dzień próbuję zrobić kolejkę budowy w mojej grze. Próbowałem już "na żywo" obliczać czas startu i końca budowy, ale skończyło się milionem warunków w pętli, które w działały dopóki ktoś nie zaczął kombinować, np anulować kilka zadań budowy na raz, szybko dodawać kolejne itp. W tej chwili czas startu i zakończenia przechowuje w bazie. Teoretycznie łatwiej jest tym zarządzać, ale mam problem z szybkim klikaniem w przycisk wybuduj. Poniżej zamieściłem przykładowy, najprostszy kod, który mi nie działa.

Zmienna $temp przechowuje ostatni dodany wpis do kolejki. Jeśli taki nie istnieje ustawia czas startu nowego na teraz, jeśli istnieje to czas startu nowego równa się czas zakończenia poprzedniego. Problem w tym, że przy szybkim kliknięciu warunek jest sprawdzany jeszcze przed dodaniem nowego wpisu z poprzedniego kliknięcia. Efekt jest taki, że kilka elementów z listy ma identyczny czas startu. Próbowałem to zabezpieczyć regenerując csrf_token przy każdym przesłaniu lub tworząc swoją zmienną sesyjną, lecz przy bardzo szybkim kliknięciu i tak dodadzą się 2-3 takie same elementy. Oczywiście nie chcę tego zabezpieczać tylko po stronie przeglądarki.

  1. public function postBuild(Requests\MyRequest $request)
  2. {
  3. $temp = $this->item->build_queue()->last()->first();
  4.  
  5. if ($temp) {
  6. $start_at = $temp->end_at->timestamp;
  7. } else {
  8. $start_at = Carbon::now()->timestamp;
  9. }
  10.  
  11. $slug = $request->slug;
  12.  
  13. $duration = 50;
  14.  
  15. BuildQueue::create([
  16. 'item_id' => $this->item->id,
  17. 'building_slug' => $slug,
  18. 'start_at' => $start_at,
  19. 'end_at' => $start_at + $duration,
  20. ]);
  21.  
  22. return redirect()->back();
  23. }


Już w kilku przypadkach miałem problem z tym, że przy szybkim odświeżaniu strony lub szybkim klikaniu w przycisk coś co powinno wydarzyć się raz, dzieje się kilka razy. Może Laravel ma jakis sposób na rozwiązanie tego problemu. Bardzo proszę o pomoc.

//edit
Sprawdziłem też w innym projekcie i problem jest ten sam. Jeszcze prościej:

  1. $a = Comment::where('post_id', $request->post_id)->where('author_name', $request->author_name)->first();
  2.  
  3. if(!$a) {
  4. Comment::create($request->all());
  5. }


Gdy dodaje komentarz z tym samym autorem powoli wszystko działa, gdy kliknę szybko parę razy to dodaje się wiele wpisów. Oczywiście takie coś można rozwiązać poprzez unique w bazie danych, ale mnie interesuje pierwszy przykład gdzie to jest niemożliwe.

Ten post edytował Looozak 28.11.2015, 14:59:06
Go to the top of the page
+Quote Post
Crozin
post 28.11.2015, 15:56:43
Post #4





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

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


Witamy w świecie współbieżności. ;-)

Przyczynę już właściwie sam zdiagnozowałeś. Dwa równoległe żądania sprawdzają w tym samym czasie czy coś istnieje, a dopiero później dodają jakieś dane.
Rozwiązanie? Obie instrukcje objęte transakcją (http://www.postgresql.org/docs/9.4/static/sql-start-transaction.html) z odpowiednio wysokim poziomem izolacji (http://www.postgresql.org/docs/9.1/static/transaction-iso.html).
Go to the top of the page
+Quote Post
Pyton_000
post 28.11.2015, 16:30:31
Post #5





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

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


@Crozin nie zakładałbym że kolega ma PSQL smile.gif
Go to the top of the page
+Quote Post
Crozin
post 28.11.2015, 16:48:26
Post #6





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

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


Bez znaczenia czy korzysta z Postgresa, MySQL-a, którego zapewne masz na myśli, Oracle'a, MSSQL-a czy jeszcze czegoś innego. Wszędzie, gdzie mamy do czynienia z ACID-owym modelem transakcji będzie coś w tym stylu.
Go to the top of the page
+Quote Post
Looozak
post 28.11.2015, 18:10:30
Post #7





Grupa: Zarejestrowani
Postów: 38
Pomógł: 0
Dołączył: 2.04.2010
Skąd: Częstochowa

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


Ustawiłem poziom izolacji na SERIALIZABLE i zaczęło działać jak trzeba. Kiedyś korzystałem z transakcji w MSSQL, tutaj też próbowałem (nie działało), ale albo w MSSQL jest tylko jeden domyślny poziom, albo po prostu nigdy go nie zmieniałem, dlatego nawet nie wiedziałem że coś takiego może mi pomóc.
Dzięki bardzo, problem rozwiązany.

Jeśli ktoś poszukuje informacji o transakcjach w Laravelu, tutaj jest wydaje mi się dobry artykuł:
LINK

Jeszcze raz dziękuję i pozdrawiam!
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: 23.04.2024 - 17:29