Vai al contenuto

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 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 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 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à). 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.