
# Motori

## Scegliere un motore

Tutti e tre i motori memorizzano le stesse informazioni: un ID UUIDv7 e un array di dati,
ma le dispongono diversamente sul disco. È questa disposizione a determinare i punti di forza di ciascun motore:

|            | DocStore | SegmentedLog | Queue |
| ---------- | -------- | ------------ | ----- |
| Su disco    | un file JSONC per record, suddiviso in partizioni UUID-tail | segmenti append-only di righe JSON precedute da lunghezza e checksum, più un manifest | un log di eventi append-only |
| Scrittura    | sostituzione atomica del file per record | aggiunte con checksum; le sovrascritture e le eliminazioni aggiungono nuove voci, recuperate con `compact()` | aggiunta con checksum per ogni evento della coda |
| Lettura    | `get()` per ID, query indicizzate | scansioni con cursore e per intervallo di tempo, `get()` tramite l'indice di stato | `claim()` nell'ordine di accodamento |
| Punto di forza | letture puntuali e ricerche indicizzate per raccolte di qualsiasi dimensione | flussi con molte aggiunte fino a circa 1M di record | distribuzione affidabile del lavoro tra processi |
| Punto debole  | scansioni di campi senza indice oltre circa 10k record | riscritture frequenti di singoli record (le versioni precedenti occupano spazio fino alla compattazione) | non è un archivio generico di record; i payload vengono eliminati al completamento di un job |

`DocStore` e `SegmentedLog` condividono la stessa [query API](https://storh.inline0.dev/it/docs/query-builder) fluida: la stessa catena `where()` viene eseguita su entrambi i motori e cambia solo l'esecuzione. `DocStore` può rispondere usando gli indici secondari, mentre `SegmentedLog` filtra durante la scansione dei segmenti. `Queue` si basa sull'acquisizione e non consente query: i worker `claim()` il job successivo invece di filtrare i job.

Regole pratiche:

- Record che recuperate, aggiornate o eliminate singolarmente e interrogate per campo:
  `DocStore`. Aggiungete [indici](https://storh.inline0.dev/it/docs/indexes) per i campi su cui applicate filtri.
- Eventi o versioni che per lo più aggiungete e rileggete in ordine o in una
  finestra temporale: `SegmentedLog`.
- Processi affidati dai produttori ai processi worker: `Queue`. Le richieste
  e i completamenti sono sicuri tra più processi, mentre `claimMany()`/`completeMany()`
  ammortizzano il costo di lock e fsync.
- Join, ricerca o report su qualsiasi raccolta: aggiungete un
  [mirror](https://storh.inline0.dev/it/docs/sql-mirror) SQL. I file restano canonici; il mirror è una
  proiezione ricostruibile in SQLite o MySQL.

I motori si combinano: condividono solo la directory radice, quindi un'applicazione può
conservare i contenuti in un `DocStore`, il registro di audit in un `SegmentedLog` e
le attività in background in un `Queue`, tutti sotto la stessa radice di archiviazione.

## DocStore

`DocStore` serve per letture puntuali e scansioni semplici di record persistenti. Scrive
un file JSONC per ogni record:

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

I file dei record sono distribuiti in partizioni in base agli ultimi UUID caratteri, così i picchi di scrittura di UUIDv7 si distribuiscono
tra le directory:

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

## SegmentedLog

`SegmentedLog` serve per record con molte aggiunte, letture tramite cursore e letture per intervallo temporale.
I record vengono scritti nel segmento attivo, una riga per record nel formato
`<byte-length>\t<xxh32>\t<json>` (vedere [Atomicità](https://storh.inline0.dev/it/docs/atomicity)). I
file di segmento usano l'estensione `.ndjson`, ma il prefisso con lunghezza e checksum
impedisce ai normali strumenti NDJSON di leggerli; rileggete i record con `stream()` o
con il generatore di query. I segmenti completi diventano segmenti sigillati con indici sparsi.

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

## Coda

`Queue` serve per code di lavoro persistenti ad alto throughput. Accodamento, richiesta, completamento,
riaccodamento e svuotamento scrivono eventi in append-only su `queue.log`; lo stato della coda viene
ricostruito in memoria all'apertura della coda. I metodi batch ammortizzano il costo di lock e
flush su molti processi. Il log cresce a ogni evento; `stats()`
riporta la dimensione attuale in byte.

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

## Persistenza e concorrenza

storh pubblica le scritture su file tramite rinomina atomica all'interno della directory di destinazione.
Alla riapertura, `repair()` rimuove i file temporanei abbandonati il cui processo proprietario è terminato,
lasciando intatti quelli dei processi di scrittura attivi. I marcatori dei writer rendono il controllo
costante, quindi l'apertura di un archivio esegue la scansione della raccolta solo dopo un arresto anomalo.

`SegmentedLog` e `Queue` scrivono righe di log protette da lunghezza e checksum. Alla riapertura
oppure durante la riparazione, le code troncate vengono riportate all'ultimo evento confermato e lo stato
in memoria viene ricostruito dai contenuti persistenti del log. Le scritture su log e coda sono serializzate con
lock del filesystem.

Le modifiche di `DocStore` sono serializzate con un lock di scrittura a livello di raccolta, così
l'aggiornamento dei file dei record e degli indici secondari costituisce un unico confine di coerenza tra
processi. Le scritture simultanee su ID di record distinti sono sicure con le stesse
ipotesi sulla rinomina atomica del filesystem. In caso di scritture simultanee sullo stesso ID, prevale l'ultima rinomina.

storh esegue il flush e fsync degli handle di file prima del completamento della rinomina o dell'aggiunta e
prova a eseguire fsync sulle directory padre dopo le rinomine atomiche. La persistenza in caso di interruzione
improvvisa dell'alimentazione dipende comunque dal fatto che il filesystem sottostante e le opzioni di mount rispettino
queste operazioni di sincronizzazione.
