Aller au contenu

Moteurs

Choisir un moteur

Les trois moteurs stockent les mêmes éléments, un identifiant UUIDv7 et un tableau de données, mais les organisent différemment sur le disque. Cette organisation détermine les usages adaptés à chaque moteur :

DocStore SegmentedLog Queue
Sur le disque un fichier JSONC par enregistrement, partitionné par suffixe UUID segments en ajout uniquement, avec des lignes JSON précédées de leur longueur et somme de contrôle, plus un manifeste un journal d'événements en ajout uniquement
Écriture remplacement atomique du fichier pour chaque enregistrement ajouts avec somme de contrôle ; les modifications et suppressions ajoutent de nouvelles entrées, l'espace étant récupéré par compact() ajout avec somme de contrôle pour chaque événement de file
Lecture get() par identifiant, requêtes indexées balayages par curseur et par plage temporelle, get() avec l'index d'état claim() dans l'ordre de mise en file
Idéal pour lectures ponctuelles et recherches indexées, quelle que soit la taille de la collection flux à nombreux ajouts, jusqu'à environ 1M d'enregistrements distribution durable de tâches entre processus
Limite balayages de champs non indexés au-delà d'environ 10k enregistrements réécritures fréquentes d'un seul enregistrement (les anciennes versions occupent de l'espace jusqu'au compactage) ce n'est pas un magasin général d'enregistrements ; les données utiles sont supprimées une fois la tâche terminée

DocStore et SegmentedLog partagent la même interface fluide de requêtes API : la même chaîne where() s'exécute sur les deux moteurs ; seule l'exécution diffère. DocStore peut répondre à partir de ses index secondaires, tandis que SegmentedLog filtre pendant le balayage des segments. Queue repose sur des revendications, sans possibilité d'effectuer des requêtes : les processus de travail claim() le travail suivant au lieu de filtrer les tâches.

Conseils pratiques :

  • Enregistrements récupérés, modifiés ou supprimés individuellement et recherchés par champ : DocStore. Ajoutez des index pour les champs utilisés dans vos filtres.
  • Événements ou versions ajoutés principalement à la suite, puis relus dans l'ordre ou par plage temporelle : SegmentedLog.
  • Tâches transmises des producteurs aux processus de travail : Queue. Les revendications et les finalisations sont sûres pour plusieurs processus, et claimMany()/completeMany() répartissent le coût des verrouillages et des fsync.
  • Jointures, recherches ou rapports sur une collection : ajoutez un SQL miroir. Les fichiers restent canoniques ; le miroir est une projection reconstructible dans SQLite ou MySQL.

Les moteurs peuvent être combinés : ils ne partagent que le répertoire racine. Une application peut donc conserver le contenu dans un DocStore, une piste d'audit dans un SegmentedLog et les tâches d'arrière-plan dans un Queue sous la même racine de stockage.

DocStore

DocStore convient aux lectures ponctuelles et aux balayages simples d'enregistrements durables. Il écrit un fichier JSONC par enregistrement :

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

Les fichiers d'enregistrement sont répartis dans des sous-répertoires selon les derniers caractères UUID, afin que les pics d'écriture UUIDv7 se répartissent entre plusieurs répertoires :

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

SegmentedLog

SegmentedLog convient aux enregistrements avec de nombreux ajouts, aux lectures par curseur et aux lectures par plage temporelle. Les enregistrements sont écrits dans le segment actif, à raison d'une ligne par enregistrement au format <byte-length>\t<xxh32>\t<json> (voir Atomicité). Les fichiers de segment ont l'extension .ndjson, mais le préfixe de longueur et de somme de contrôle empêche les outils NDJSON classiques de les lire ; utilisez stream() ou le générateur de requêtes pour relire les enregistrements. Les segments complets deviennent des segments scellés dotés d'index clairsemés.

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

Queue

Queue est conçu pour les files de travaux durables à haut débit. Les opérations d’ajout, de prise en charge, de validation, de remise en file et de purge écrivent des événements en ajout seul dans queue.log ; l’état de la file est rejoué en mémoire à son ouverture. Les méthodes par lots répartissent le coût des verrous et des vidages sur de nombreux travaux. Le journal grossit à chaque événement ; stats() indique sa taille actuelle en octets.

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

Durabilité et concurrence

storh publie les écritures de fichiers par renommage atomique dans le répertoire cible. À la réouverture, repair() supprime les fichiers temporaires abandonnés dont le processus propriétaire n’est plus actif, tout en laissant intacts les fichiers temporaires des processus d’écriture actifs. Les marqueurs d’écriture rendent cette vérification instantanée ; ainsi, le nettoyage de toute la collection à l’ouverture du magasin n’a lieu qu’après un plantage.

SegmentedLog et Queue écrivent des lignes de journal protégées par une longueur et une somme de contrôle. La réouverture ou la réparation tronque les fins incomplètes jusqu’au dernier événement validé et reconstruit l’état en mémoire à partir du contenu durable du journal. Les écritures dans le journal et la file sont sérialisées à l’aide de verrous du système de fichiers.

Les mutations de DocStore sont sérialisées par un verrou en écriture au niveau de la collection ; les fichiers des enregistrements et les index secondaires sont donc mis à jour dans une même limite de cohérence entre les processus. Les écritures simultanées sur des identifiants d’enregistrement distincts sont sûres si le système de fichiers respecte les mêmes hypothèses de renommage atomique. Pour les écritures simultanées sur un même identifiant, le dernier renommage l’emporte.

storh vide les tampons et synchronise les descripteurs de fichiers avant la fin d’un renommage ou d’un ajout, et tente de synchroniser les répertoires parents après les renommages atomiques. La durabilité en cas de coupure de courant dépend toujours du respect de ces synchronisations par le système de fichiers et les options de montage sous-jacents.