
# Motorok

## Motorválasztás

Mindhárom motor ugyanazt tárolja: egy UUIDv7 azonosítót és egy adattömböt,
de másképp rendezi el őket a lemezen. Ez az elrendezés határozza meg, mire alkalmasak az egyes motorok:

|            | DocStore | SegmentedLog | Queue |
| ---------- | -------- | ------------ | ----- |
| Lemezen    | rekordonként egy JSONC fájl, UUID-farok szerinti particionálással | csak hozzáfűzhető szegmensek, hossz- és ellenőrzőösszeg-előtaggal ellátott JSON sorokkal, valamint jegyzékkel | egy csak hozzáfűzhető eseménynapló |
| Írás       | atomi fájlcserével rekordként | ellenőrzőösszeggel védett hozzáfűzések; a felülírások és törlések új bejegyzést fűznek hozzá, a helyet a(z) `compact()` szabadítja fel | ellenőrzőösszeggel védett hozzáfűzés minden sorsoreseménynél |
| Olvasás    | `get()` azonosító alapján, indexelt lekérdezések | kurzor- és időtartomány-pásztázás, `get()` az állapotindex alapján | `claim()` sorba állítási sorrendben |
| Ideális    | pontszerű olvasásokhoz és indexelt keresésekhez bármilyen gyűjteményméretnél | sok hozzáfűzésű adatfolyamokhoz, körülbelül 1M rekordig | tartós munkafeladatok folyamatok közötti kiosztásához |
| Hátrány    | nem indexelt mezők pásztázása körülbelül 10k rekord felett | gyakori, egyrekordos átírások (a régi verziók a tömörítésig helyet foglalnak) | nem általános rekordtár; a hasznos adatok a feladat befejezése után törlődnek |

A(z) `DocStore` és a(z) `SegmentedLog` ugyanazt a gördülékeny
[lekérdezési API](https://storh.inline0.dev/hu/docs/query-builder) kezelőfelületet használja: ugyanaz a `where()` lánc mindkét
motoron fut, és csak a végrehajtás tér el. A(z) `DocStore` a másodlagos
indexeiből is tud választ adni, míg a(z) `SegmentedLog` szegmensek pásztázása közben
szűr. A(z) `Queue` igénylésalapú, nem kérdezhető le: a feldolgozók az
következő feladatot `claim()`, nem pedig szűrik a feladatokat.

Ökölszabályok:

- Egyenként lekért, frissített vagy törölt, illetve mező alapján lekérdezhető rekordok:
  `DocStore`. Adjon hozzá [indexeket](https://storh.inline0.dev/hu/docs/indexes) a szűréshez használt mezőkhöz.
- Többnyire hozzáfűzött, majd sorrendben vagy időablak szerint visszaolvasott események vagy verziók: `SegmentedLog`.
- A létrehozóktól a feldolgozó folyamatokhoz továbbított feladatok: `Queue`. A lefoglalás és a befejezés több folyamat között is biztonságos, a `claimMany()`/`completeMany()` pedig megosztja a zárolás és az fsync költségét.
- Összekapcsolások, keresés vagy jelentéskészítés bármely gyűjteményen: adjon hozzá egy [SQL tükröt](https://storh.inline0.dev/hu/docs/sql-mirror). A fájlok maradnak a hiteles források; a tükör újraépíthető vetület SQLite vagy MySQL formátumban.

A motorok kombinálhatók: csak a gyökérkönyvtárat osztják meg, így egy alkalmazás ugyanazon tárolási gyökér alatt tarthatja a tartalmat egy `DocStore`-ban, az auditnaplót egy `SegmentedLog`-ban, a háttérfeladatokat pedig egy `Queue`-ban.

## DocStore

A `DocStore` tartós rekordok közvetlen lekérésére és egyszerű bejárására szolgál. Rekordonként egy JSONC fájlt ír:

```php
$store = new Storh\DocStore($root, 'articles');
$record = $store->put(['slug' => 'hello', 'published' => true]);
$same = $store->get($record->id());
```

A rekordfájlok elosztása a UUID utolsó karakterei alapján történik, így a UUIDv7 írási terhelési csúcsok több könyvtár között oszlanak meg:

```text
articles/data/2d/018bcfe5-6800-7000-8000-2d5dba6274da.jsonc
```

## SegmentedLog

A `SegmentedLog` hozzáfűzés-intenzív rekordokhoz, kurzoros olvasáshoz és időtartomány szerinti olvasáshoz készült. A rekordok az aktív szegmensbe kerülnek, rekordonként egy sorban, `<byte-length>\t<xxh32>\t<json>` formátumban (lásd: [Atomi működés](https://storh.inline0.dev/hu/docs/atomicity)). A szegmensfájlok kiterjesztése `.ndjson`, de a hossz- és ellenőrzőösszeg-előtag miatt a hagyományos NDJSON-eszközök nem tudják olvasni őket; a rekordokat a `stream()` vagy a lekérdezésépítő segítségével olvassa vissza. A megtelt szegmensek lezárt szegmensekké válnak, ritka indexekkel.

```php
$events = new Storh\SegmentedLog($root, 'events');
$events->put(['type' => 'user.created']);
$events->compact();
```

## Sor

A `Queue` nagy áteresztőképességű, tartós feladatsorokhoz készült. A sorba állítás, lefoglalás, befejezés, újrasorba állítás és törlés csak hozzáfűzött eseményeket ír a `queue.log` fájlba; a sor állapota a sor megnyitásakor kerül visszajátszásra a memóriába. A kötegelt metódusok sok feladat között osztják el a zárolás és a kiürítés költségét. A napló minden eseménnyel növekszik; a `stats()` jelenti az aktuális méretét bájtban.

```php
$queue = new Storh\Queue($root, 'mail');
$queue->enqueue(['to' => 'person@example.test']);
$job = $queue->claim();
```

## Tartósság és párhuzamosság

A storh atomi átnevezéssel teszi közzé a fájlírásokat a célkönyvtáron belül. Újranyitáskor és a `repair()` eltávolítja azokat az elhagyott ideiglenes fájlokat, amelyek tulajdonos folyamata már leállt, az aktív írók ideiglenes fájljait viszont érintetlenül hagyja. Az írójelölők révén az ellenőrzés állandó idejű, így a tároló megnyitásakor csak összeomlás után vizsgálja át a gyűjteményt.

A `SegmentedLog` és a `Queue` hosszal és ellenőrzőösszeggel védett naplósorokat ír. Újranyitáskor vagy javításkor a rendszer a részlegesen írt napló végét az utolsó véglegesített eseménynél csonkolja, majd a tartós naplótartalomból újraépíti a memóriabeli állapotot. A napló- és sorírásokat fájlrendszer-zárolások sorosítják.

A `DocStore` módosításait gyűjteményszintű írási zárolás sorosítja, így a rekordfájlok és a másodlagos indexek egyetlen konzisztenciahatár mentén frissülnek a folyamatok között. A különböző rekordazonosítókra irányuló párhuzamos írások biztonságosak, ha a fájlrendszer biztosítja az atomi átnevezés feltételeit. Ugyanazon azonosító egyidejű írásai közül az utoljára átnevezett érvényesül.

A storh kiüríti és fsync-kel szinkronizálja a fájlleírókat az átnevezés vagy a hozzáfűzés befejezése előtt, és az atomi átnevezések után megkísérli az fsync-et a szülőkönyvtárakon. Az áramkimaradással szembeni tartósság továbbra is attól függ, hogy az alapul szolgáló fájlrendszer és a csatolási beállítások betartják-e ezeket a szinkronizálásokat.
