Súbežné zápisy a integrita
Dvaja ľudia, ktorí ukladajú v tej istej chvíli, nikdy nevytvoria duplicitné štítky, neporušia limit typu, nepripoja zmazaný štítok ani nepokazia počet použití. Každé meniace volanie beží v transakcii na spojení modelu štítku; vonkajšia transakcia pri deadlocku zopakuje pokus trikrát a pri každom pokuse načíta riadky znova a udalosti sa spustia až po potvrdení vonkajšej transakcie. Vo vašej vlastnej transakcii sa zápis k nej pripojí a neopakuje sa — Laravel vnorenú transakciu nikdy neopakuje, takže rozhoduje vaša vonkajšia transakcia.
Poradie zámkov
Každý zápis zamyká v pevnom poradí a zámky riadkov štítkov berie jedným vzostupným prechodom podľa kľúčov, takže dva zápisy na seba nikdy nečakajú v kruhu:
| Zápis | Zámky, v poradí |
|---|---|
| attach / detach / sync / clear | Pri názvoch voľného stromového typu najprv zámok tohto stromu. Potom vlastný riadok modelu, aby každé ďalšie čítanie videlo potvrdený zápis predchádzajúceho zapisovateľa a limit aj sync platili. Potom jeden prechod uzamkne každý riadok štítku, ktorého sa zápis dotkne — pomenované štítky, nesené štítky, ktoré odpája (vo vašej transakcii všetky nesené), a každý riadok, ktorý môže nový názov zmeniť: držiteľov slugov a v strome rodiča a podstrom zmazaného držiteľa. Nové voľné štítky vznikajú v savepointoch so zamykajúcim opätovným čítaním, pri štítkoch na pripojenie sa znova overí, že sú živé, a odpojenie znova prečíta, ktoré priradenia ešte nesie — len tie odstráni, zaráta a ohlási. |
| … vo vašej vlastnej transakcii | Najprv sa uzamknú riadky štítkov, na ktoré ukazujú riadky spojovacej tabuľky modelu (podľa vašej snímky), a štítky, ktoré pripája; potom sa riadky spojovacej tabuľky prečítajú zamykajúcim čítaním, ktoré vždy vidí najnovšie potvrdené riadky, a štítok, ktorý odhalil novší riadok, sa uzamkne naposledy. Riadky štítkov pred riadkami spojovacej tabuľky — v tom istom poradí zamyká zmazanie aj obnova štítku. |
| Vytvorenie v strome (aj koreň a znovu vytvorený názov), presun, zoradenie, zmazanie s deťmi, obnova | Najprv riadok tag_locks stromu (jeden na typ a rozsah); potom jedným vzostupným prechodom každý riadok štítku, o ktorom zápis rozhoduje alebo ktorý mení — samotný štítok, jeho starého a nového rodiča, podstrom, súrodencov posunutých pozíciou, zmazaný štítok, ktorý názov vráti. Vo vašej transakcii sa tieto riadky potom čítajú zamykajúcim čítaním. |
| Premenovanie / úprava | Riadok štítku a držitelia každého slugu, ktorý môže zmena prevziať, jedným prechodom v poradí kľúčov. |
tags:repair --paths | Strom po strome, vo vnútri zámku daného stromu. |
tags:repair --counts | Dávky po 500 štítkoch; každá pred počítaním uzamkne svoje riadky štítkov v poradí kľúčov. |
Keďže riadok modelu je prvý, aplikácia, ktorá v rámci vlastnej transakcie uzamkne model a potom pripojí nový názov, sa s balíkom nezablokuje. A keďže zápis štítkov vo vašej transakcii zamyká riadky štítkov pred riadkami spojovacej tabuľky, DB::transaction(fn () => $event->syncTags($labels, 'label')) sa nikdy nezablokuje so správcom, ktorý jedno z týchto označení maže.
Kedy môže deadlock ešte nastať
Databáza jednu stranu preruší; zápis vo vlastnej transakcii sa zopakuje, transakciu, ktorú ste otvorili vy, opakujete sami:
- vo vašej transakcii štítok, ktorý k tomu istému modelu pripojila iná požiadavka až po vytvorení vašej snímky a ktorý sa práve v tej chvíli maže;
- vo vašej transakcii zápis do stromu a zápis štítkov na dvoch štítkoch toho stromu, keď iná požiadavka po vytvorení vašej snímky presunula riadok do množiny zápisu do stromu — prechod číta množinu z tejto snímky a novší riadok sa uzamkne, až keď k nemu zápis dôjde;
- voľný názov, ktorého zmazaný držiteľ má ešte zvyšné priradenie na tom istom modeli (obyčajné $tag->delete()), znovu vytvorený vo vašej transakcii v čase, keď správca tento štítok obnovuje.
Čo je zaručené
- Dve súbežné attachTags() na type s jedným štítkom nikdy nenechajú modelu dve kategórie.
- Dve súbežné vytvorenia toho istého názvu skončia jedným štítkom a DuplicateTagException — pri findOrCreate() dostanú obe ten istý štítok.
- Pripojenie súbežné so zmazaním toho istého štítku nikdy nepripojí zmazaný štítok (TagNotFoundException, „The tag is deleted.“).
- Počty použití sa upravujú v tej istej transakcii ako riadky spojovacej tabuľky, presne o zapísané riadky, takže zostávajú presné; presné je aj prepočítanie opravou počas živej prevádzky.
- Presuny, zoradenia a mazanie podstromov sa v jednom strome nikdy neprekrývajú a vo vašej transakcii vidia strom tak, ako ho nechal zapisovateľ, na ktorého čakali: žiadny cyklus rodičov, žiadne prehliadnuté nové dieťa, žiadna zastaraná cesta.
- Odpojenie, ktoré čakalo na riadok štítku držaný mazaním, zaráta len priradenia, ktoré naozaj odstránilo.
MySQL a PostgreSQL
- Vkladanie je obyčajný INSERT, nikdy INSERT IGNORE, ktorý by aj chybu cudzieho kľúča či orezania premenil na upozornenie; neúspešné vloženie beží v savepointe, takže PostgreSQL ponechá vonkajšiu transakciu použiteľnú.
- Vo vnútri transakcie, ktorú ste otvorili — a ktorá si mohla snímku REPEATABLE READ v MySQL vziať skôr —, sa aktuálne priradenia aj každý riadok stromu, o ktorom zápis do stromu rozhoduje, čítajú zamykajúcim čítaním; agregácia sa číta ako najvyšší riadok, pretože PostgreSQL pri nej FOR UPDATE odmieta.
- Posledná pozícia súrodenca je jediné zostávajúce obyčajné čítanie: zastaraná hodnota len zrovná dvoch súrodencov, ktorí sa potom zoradia podľa pozície a kľúča.
- Kľúče zvonka sa pred akýmkoľvek SQL overia proti nakonfigurovanému typu kľúča — vrátane kľúča bigint mimo 64-bitového rozsahu —, pretože PostgreSQL by na nesúhlasiaci kľúč odpovedal chybou a MySQL by ho potichu pretypoval.
Každá udalosť implementuje ShouldDispatchAfterCommit: listenery sa spustia až po potvrdení zmeny a odvolaný zápis neohlási nič. Balík je testovaný proti PostgreSQL aj MySQL s paralelnými zápismi.
Zápisy mimo balíka
Hromadné mazanie modelov so štítkami, obyčajné $tag->delete(), import cez SQL či ručne upravené parent_id tieto záruky obídu. tags:doctor nájde, čo po nich zostane, a tags:repair to opraví — cesty a hĺbky, kľúče rozsahu, osirotené priradenia, priradenia na zmazaných štítkoch a počty použití — po dávkach, bezpečne aj pri živej prevádzke (pozrite Artisan príkazy).
Prejavte lásku k open source
Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.
Ďalšie spôsoby podpory vrátane kryptomienOdoslaním daru súhlasíte s našimi podmienkami prijímania darov.
Chcete to zabudovať do svojho produktu?
Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.