
# Motorer

## Valg af motor

Alle tre motorer gemmer det samme: et id af typen UUIDv7 plus et data-array, men de organiserer det forskelligt på disken. Denne organisering afgør, hvad hver motor egner sig til:

|            | DocStore | SegmentedLog | Queue |
| ---------- | -------- | ------------ | ----- |
| På disk    | én JSONC-fil pr. post, opdelt i shards efter de sidste UUID tegn | tilføjelsesbaserede segmenter med længde- og kontrolsumpræfikserede JSON-linjer samt et manifest | én tilføjelsesbaseret hændelseslog |
| Skrivning  | atomisk filudskiftning pr. post | tilføjelser med kontrolsum; overskrivninger og sletninger tilføjer nye poster, som frigøres ved `compact()` | tilføjelse med kontrolsum for hver køhændelse |
| Læsning    | `get()` efter id, indekserede forespørgsler | markør- og tidsområdescanninger, `get()` via tilstandsindekset | `claim()` i enqueue-rækkefølge |
| Bedst til | punktlæsninger og indekserede opslag ved alle samlingsstørrelser | streams med mange tilføjelser, op til omkring 1M poster | varig arbejdsfordeling mellem processer |
| Svaghed    | scanning af ikke-indekserede felter over cirka 10k poster | hyppige omskrivninger af enkeltposter (ældre versioner optager plads indtil komprimering) | ikke et generelt postlager; payloads fjernes, når et job er afsluttet |

`DocStore` og `SegmentedLog` deler den samme flydende [forespørgsels-API](https://storh.inline0.dev/da/docs/query-builder): den samme `where()`-kæde fungerer på begge motorer, og kun udførelsen er forskellig. `DocStore` kan hente svar fra sine sekundære indekser, mens `SegmentedLog` filtrerer under scanning af segmenter.
`Queue` bruger claims, ikke forespørgsler: arbejdere `claim()` det næste job i stedet for at filtrere job.

Tommelfingerregler:

- Poster, som du henter, opdaterer eller sletter enkeltvis og forespørger på efter felt: `DocStore`. Tilføj [indekser](https://storh.inline0.dev/da/docs/indexes) for de felter, du filtrerer på.
- Hændelser eller versioner, som du hovedsageligt tilføjer og læser tilbage i rækkefølge eller inden for et tidsinterval: `SegmentedLog`.
- Job, der sendes fra producenter til arbejdsprocesser: `Queue`. Claims og afslutninger er sikre på tværs af processer, og `claimMany()`/`completeMany()` fordeler låse- og fsync-omkostningerne.
- Sammenføjninger, søgning eller rapportering på tværs af en samling: tilføj et [SQL-spejl](https://storh.inline0.dev/da/docs/sql-mirror). Filerne forbliver kanoniske; spejlet er en genopbyggelig projektion i SQLite eller MySQL.

Motorer kan kombineres: de deler kun rodmappen, så en applikation kan gemme indhold i en `DocStore`, et revisionsspor i en `SegmentedLog` og baggrundsarbejde i en `Queue` under samme lagerrod.

## DocStore

`DocStore` er til punktlæsninger og enkle scanninger af varige poster. Den skriver én JSONC-fil pr. post:

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

Postfiler opdeles i shards efter de sidste UUID tegn, så skriveudbrud fra UUIDv7 fordeles på tværs af mapper:

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

## SegmentedLog

`SegmentedLog` er til poster med mange tilføjelser, markørlæsninger og tidsområdelæsninger. Poster skrives til det aktive segment, én linje pr. post i formatet `<byte-length>\t<xxh32>\t<json>` (se [Atomicitet](https://storh.inline0.dev/da/docs/atomicity)). Segmentfilerne bruger filtypen `.ndjson`, men længde- og kontrolsumpræfikset betyder, at almindelige NDJSON-værktøjer ikke kan læse dem; læs posterne med `stream()` eller forespørgselsbyggeren. Fulde segmenter rulles over i forseglede segmenter med sparsomme indekser.

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

## Queue

`Queue` er til varige arbejdskøer med høj gennemstrømning. Enqueue, claim, complete, requeue og purge skriver tilføjelsesbaserede hændelser til `queue.log`; køtilstanden afspilles til hukommelsen, når køen åbnes. Batchmetoder fordeler låse- og flush-omkostningen på mange job. Loggen vokser med hver hændelse; `stats()` rapporterer dens aktuelle størrelse i bytes.

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

## Holdbarhed og samtidighed

storh offentliggør filskrivninger med atomisk omdøbning i målmappen. Ved genåbning og `repair()` fjernes forladte midlertidige filer, hvis ejerproces er afsluttet, mens midlertidige filer fra aktive skrivere bevares. Markører for skrivere gør kontrollen hurtig, så samlingen kun gennemgås, når et lager åbnes efter et nedbrud.

`SegmentedLog` og `Queue` skriver loglinjer med længde- og kontrolsumkontrol. Genåbning eller reparation afkorter afbrudte slutninger til den senest bekræftede hændelse og genopbygger tilstanden i hukommelsen ud fra loggens varige indhold. Log- og køskrivninger serialiseres med filsystemlåse.

`DocStore`-ændringer serialiseres med en skrivelås på samlingsniveau, så postfiler og sekundære indekser opdateres samlet som én konsistensgrænse på tværs af processer. Samtidige skrivninger til forskellige post-id'er er sikre under de samme antagelser om atomisk omdøbning i filsystemet. Ved samtidige skrivninger til samme id vinder den sidste omdøbning.

storh tømmer og fsync'er filhandles før omdøbning eller afslutning af tilføjelse og forsøger at fsync'e overordnede mapper efter atomiske omdøbninger. Holdbarhed ved strømsvigt afhænger stadig af, om det underliggende filsystem og monteringsindstillingerne overholder disse synkroniseringer.
