Storh
Record prima su file per PHP: documenti JSONC, log segmentati in sola aggiunta e code basate su log con id UUIDv7.
composer require storh/storhUltima versione v0.5.2 MIT PHP 8.2+
$pages = $docs
->query()
->where('kind')->eq('page')
->orderBy('title')
->limit(20)
->get();Nella pratica
La guida rapida in due parti: un documento nella directory radice di archiviazione, poi una voce di log e un job in coda accanto.
Una radice, tre motori
StorageRoot::resolve() normalizza una directory di base e uno spazio dei nomi in una radice; al primo utilizzo, ogni motore crea al suo interno la propria directory.
Ogni motore restituisce oggetti StorageRecord con id() e data(). La coda aggiunge ogni presa in carico e completamento al proprio log degli eventi.
use Storh\DocStore;
use Storh\Queue;
use Storh\RecordQuery;
use Storh\SegmentedLog;
use Storh\StorageRoot;
$root = StorageRoot::resolve(__DIR__ . '/var/storh', 'demo');
$docs = new DocStore($root, 'pages');
$home = $docs->put([
'kind' => 'page',
'title' => 'Home',
]);
$loaded = $docs->get($home->id());$log = new SegmentedLog($root, 'events');
$log->put([
'type' => 'page.saved',
'pageId' => $home->id(),
]);
foreach ($log->stream(RecordQuery::all()->after($home->id())->limit(50)) as $record) {
// Process records lazily.
}
$queue = new Queue($root, 'jobs');
$jobId = $queue->enqueue(['task' => 'render', 'pageId' => $home->id()]);
$job = $queue->claim();
if (null !== $job) {
$queue->complete($job->id());
}Archivio documenti JSONC
Un file JSONC per record, id UUIDv7, sharding con due caratteri del gruppo finale di UUID, sostituzione atomica e filtri di campo in streaming.
Log segmentato
Segmenti in sola aggiunta di righe JSON precedute da lunghezza e checksum, indici sparsi, letture tramite cursore e per intervallo temporale, recupero dopo un arresto anomalo e compattazione.
Coda basata su log
I job passano dagli stati in attesa, in elaborazione e completato tramite eventi di coda compatti e in sola aggiunta.
Record UUIDv7
Id ordinabili e individuabili nel tempo per cursori stabili, scansioni con limiti e percorsi di archiviazione adatti allo sharding.
Scritture atomiche
Le scritture su file temporanei e le ridenominazioni atomiche mantengono integri i record. Dopo l'arresto anomalo di un writer, alla successiva apertura vengono eliminati i file temporanei orfani.
Recupero da scritture parziali
Le letture dei segmenti convalidano i campi di lunghezza e checksum; il recupero tronca le righe finali incomplete.
Prima su file
Nessun server di database, nessun adattatore per framework, nessun pacchetto di runtime Composer; basta passare una directory.
Letture JSONC
I file dei record possono contenere commenti e virgole finali. storh scrive JSON compatti su una sola riga, quindi i commenti restano fino alla scrittura successiva.
Generatore di query
Query fluide come where('status')->in([...]) su DocStore e SegmentedLog; DocStore usa gli indici secondari quando disponibili.
Indici secondari
Indici di uguaglianza, univoci e per intervallo, archiviati come file accanto alla raccolta, derivati dai record e ricostruibili in qualsiasi momento.
Schemi
Gli schemi facoltativi delle raccolte convalidano ogni scrittura e dichiarano quali campi vengono indicizzati.
SQL Mirror
Invia le raccolte a SQLite o MySQL per join, ricerca e report, mantenendo canonici i file. Richiede ext-pdo o ext-mysqli solo quando viene usato.
Perché Storh
Una directory, non un server
Passate una directory di base e storh vi scrive. Richiede PHP 8.2 ed ext-json, senza pacchetti di runtime Composer né adattatori per framework.
Una forma di record, tre strutture
Documenti, log e code archiviano tutti un id UUIDv7 più un array di dati. Scegliete il layout su disco in base al carico di lavoro e mantenete tutti e tre sotto un'unica radice.
Controlli eseguibili
verify() confronta indici e stato del log con una scansione completa dei file. repair() mette in quarantena i record corrotti e tronca le code dei log incomplete; CLI stampa JSON per i job cron e i controlli di stato.
Benchmark con configurazione documentata
README pubblica le mediane per i record 50,000 di ciascun motore su un Apple M1 Pro con PHP 8.5, ad esempio 5.6 µs per lettura puntuale. Ogni scrittura in quelle esecuzioni usa fsync, quindi le velocità di scrittura dipendono dal filesystem.
A confronto con
storh è un sistema di archiviazione basato prima sui file, non un motore di query relazionali, quindi questi strumenti svolgono alcune delle operazioni che non offre.
| Strumento | Ideale per | Differenze rispetto a Storh |
|---|---|---|
| SQLite | Ideale perUn motore SQL autonomo in un singolo file, con transazioni ACID serializzabili che resistono all'arresto anomalo di un programma o a un'interruzione di corrente. La documentazione lo presenta come sostituto dei file di dati improvvisati. | Differenze rispetto a Storhstorh mantiene un file JSON per record e il suo nucleo non richiede un driver PDO. Non supporta transazioni su più record e la documentazione consiglia di affidare quel lavoro a un vero database. Può inviare raccolte a SQLite per join e report, mantenendo canonici i file. |
| MySQL | Ideale perUn database relazionale gestito da un server SQL multithread, con programmi client e librerie, strumenti di amministrazione e un'ampia gamma di API. | Differenze rispetto a Storhstorh viene eseguito all'interno del processo PHP su una directory, quindi non c'è alcun server da gestire. Per join, ricerca e report, effettua il mirroring delle raccolte in MySQL, anche tramite un handle mysqli esistente; i file restano la fonte di verità. |
| SleekDB | Ideale perUn database documentale a file in PHP senza dipendenze di terze parti, che archivia dati senza schema come normali file JSON. Il suo API di query include join, ricerca testuale, group by e having, e filtri su proprietà annidate. | Differenze rispetto a Storhstorh abbina il proprio archivio documentale a un log segmentato in sola aggiunta e a una coda basata su log sotto un'unica radice, e usa fsync per ogni scrittura prima di confermarne l'archiviazione. I join non fanno parte di storh; per eseguirli, invia le raccolte a SQLite o MySQL. |
Domande e risposte
Risposte brevi dalla documentazione di Storh.
Sì. DocStore serializza le modifiche con un blocco di scrittura a livello di raccolta; in caso di scritture simultanee sullo stesso id prevale l'ultima rinominazione. La presa in carico dei job in coda mantiene il blocco della coda, quindi due worker non possono prendere lo stesso job in attesa.
Sì. Le letture accettano commenti e virgole finali, ma la successiva put() per quell'id riscrive il file in formato JSON compatto e li rimuove. In modalità cache TRUST, le modifiche ai file apportate al di fuori di storh non vengono rilevate.
Le letture puntuali per id rimangono veloci finché il filesystem è in grado di gestire il numero di file. Le scansioni dei campi DocStore senza indice rallentano oltre circa 10k record, quindi dichiarate indici per i campi su cui filtrate. Le scansioni del log segmentato e le letture per intervallo sono gestibili fino a circa 1M di record.
Le scritture usano fsync e vengono pubblicate con una rinominazione atomica; alla riapertura, un log o una coda tronca le code incomplete fino all'ultimo evento confermato. La durabilità in caso di interruzione di corrente dipende comunque dal supporto del filesystem e dalle opzioni di montaggio per queste sincronizzazioni.
No. Inviate le raccolte a SQLite o MySQL con SqlMirror per join, ricerca e report. Le scritture atomiche su più record richiedono comunque un vero database come sistema di registrazione.
No. Queue non è un archivio generico di record e il payload di un job viene eliminato una volta completato.
Iniziate con la guida rapida, poi consultate Scelta del motore per abbinare DocStore, SegmentedLog o Queue al vostro carico di lavoro.