---
title: "Súbehy na reálnej databáze — Testing for Laravel | Roundly"
description: "Racer vytvorí druhú databázovú reláciu vo forknutom procese, takže test uvidí, že zámok na Postgrese či MySQL naozaj drží, nielen že bol vyžiadaný."
url: https://roundly-consulting.com/sk/open-source/navody/testing-for-laravel/subehy-na-realnej-databaze
language: sk
---

[Všetky balíky](https://roundly-consulting.com/sk/open-source.md)

[Testing for Laravel](https://roundly-consulting.com/sk/open-source/navody/testing-for-laravel.md)

# Súbehy na reálnej databáze

Záznamník zámkov dokazuje, že zámok bol vyžiadaný; že naozaj drží, dokáže až druhá relácia. RoundlyConsulting\\Testing\\Concurrency\\Racer forkne testovací proces a v potomkovi spustí jednu operáciu na vlastnom pripojení, kým rodič drží zámok:

```php
use Illuminate\Support\Facades\DB;
use RoundlyConsulting\Testing\Concurrency\Racer;

it('serialises two redemptions of the last coupon', function (): void {
    DB::beginTransaction();
    Coupon::query()->lockForUpdate()->find($coupon->id);

    $racer = (new Racer)
        ->run(fn (): string => Coupons::redeem($coupon->code) ? 'redeemed' : 'refused')
        ->waitUntilBlockedOrDone();

    Coupons::redeem($coupon->code);
    DB::commit();

    expect($racer->outcome())->toBe('refused');
})->skip(fn (): bool => ! Racer::available(), 'needs a real engine and pcntl + posix');
```

waitUntilBlockedOrDone() sa vráti, keď racer čaká za zámkom rodiča, takže rodič môže pred commitom dokončiť svoju stranu súbehu. Ten istý mechanizmus bez fasády v ceste:

```php
it('waits until the racer blocks on a row the parent holds, then lets it through', function (): void {
    DB::table('race_rows')->insert(['id' => 1, 'value' => 1]);

    DB::beginTransaction();
    DB::table('race_rows')->where('id', 1)->lockForUpdate()->first();

    $racer = (new Racer)->run(function (): string {
        DB::table('race_rows')->where('id', 1)->update(['value' => 2]);

        return 'updated';
    })->waitUntilBlockedOrDone();

    // Blocked, not finished: the row is still the parent's.
    expect(DB::table('race_rows')->value('value'))->toBe(1);

    DB::commit();

    expect($racer->outcome())->toBe('updated')
        ->and(DB::table('race_rows')->value('value'))->toBe(2);
})->skip(fn (): bool => ! Racer::available(), 'needs a real engine (pgsql/mysql) and pcntl + posix');
```

## API

| Člen | Správanie |
| --- | --- |
| `Racer::available()` | true na vetve pgsql alebo mysql (DriverMatrix::driver()), ak sú k dispozícii pcntl\_fork() a posix\_kill(). Použite ho v ->skip(), aby vetva, ktorá súbeh spustiť nevie, to viditeľne oznámila. |
| `run(Closure $operation)` | Forkne proces a v rodičovi sa hneď vráti. Operácia vracia výsledok ako reťazec. Racer beží raz: druhé run() vyhodí LogicException. Bez pcntl alebo posix vyhodí RuntimeException s názvami rozšírení. |
| `waitUntilBlockedOrDone(float $timeoutSeconds = 30.0)` | Vráti sa, keď racer čaká na zámok v databáze tohto testu (Postgres pg\_stat\_activity, MySQL performance\_schema.data\_lock\_waits) alebo skončil, pretože ho nič nezablokovalo. Racer, ktorý v časovom limite neurobí ani jedno, sa ukončí cez SIGKILL a volanie vyhodí RuntimeException — bez osirelého procesu. |
| `outcome()` | Počká na koniec racera a vráti, čo nahlásil. |
| `Racer::CONNECTION` | racer — predvolené pripojenie potomka. |

outcome() a waitUntilBlockedOrDone() zavolané pred run() vyhodia LogicException.

## Výsledky

```php
(new Racer)->run(fn (): string => 'won')->outcome();
// 'won'

(new Racer)->run(fn (): string => throw new RuntimeException('lost the race'))->outcome();
// 'error: RuntimeException: lost the race'

(new Racer)->run(function (): string { exit(0); })->outcome();
// 'error: the racer exited without reporting an outcome'

(new Racer)->run(fn (): string => '')->outcome();
// '' — an empty outcome stays apart from a racer that never reported

$racer = (new Racer)->run(function (): string {
    sleep(30);

    return 'too late';
});
$racer->waitUntilBlockedOrDone(timeoutSeconds: 0.3);
// throws RuntimeException: The racer neither blocked nor finished within 0.3 s.
// $racer->outcome() then starts with 'error: the racer was killed'
```

## Čo robí potomok a prečo

- Vlastné pripojenie. Zaregistruje racer z DriverMatrix::connectionConfig() — pri --parallel databázu workera — a nastaví ho ako predvolené. Pripojenia rodiča sa nikdy nedotkne: zdieľa s ním relácie na serveri a zatvorenie jednej by rodičovi ukončilo reláciu uprostred testu.
- Končí cez posix\_kill(SIGKILL), nikdy cez exit() ani návrat: žiadne shutdown handlery, žiadne deštruktory, takže nič nezatvorí zdieľané relácie — ani reláciu ponechanej schémy. Operácia, ktorá sama zavolá exit(), sa aj tak ukončí cez SIGKILL zo shutdown funkcie.
- Výsledky sú reťazce. Výnimka sa zmení na error: <trieda>: <správa>; operácia, ktorá zavolá exit(), nahlási, že skončila bez výsledku; potomok, ktorý bol ukončený zvonka alebo zlyhal skôr, než čokoľvek zapísal, nahlási, že zanikol bez výsledku. Prázdny reťazec zostane prázdnym reťazcom.
- Kontrola čakania na zámok sa obmedzuje na databázu tohto testu, takže čakanie iného paralelného workera tento test predčasne neuvoľní.

## Rozšírenia a enginy

- ext-pcntl a ext-posix sú odporúčané, nie povinné: bez nich sa balík nainštaluje ako zvyčajne, Racer::available() vráti false a testy súbehov sa viditeľne preskočia. Obe existujú len na Unixe.
- run() funguje aj na SQLite, no potomok s databázou v pamäti začína s vlastnou prázdnou databázou, takže súbeh tam nič nedokazuje — available() je false. Ani MariaDB tu nie je engine pre súbehy: jej tabuľky čakania na zámky sa líšia od MySQL.
- Racer nepotrebuje Testbench: použije ho každá Laravel aplikácia, ktorej testovacie pripojenie je cez maticu driverov pgsql alebo mysql.

## 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 kryptomien](https://roundly-consulting.com/sk/podporte-nas.md)

Odoslaním daru súhlasíte s našimi [podmienkami prijímania darov](https://roundly-consulting.com/sk/podmienky-darov.md).

[Podporte našu open-source prácu (otvorí sa v novej karte)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Pridajte sa k nám na Patreone (otvorí sa v novej karte)](https://www.patreon.com/cw/roundly)

## 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.

[Ponuka do 48 hodín](https://roundly-consulting.com/sk/kontakt.md) [Prezrieť všetky balíky](https://roundly-consulting.com/sk/open-source/navody/testing-for-laravel.md)
