
# Motoare

## Alegerea unui motor

Toate cele trei motoare stochează aceleași date: un ID UUIDv7 plus un tablou de date,
dar le organizează diferit pe disc. Această organizare determină la ce se
pricepe fiecare motor:

|            | DocStore | SegmentedLog | Queue |
| ---------- | -------- | ------------ | ----- |
| Pe disc    | un fișier JSONC per înregistrare, partiționat după ultimele caractere UUID | segmente doar pentru adăugare, cu linii JSON precedate de lungime și sumă de control, plus un manifest | un jurnal de evenimente doar pentru adăugare |
| Scriere    | înlocuire atomică de fișier per înregistrare | adăugări cu sume de control; suprascrierile și ștergerile adaugă intrări noi, recuperate prin `compact()` | adăugare cu sumă de control per eveniment din coadă |
| Citire     | `get()` după ID, interogări indexate | scanări cu cursor și pe interval de timp, `get()` prin indexul de stare | `claim()` în ordinea adăugării în coadă |
| Punct forte | citiri punctuale și căutări indexate pentru colecții de orice dimensiune | fluxuri intensive în adăugări, cu până la aproximativ 1M de înregistrări | distribuirea persistentă a sarcinilor între procese |
| Punct slab | scanări ale câmpurilor fără index pentru colecții de peste aproximativ 10k înregistrări | rescrieri frecvente ale unei singure înregistrări (versiunile vechi ocupă spațiu până la compactare) | nu este un depozit general de înregistrări; payload-urile sunt eliminate la finalizarea unei sarcini |

`DocStore` și `SegmentedLog` folosesc aceeași [interogare fluentă API](https://storh.inline0.dev/ro/docs/query-builder): aceeași succesiune `where()` de apeluri funcționează pe ambele motoare, diferind doar execuția. `DocStore` poate răspunde folosind indexurile secundare, în timp ce `SegmentedLog` filtrează la scanarea segmentelor. `Queue` se bazează pe preluarea sarcinilor, nu pe interogări: lucrătorii `claim()` următoarea sarcină în loc să filtreze sarcinile.

Reguli orientative:

- Înregistrări pe care le preluați, actualizați sau ștergeți individual și le interogați după câmp:
  `DocStore`. Adăugați [indexuri](https://storh.inline0.dev/ro/docs/indexes) pentru câmpurile după care filtrați.
- Evenimente sau versiuni pe care, în principal, le adăugați și le citiți în ordine sau într-o fereastră de timp: `SegmentedLog`.
- Sarcini transmise de producători către procese lucrătoare: `Queue`. Preluările și
  finalizările sunt sigure în mai multe procese, iar `claimMany()`/`completeMany()` distribuie
  costurile de blocare și fsync.
- Îmbinări, căutări sau raportări în orice colecție: adăugați o oglindă
  [SQL](https://storh.inline0.dev/ro/docs/sql-mirror). Fișierele rămân sursa canonică; oglinda este o proiecție
  reconstruibilă în SQLite sau MySQL.

Motoarele se pot combina: partajează doar directorul rădăcină, astfel încât o aplicație poate
păstra conținutul într-un `DocStore`, istoricul de audit într-un `SegmentedLog` și
sarcinile de fundal într-un `Queue`, sub aceeași rădăcină de stocare.

## DocStore

`DocStore` este potrivit pentru citiri punctuale și scanări simple ale înregistrărilor persistente. Scrie
un fișier JSONC pentru fiecare înregistrare:

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

Fișierele de înregistrare sunt partiționate în funcție de ultimele caractere UUID, astfel încât rafalele de scriere UUIDv7 să se distribuie
între directoare:

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

## SegmentedLog

`SegmentedLog` este potrivit pentru înregistrări adăugate frecvent, citiri cu cursor și citiri pe intervale de timp.
Înregistrările sunt scrise în segmentul activ, câte o linie per înregistrare în formatul
`<byte-length>\t<xxh32>\t<json>` (consultați [Atomicitate](https://storh.inline0.dev/ro/docs/atomicity)). Fișierele segmentelor au extensia `.ndjson`, dar prefixul cu lungimea și suma de control înseamnă că instrumentele NDJSON simple nu le pot citi; folosiți `stream()` sau constructorul de interogări pentru a citi înregistrările. Segmentele complete devin segmente sigilate, cu indexuri sparse.

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

## Queue

`Queue` este destinat cozilor persistente cu debit mare. Operațiile enqueue, claim, complete, requeue și purge scriu evenimente doar pentru adăugare în `queue.log`; starea cozii este reluată în memorie la deschiderea cozii. Metodele pentru loturi distribuie costul blocării și al flush-ului între mai multe sarcini. Jurnalul crește cu fiecare eveniment; `stats()` raportează dimensiunea curentă în octeți.

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

## Persistență și concurență

storh publică scrierile de fișiere prin redenumire atomică în directorul țintă.
La redeschidere și `repair()` elimină fișierele temporare abandonate al căror proces proprietar s-a închis,
și lasă în pace fișierele temporare ale proceselor de scriere active. Marcajele scriitorilor fac verificarea constantă ca timp, astfel încât deschiderea unei stocări parcurge colecția doar după o cădere a procesului.

`SegmentedLog` și `Queue` scriu linii de jurnal protejate prin lungime și sumă de control. La redeschidere
sau reparare, cozile incomplete sunt trunchiate până la ultimul eveniment confirmat, iar starea din memorie este reconstruită din conținutul persistent al jurnalului. Scrierile în jurnal și în coadă sunt serializate cu blocări ale sistemului de fișiere.

Modificările `DocStore` sunt serializate cu o blocare de scriere la nivel de colecție, astfel încât
fișierele de înregistrări și indexurile secundare se actualizează ca o singură unitate de consistență între procese. Scrierile simultane către ID-uri distincte sunt sigure, cu aceleași presupuneri privind redenumirea atomică a sistemului de fișiere. Scrierile simultane către același ID sunt câștigate de ultima redenumire.

storh golește buffer-ele și execută fsync pentru descriptorii de fișier înainte de redenumire sau finalizarea adăugării și încearcă să execute fsync pentru directoarele părinte după redenumiri atomice. Persistența la pierderea alimentării depinde în continuare de respectarea acestor operații de către sistemul de fișiere și opțiunile de montare subiacente.
