
# Motores

## Elegir un motor

Los tres motores almacenan lo mismo: un identificador UUIDv7 más una matriz de datos, pero lo organizan de forma distinta en el disco. Esa organización determina para qué sirve mejor cada motor:

|            | DocStore | SegmentedLog | Queue |
| ---------- | -------- | ------------ | ----- |
| En disco   | un archivo JSONC por registro, particionado por los últimos caracteres de UUID | segmentos de solo anexado con líneas JSON precedidas por su longitud y suma de comprobación, más un manifiesto | un registro de eventos de solo anexado |
| Escritura  | reemplazo atómico de archivo por registro | anexados con suma de comprobación; las sobrescrituras y eliminaciones añaden nuevas entradas, recuperadas mediante `compact()` | anexado con suma de comprobación por evento de cola |
| Lectura    | `get()` por ID, consultas indexadas | lecturas con cursor y por intervalo de tiempo, `get()` mediante el índice de estado | `claim()` en orden de incorporación a la cola |
| Ventaja    | lecturas puntuales y búsquedas indexadas con colecciones de cualquier tamaño | flujos con muchos anexados de hasta cerca de 1M registros | despacho persistente de trabajos entre procesos |
| Desventaja | recorridos de campos sin índice en colecciones de más de unos 10k registros | reescrituras frecuentes de registros individuales (las versiones antiguas ocupan espacio hasta la compactación) | no es un almacén general de registros; las cargas útiles se descartan al completarse un trabajo |

`DocStore` y `SegmentedLog` comparten la misma interfaz fluida de [consultas API](https://storh.inline0.dev/es/docs/query-builder): la misma cadena `where()` funciona en ambos motores; solo cambia la ejecución. `DocStore` puede responder mediante sus índices secundarios, mientras que `SegmentedLog` filtra al recorrer los segmentos. `Queue` se basa en reclamaciones y no admite consultas: los procesos de trabajo `claim()` el siguiente trabajo en lugar de filtrarlos.

Reglas prácticas:

- Para registros que obtiene, actualiza o elimina de forma individual y consulta por campo: `DocStore`. Añada [índices](https://storh.inline0.dev/es/docs/indexes) para los campos por los que filtra.
- Para eventos o versiones que principalmente añade y lee en orden o por intervalo de tiempo: `SegmentedLog`.
- Para trabajos que pasan de productores a procesos de trabajo: `Queue`. Las reclamaciones y finalizaciones son seguras en varios procesos, y `claimMany()`/`completeMany()` amortizan el coste de los bloqueos y fsync.
- Para combinar, buscar o generar informes sobre cualquier colección: añada una réplica [SQL](https://storh.inline0.dev/es/docs/sql-mirror). Los archivos siguen siendo la fuente canónica; la réplica es una proyección reconstruible en SQLite o MySQL.

Los motores se pueden combinar: solo comparten el directorio raíz, así que una aplicación puede guardar contenido en un `DocStore`, un registro de auditoría en un `SegmentedLog` y trabajos en segundo plano en una `Queue` bajo la misma raíz de almacenamiento.

## DocStore

`DocStore` sirve para lecturas puntuales y recorridos sencillos de registros persistentes. Escribe un archivo JSONC por registro:

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

Los archivos de registros se particionan según los últimos caracteres de UUID, para distribuir las ráfagas de escritura de UUIDv7 entre los directorios:

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

## SegmentedLog

`SegmentedLog` sirve para registros con muchos anexados, lecturas con cursor y lecturas por intervalo de tiempo. Los registros se escriben en el segmento activo, uno por línea con el formato `<byte-length>\t<xxh32>\t<json>` (consulte [Atomicidad](https://storh.inline0.dev/es/docs/atomicity)). Los archivos de segmento usan la extensión `.ndjson`, pero el prefijo con la longitud y la suma de comprobación impide que las herramientas de NDJSON sin procesar puedan leerlos; lea los registros con `stream()` o con el generador de consultas. Los segmentos completos pasan a ser segmentos sellados con índices dispersos.

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

## Queue

`Queue` está diseñado para colas de trabajo duraderas y de alto rendimiento. Encolar, reclamar, completar, volver a encolar y purgar escriben eventos de solo anexado en `queue.log`; el estado de la cola se reproduce en memoria al abrirla. Los métodos por lotes amortizan el coste de bloqueo y vaciado entre muchos trabajos. El registro crece con cada evento; `stats()` indica su tamaño actual en bytes.

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

## Durabilidad y concurrencia

storh publica las escrituras de archivos mediante un renombrado atómico dentro del directorio de destino. Al reabrir y con `repair()` se eliminan los archivos temporales abandonados cuyo proceso propietario ya no está activo, sin tocar los archivos temporales de escritores activos. Las marcas de escritor permiten comprobarlo en tiempo constante, por lo que al abrir un almacén solo se recorren los archivos tras un fallo.

`SegmentedLog` y `Queue` escriben líneas de registro protegidas por longitud y suma de comprobación. Al reabrir o reparar, se truncan los finales incompletos hasta el último evento confirmado y se reconstruye el estado en memoria a partir del contenido persistente del registro. Las escrituras del registro y la cola se serializan mediante bloqueos del sistema de archivos.

Las mutaciones de `DocStore` se serializan con un bloqueo de escritura a nivel de colección, de modo que los archivos de registros y los índices secundarios se actualizan como una única unidad de coherencia entre procesos. Las escrituras simultáneas en distintos identificadores de registro son seguras bajo los mismos supuestos de renombrado atómico del sistema de archivos. Si se escribe simultáneamente en el mismo identificador, prevalece el último renombrado.

storh vacía los búferes y sincroniza los descriptores de archivo con fsync antes de completar un renombrado o un añadido, e intenta sincronizar con fsync los directorios principales después de los renombrados atómicos. La durabilidad ante un corte de energía aún depende de que el sistema de archivos y las opciones de montaje respeten esas sincronizaciones.
