
# Moottorit

## Moottorin valinta

Kaikki kolme moottoria tallentavat saman asian: UUIDv7-tunnisteen ja data-olioiden taulukon, mutta järjestävät tiedot levylle eri tavoin. Tallennustapa määrittää kunkin moottorin vahvuudet:

|            | DocStore | SegmentedLog | Queue |
| ---------- | -------- | ------------ | ----- |
| Levyrakenne | yksi JSONC-tiedosto tietuetta kohti, UUID-häntämerkkien mukaan jaettuna | lisäyskäyttöön tarkoitetut segmentit, joissa pituudella ja tarkistussummalla etuliitetyt JSON-rivit, sekä manifesti | yksi lisäyskäyttöön tarkoitettu tapahtumaloki |
| Kirjoittaminen | tietuekohtainen atominen tiedostokorvaus | tarkistussummalliset lisäykset; ylikirjoitukset ja poistot lisäävät uusia merkintöjä, tila vapautuu `compact()`-toiminnolla | jokaisen jonotapahtuman tarkistussummallinen lisäys |
| Lukeminen | `get()` tunnisteen perusteella, indeksoidut haut | kohdistin- ja aikaväliluvut, `get()` tilaindeksin avulla | `claim()` lisäysjärjestyksessä |
| Sopii parhaiten | yksittäislukuihin ja indeksoituihin hakuihin kaiken kokoisissa kokoelmissa | lisäyspainotteisiin virtoihin noin 1M tietueeseen asti | pysyvien töiden välittämiseen prosessien välillä |
| Heikkous | indeksoimattomien kenttien läpikäynti yli noin 10k tietueen kokoelmissa | usein toistuviin yksittäisten tietueiden uudelleenkirjoituksiin (vanhat versiot vievät tilaa tiivistykseen asti) | ei ole yleiskäyttöinen tietuevarasto; hyötykuormat pudotetaan työn valmistuttua |

`DocStore` ja `SegmentedLog` käyttävät samaa sujuvaa kyselyrajapintaa [API](https://storh.inline0.dev/fi/docs/query-builder): sama `where()`-ketju toimii kummassakin moottorissa, ja vain suoritus eroaa. `DocStore` voi hakea tuloksen toissijaisista indekseistään, kun taas `SegmentedLog` suodattaa segmenttejä lukiessaan. `Queue` perustuu varaamiseen eikä tue kyselyitä: työntekijät `claim()` seuraavan työn suodattamisen sijaan.

Nyrkkisäännöt:

- Tietueet, joita noudat, päivität tai poistat yksitellen ja haet kentän perusteella:
  `DocStore`. Lisää [indeksit](https://storh.inline0.dev/fi/docs/indexes) kentille, joiden perusteella suodatat.
- Tapahtumat tai versiot, joita pääasiassa lisäät ja luet järjestyksessä tai aikavälin mukaan: `SegmentedLog`.
- Tuottajilta työntekijäprosesseille välitettävät työt: `Queue`. Varaus ja valmistuminen ovat turvallisia usean prosessin käytössä, ja `claimMany()`/`completeMany()` vähentävät lukituksen ja fsync-operaatioiden kustannusta.
- Liitokset, haku tai raportointi missä tahansa kokoelmassa: lisää [SQL-peili](https://storh.inline0.dev/fi/docs/sql-mirror). Tiedostot pysyvät kanonisina; peili on uudelleenrakennettava projektio kohteessa SQLite tai MySQL.

Moottorit toimivat yhdessä: ne jakavat vain juurihakemiston, joten sovellus voi tallentaa sisällön kohteeseen `DocStore`, tarkastusketjun kohteeseen `SegmentedLog` ja taustatyöt kohteeseen `Queue` saman tallennusjuuren alle.

## DocStore

`DocStore` sopii yksittäislukuihin ja yksinkertaisiin pysyvien tietueiden läpikäynteihin. Se kirjoittaa yhden JSONC-tiedoston tietuetta kohti:

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

Tietuetiedostot jaetaan UUID-häntämerkkien mukaan, jotta UUIDv7-kirjoituspiikit jakautuvat eri hakemistoihin:

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

## SegmentedLog

`SegmentedLog` sopii lisäyspainotteisille tietueille, kohdistinluvuille ja aikaväliluvuille. Tietueet kirjoitetaan aktiiviseen segmenttiin, yksi rivi tietuetta kohti muodossa `<byte-length>\t<xxh32>\t<json>` (katso [Atomisuus](https://storh.inline0.dev/fi/docs/atomicity)). Segmenttitiedostojen tunniste on `.ndjson`, mutta pituus- ja tarkistussummaetuliitteen vuoksi tavalliset NDJSON-työkalut eivät voi lukea niitä; lue tietueet takaisin kohteella `stream()` tai kyselyrakentajalla. Täydet segmentit siirtyvät suljetuiksi segmenteiksi, joilla on harvat indeksit.

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

## Queue

`Queue` on tarkoitettu suuritehoisiin pysyviin työjonoihin. Enqueue, claim, complete, requeue ja purge kirjoittavat lisäyskäyttöön tarkoitettuja tapahtumia kohteeseen `queue.log`; jonon tila luetaan uudelleen muistiin, kun jono avataan. Erämenetelmät jakavat lukituksen ja tyhjennyksen kustannuksen useille töille. Lokia kasvattaa jokainen tapahtuma; `stats()` ilmoittaa sen nykyisen koon tavuina.

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

## Pysyvyys ja samanaikaisuus

storh julkaisee tiedostokirjoitukset atomisella nimeämisellä kohdehakemiston sisällä. Uudelleenavaus ja `repair()` poistavat hylätyt väliaikaistiedostot, joiden omistajaprosessi on päättynyt, mutta jättävät aktiivisten kirjoittajien väliaikaistiedostot rauhaan. Kirjoittajamerkkien ansiosta tarkistus on vakioaikainen, joten tallennuksen avaaminen käy kokoelman läpi vain kaatumisen jälkeen.

`SegmentedLog` ja `Queue` kirjoittavat pituudella ja tarkistussummalla suojattuja lokirivejä. Uudelleenavaus tai korjaus katkaisee rikkoutuneet loput viimeiseen vahvistettuun tapahtumaan asti ja rakentaa muistissa olevan tilan uudelleen kestävän lokin sisällöstä. Lokin ja jonon kirjoitukset sarjallistetaan tiedostojärjestelmälukoilla.

`DocStore`-muutokset sarjallistetaan kokoelman tason kirjoituslukolla, joten tietuetiedostot ja toissijaiset indeksit päivittyvät yhtenä eheysrajana prosessien välillä. Samanaikaiset kirjoitukset eri tietuetunnuksiin ovat turvallisia samoilla tiedostojärjestelmän atomisen uudelleennimeämisen oletuksilla. Samanaikaisista saman tunnuksen kirjoituksista viimeinen uudelleennimeäminen voittaa.

storh tyhjentää tiedostokahvojen puskurit ja synkronoi ne ennen uudelleennimeämisen tai lisäyksen valmistumista sekä yrittää synkronoida ylätason hakemistot atomisten uudelleennimeämisten jälkeen. Sähkökatkon kestävyys riippuu silti siitä, noudattavatko alla oleva tiedostojärjestelmä ja liitosasetukset näitä synkronointeja.
