
# Mootorid

## Mootori valimine

Kõik kolm mootorit salvestavad sama tüüpi sisu: UUIDv7 ID ja andmemassiivi,
kuid paigutavad selle kettale erinevalt. Paigutus määrab, milleks iga mootor
sobib:

|            | DocStore | SegmentedLog | Queue |
| ---------- | -------- | ------------ | ----- |
| Kettal     | üks JSONC fail kirje kohta, UUID lõpu järgi jaotatud | ainult lisatavad pikkuse ja kontrollsummaga prefiksiga JSON read segmentides ning manifest | üks ainult lisatav sündmuste logi |
| Kirjutamine | iga kirje puhul faili atomaarne asendamine | kontrollsummaga lisamised; ülekirjutused ja kustutamised lisavad uusi kirjeid, ruum vabastatakse `compact()` abil | iga järjekorrasündmuse lisamine kontrollsummaga |
| Lugemine | `get()` ID järgi, indekseeritud päringud | kursori- ja ajavahemiku skannimised, `get()` olekuindeksi kaudu | `claim()` lisamise järjekorras |
| Sobib kõige paremini | üksikkirje lugemiseks ja indekseeritud otsinguteks mis tahes kogu suuruse korral | lisamismahukate voogude jaoks, kuni umbes 1M kirjet | püsivaks tööde suunamiseks protsesside vahel |
| Nõrk koht | indekseerimata väljade skannimine, kui kirjeid on üle ligikaudu 10k | sagedased üksikkirje ümberkirjutused (vanad versioonid võtavad ruumi kuni tihendamiseni) | pole üldotstarbeline kirjehoidla; koormad eemaldatakse töö lõpetamisel |

`DocStore` ja `SegmentedLog` kasutavad sama sujuvat
[päringu API](https://storh.inline0.dev/et/docs/query-builder) liidest: sama `where()` ahel töötab mõlema mootoriga ja erineb ainult täitmine. `DocStore` saab vastata sekundaarsete indeksite põhjal, `SegmentedLog` aga filtreerib segmentide skannimisel.
`Queue` põhineb hõivamisel ega toeta päringuid: töötajad `claim()` järgmise töö, mitte ei filtreeri töid.

Rusikareeglid:

- Kirjed, mida hangite, uuendate või kustutate ükshaaval ning mille kohta esitate väljade järgi päringuid:
  `DocStore`. Lisage [indeksid](https://storh.inline0.dev/et/docs/indexes) väljadele, mille järgi filtreerite.
- Sündmused või versioonid, mida peamiselt lisate ning loete tagasi järjekorras või ajavahemiku järgi:
  `SegmentedLog`.
- Tootjatelt tööprotsessidele edasi antavad tööd: `Queue`. Hõivamised ja lõpetamised on turvalised mitme protsessi korral ning `claimMany()`/`completeMany()` vähendavad luku ja fsync-i kulu.
- Ühendamised, otsing või aruandlus mis tahes kogu kohta: lisage
  [SQL peegel](https://storh.inline0.dev/et/docs/sql-mirror). Failid jäävad kanooniliseks; peegel on uuesti koostatav projektsioon asukohas SQLite või MySQL.

Mootoreid saab kombineerida: neil on ühine ainult juurkataloog, nii et rakendus saab hoida sisu mootoris `DocStore`, auditijälge mootoris `SegmentedLog` ja taustatöid mootoris `Queue` sama salvestusjuure all.

## DocStore

`DocStore` sobib püsikirjete üksiklugemiseks ja lihtsateks skannimisteks. See kirjutab
iga kirje kohta ühe JSONC faili:

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

Kirjefailid jaotatakse UUID viimase märgi järgi, et UUIDv7 kirjutusimpulsid jaguneksid kataloogide vahel:

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

## SegmentedLog

`SegmentedLog` sobib paljude lisamiste, kursori abil lugemise ja ajavahemiku järgi lugemise jaoks.
Kirjed kirjutatakse aktiivsesse segmenti, üks rida kirje kohta kujul
`<byte-length>\t<xxh32>\t<json>` (vt [Atomaarusus](https://storh.inline0.dev/et/docs/atomicity)). Segmendifailidel on laiend `.ndjson`,
kuid pikkuse ja kontrollsumma prefiksi tõttu ei saa neid tavaliste NDJSON-tööriistadega lugeda; lugege kirjeid tagasi tööriistaga `stream()` või päringukoostajaga. Täielikud segmendid suletakse ja neile lisatakse hõredad indeksid.

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

## Queue

`Queue` on mõeldud suure läbilaskevõimega püsivate tööjärjekordade jaoks. Tööde lisamine, hõivamine, lõpetamine, uuesti järjekorda panemine ja tühjendamine kirjutavad lisamisrežiimis sündmusi faili `queue.log`; järjekorra olek laaditakse järjekorra avamisel mällu sündmusi taasesitades. Paketmeetodid jaotavad lukustamise ja kettale kirjutamise kulu paljude tööde vahel. Logi kasvab iga sündmusega; `stats()` näitab selle praegust suurust baitides.

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

## Püsivus ja samaaegne kasutus

storh avaldab failikirjutused sihtkataloogis aatomilise ümbernimetamise teel. Uuesti avamine ja `repair()` eemaldavad hüljatud ajutised failid, mille omanikprotsess on lõppenud, jättes aktiivsete kirjutajate ajutised failid alles. Kirjutajamarkerite abil on kontroll konstantse ajakuluga, nii et salvesti avamisel vaadatakse kogumik läbi ainult pärast krahhi.

`SegmentedLog` ja `Queue` kirjutavad logiridu, mille pikkus ja kontrollsumma on kaitstud. Uuesti avamine või parandamine kärbib poolikud lõpuosad viimase kinnitatud sündmuseni ning taastab mälus oleva oleku püsiva logi sisu põhjal. Logi- ja järjekorrakirjutused järjestatakse failisüsteemi lukkudega.

`DocStore` muudatused järjestatakse kogumitaseme kirjutuslukuga, nii et kirjefailid ja sekundaarsed indeksid uuenevad protsessideüleselt ühe terviklikkuse piirina. Eri kirje ID-de samaaegne kirjutamine on ohutu samadel failisüsteemi aatomilise ümbernimetamise eeldustel. Sama ID puhul võidab viimasena ümbernimetatud fail.

storh tühjendab failikäepidemed puhvrisse ja sünkroonib need enne ümbernimetamise või lisamise lõpetamist ning püüab pärast aatomilisi ümbernimetamisi sünkroonida ka ülemkataloogid. Elektrikatkestuse korral sõltub andmete säilimine siiski sellest, kas aluseks olev failisüsteem ja ühendamisvalikud neid sünkroonimisi järgivad.
