
# Engines

## Een engine kiezen

Alle drie de engines slaan hetzelfde op: een id van UUIDv7 plus een array met data,
maar gebruiken daarvoor elk een andere indeling op schijf. Die indeling bepaalt waar elke engine goed in is:

|            | DocStore | SegmentedLog | Queue |
| ---------- | -------- | ------------ | ----- |
| Op schijf  | één JSONC-bestand per record, UUID-staart geshard | append-only-segmenten met regels met een voorvoegsel van lengte en checksum, in JSON-formaat, plus een manifest | één append-only-gebeurtenislog |
| Schrijven  | atomair bestand vervangen per record | records met checksum toevoegen; overschrijvingen en verwijderingen voegen nieuwe vermeldingen toe, ruimte wordt teruggewonnen door `compact()` | toevoegen met checksum per queuegebeurtenis |
| Lezen      | `get()` op id, geïndexeerde queries | cursor- en tijdsbereikscans, `get()` via de statusindex | `claim()` in enqueue-volgorde |
| Ideaal voor | puntlezingen en geïndexeerde lookups bij elke collectiegrootte | append-intensieve streams tot ongeveer 1M records | duurzame taakverdeling tussen processen |
| Minder geschikt voor | niet-geïndexeerde veldscans boven circa 10k records | veelvuldige herschrijvingen van één record (oude versies nemen ruimte in tot compactie) | geen algemene recordstore; payloads worden verwijderd zodra een taak is voltooid |

`DocStore` en `SegmentedLog` delen dezelfde vloeiende
[query API](https://storh.inline0.dev/nl/docs/query-builder): dezelfde `where()`-keten werkt op beide
engines; alleen de uitvoering verschilt. `DocStore` kan resultaten uit zijn
secundaire indexen ophalen, terwijl `SegmentedLog` filtert tijdens het scannen van segmenten.
`Queue` werkt met claims en ondersteunt geen queries: workers `claim()` de volgende taak in plaats
van taken te filteren.

Vuistregels:

- Records die u afzonderlijk ophaalt, bijwerkt of verwijdert en op veld opvraagt:
  `DocStore`. Voeg [indexen](https://storh.inline0.dev/nl/docs/indexes) toe voor de velden waarop u filtert.
- Gebeurtenissen of versies die u meestal toevoegt en in volgorde of per tijdvenster terugleest: `SegmentedLog`.
- Taken die producenten aan workerprocessen doorgeven: `Queue`. Claimen en voltooien is veilig voor meerdere processen, en `claimMany()`/`completeMany()` spreiden de kosten van locks en fsync over meerdere taken.
- Joins, zoeken of rapportages over een collectie: voeg een [SQL-mirror](https://storh.inline0.dev/nl/docs/sql-mirror) toe. Bestanden blijven canoniek; de mirror is een opnieuw op te bouwen projectie in SQLite of MySQL.

Engines kunnen worden gecombineerd: ze delen alleen de hoofdmap, zodat één applicatie content kan bewaren in een `DocStore`, een audittrail in een `SegmentedLog` en achtergrondtaken in een `Queue`, allemaal onder dezelfde opslaghoofdmap.

## DocStore

`DocStore` is bedoeld voor puntgewijze reads en eenvoudige scans van duurzame records. Er wordt één JSONC-bestand per record geschreven:

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

Recordbestanden worden geshard op basis van de laatste UUID tekens, zodat UUIDv7 schrijfpieken over mappen worden verspreid:

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

## SegmentedLog

`SegmentedLog` is bedoeld voor records die vaak worden toegevoegd, cursorreads en reads binnen een tijdsbereik. Records worden naar het actieve segment geschreven, één regel per record in de vorm `<byte-length>\t<xxh32>\t<json>` (zie [Atomiciteit](https://storh.inline0.dev/nl/docs/atomicity)). De segmentbestanden hebben de extensie `.ndjson`, maar door de prefix met lengte en checksum kunnen gewone NDJSON-tools ze niet lezen; lees records terug met `stream()` of de querybuilder. Volledige segmenten worden afgerond en vervangen door verzegelde segmenten met sparse indexen.

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

## Wachtrij

`Queue` is bedoeld voor duurzame wachtrijen met een hoge verwerkingscapaciteit. Enqueue, claim, complete, requeue en purge schrijven append-only-gebeurtenissen naar `queue.log`; de wachtrijstatus wordt bij het openen van de wachtrij opnieuw in het geheugen opgebouwd. Batchmethoden spreiden de kosten van locks en flushes over meerdere taken. Het log groeit met elke gebeurtenis; `stats()` geeft de huidige omvang in bytes weer.

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

## Duurzaamheid en gelijktijdigheid

storh maakt bestandsschrijfbewerkingen beschikbaar met een atomaire hernoeming binnen de doelmap. Bij opnieuw openen verwijderen `repair()` achtergelaten tijdelijke bestanden waarvan het eigenaarproces niet meer actief is, terwijl tijdelijke bestanden van actieve schrijvers blijven staan. Writer-markeringen maken deze controle constant in tijd, zodat een store de collectie alleen na een crash doorzoekt.

`SegmentedLog` en `Queue` schrijven logregels met lengte- en checksumcontrole. Opnieuw openen of repareren kapt afgebroken staartstukken af tot de laatste vastgelegde gebeurtenis en bouwt de status in het geheugen opnieuw op aan de hand van de duurzame loginhoud. Schrijfbewerkingen naar logs en wachtrijen worden met bestandssysteemlocks geserialiseerd.

Mutaties van `DocStore` worden geserialiseerd met een schrijfslot op collectieniveau, zodat recordbestanden en secundaire indexen als één consistent geheel worden bijgewerkt over processen heen. Gelijktijdig schrijven naar verschillende record-ID's is veilig onder dezelfde aannames over atomaire hernoeming door het bestandssysteem. Bij gelijktijdig schrijven naar dezelfde ID wint de laatste hernoeming.

storh flusht bestanden en voert fsync uit op bestandshandles voordat een hernoeming of toevoeging is afgerond, en probeert na atomaire hernoemingen fsync uit te voeren op bovenliggende mappen. De duurzaamheid bij stroomuitval blijft afhankelijk van de vraag of het onderliggende bestandssysteem en de mountopties deze synchronisaties respecteren.
