Pular para o conteúdo

Mecanismos

Escolhendo um mecanismo

Os três mecanismos armazenam a mesma coisa: um ID UUIDv7 mais um array de dados, mas organizam esses dados de formas diferentes no disco. Essa organização determina para que cada mecanismo é mais indicado:

DocStore SegmentedLog Queue
No disco um arquivo JSONC por registro, fragmentado por cauda UUID segmentos somente de acréscimo com linhas JSON prefixadas pelo comprimento e pela soma de verificação, mais um manifesto um log de eventos somente de acréscimo
Gravação substituição atômica de arquivo por registro acréscimos com soma de verificação; sobrescritas e exclusões acrescentam novas entradas, recuperadas por compact() acréscimo com soma de verificação por evento da fila
Leitura get() por ID, consultas indexadas leituras com cursor e por intervalo de tempo, get() pelo índice de estado claim() na ordem de enfileiramento
Ideal para leituras pontuais e buscas indexadas em coleções de qualquer tamanho fluxos com muitos acréscimos, até cerca de 1M de registros distribuição durável de trabalho entre processos
Limitação varreduras de campos sem índice acima de cerca de 10k registros regravações frequentes de um único registro (versões antigas ocupam espaço até a compactação) não é um armazenamento geral de registros; os payloads são descartados quando uma tarefa é concluída

DocStore e SegmentedLog compartilham a mesma consulta fluente API: a mesma sequência where() funciona nos dois mecanismos, e apenas a execução muda. DocStore pode responder usando seus índices secundários, enquanto SegmentedLog filtra durante a leitura dos segmentos. Queue usa reivindicações, não consultas: os trabalhadores claim() o próximo trabalho em vez de filtrar tarefas.

Regras práticas:

  • Registros que você busca, atualiza ou exclui individualmente e consulta por campo: DocStore. Adicione índices aos campos usados nos filtros.
  • Eventos ou versões que você geralmente apenas acrescenta e lê de volta em ordem ou por intervalo de tempo: SegmentedLog.
  • Tarefas encaminhadas de produtores para processos de trabalho: Queue. As reivindicações e conclusões são seguras para vários processos, e claimMany()/completeMany() amortizam o custo de bloqueio e fsync.
  • Junções, pesquisa ou relatórios sobre qualquer coleção: adicione um SQLespelho. Os arquivos permanecem canônicos; o espelho é uma projeção reconstruível em SQLite ou MySQL.

Os mecanismos se combinam: eles compartilham apenas o diretório raiz, então um aplicativo pode manter conteúdo em um DocStore, um registro de auditoria em um SegmentedLog e tarefas em segundo plano em um Queue sob a mesma raiz de armazenamento.

DocStore

DocStore serve para leituras pontuais e varreduras simples de registros duráveis. Ele grava um arquivo JSONC por registro:

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

Os arquivos de registro são distribuídos em fragmentos pelas UUID últimas letras, para que os picos de gravação de UUIDv7 se espalhem entre diretórios:

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

SegmentedLog

SegmentedLog serve para registros com muitas inserções, leituras por cursor e leituras por intervalo de tempo. Os registros são gravados no segmento ativo, uma linha por registro no formato <byte-length>\t<xxh32>\t<json> (consulte Atomicidade). Os arquivos de segmento usam a extensão .ndjson, mas o prefixo de tamanho e checksum impede que ferramentas NDJSON comuns os leiam; leia os registros usando stream() ou com o construtor de consultas. Segmentos completos são convertidos em segmentos selados com índices esparsos.

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

Fila

Queue serve para filas de trabalho duráveis de alta vazão. Enfileirar, reivindicar, concluir, reenfileirar e limpar grava eventos somente de acréscimo em queue.log; o estado da fila é reproduzido na memória quando ela é aberta. Os métodos em lote amortizam os custos de bloqueio e flush entre várias tarefas. O log cresce a cada evento; stats() informa seu tamanho atual em bytes.

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

Durabilidade e concorrência

storh publica gravações de arquivos usando renomeação atômica no diretório de destino. Reabrir e repair() removem arquivos temporários abandonados cujo processo proprietário já terminou, enquanto preservam os arquivos temporários de processos de gravação ativos. Os marcadores de gravação tornam essa verificação constante, então a abertura do armazenamento percorre a coleção somente após uma falha.

SegmentedLog e Queue gravam linhas de log protegidas por tamanho e checksum. Reabrir ou reparar trunca as partes finais incompletas até o último evento confirmado e reconstrói o estado em memória com base no conteúdo durável do log. As gravações no log e na fila são serializadas com bloqueios do sistema de arquivos.

As alterações de DocStore são serializadas com um bloqueio de gravação no nível da coleção, de modo que os arquivos de registro e os índices secundários sejam atualizados como um limite de consistência entre processos. Gravações simultâneas em IDs de registro diferentes são seguras sob as mesmas premissas de renomeação atômica do sistema de arquivos. Gravações simultâneas no mesmo ID seguem a regra de que vence a última renomeação.

storh descarrega e sincroniza (fsync) os manipuladores de arquivos antes da renomeação ou da conclusão de um acréscimo e tenta sincronizar (fsync) os diretórios pai após renomeações atômicas. A durabilidade em caso de queda de energia ainda depende de o sistema de arquivos subjacente e as opções de montagem respeitarem essas sincronizações.