Pular para o conteúdo

Espelhamento de SQL

SqlMirror envia coleções de storh para tabelas SQLite ou MySQL para que você possa fazer junções, ordenação entre tabelas, pesquisa de substrings e relatórios em SQL, enquanto os arquivos permanecem canônicos. Pense nisso como um índice secundário que, por acaso, é um banco de dados: o espelho é derivado, descartável e pode ser reconstruído a qualquer momento. ext-pdo com o driver sqlite ou mysql é necessário somente quando você o utiliza; o pacote principal não tem dependências.

Configuração

PHP
use Storh\DocStore;
use Storh\Schema;
use Storh\SqlMirror;

$schema = Schema::collection('pages')
    ->string('slug')->unique()
    ->string('kind')->index()
    ->int('publishedAt')->range();

$pages  = new DocStore($root, 'pages', schema: $schema);
$events = new Storh\SegmentedLog($root, 'events');

$mirror = new SqlMirror(new PDO('sqlite:' . $root . '/mirror.db'), 'app_');
$mirror->collection($pages, 'pages', $schema);
$mirror->collection($events, 'events');

$mirror->install();
$mirror->push();

É possível espelhar ambos os mecanismos: coleções DocStore e fluxos SegmentedLog (somente registros ativos são enviados). Informar um Schema é opcional e independente do armazenamento: os campos string/int/float/bool declarados tornam-se colunas tipadas e indexadas; todo o resto continua consultável na coluna data JSON. Campos mixed não podem ser marcados para indexação.

Conexões

O construtor aceita um identificador de PDO (driver sqlite ou mysql, detectado automaticamente), um identificador de mysqli ou qualquer implementação de SqlMirrorConnection:

PHP
$mirror = new SqlMirror(new PDO('sqlite:' . $root . '/mirror.db'));
$mirror = new SqlMirror(new PDO('mysql:host=127.0.0.1;dbname=app;charset=utf8mb4', $user, $password));
$mirror = new SqlMirror(new mysqli('127.0.0.1', $user, $password, 'app'), 'wp_storh_');

O caminho mysqli funciona com qualquer um dos modos de relatório de erros do mysqli, então um identificador existente de um aplicativo hospedeiro (por exemplo, o $wpdb->dbh de WordPress) pode ser reutilizado sem alterações. O comportamento é idêntico em todos os drivers; falhas no envio revertem a transação da coleção em todos eles.

Cada tabela recebe id (chave primária), hash (usado nos envios incrementais), as colunas tipadas e data (o JSON completo do registro).

Sincronização

PHP
$mirror->push();                    // reconcile: insert, update, delete as needed
$mirror->push('pages');             // reconcile a single collection
$mirror->flush('pages', $ids);      // push or remove just these ids
$mirror->pull();                    // write mirror rows back into the store
$mirror->verify();                  // report missing, stale, and orphaned rows
$mirror->rebuild();                 // clear all rows and push from scratch
$mirror->uninstall();               // drop the mirror tables

push() compara os hashes dos registros com o espelho e grava cada coleção em uma única transação, de modo que uma falha ou interrupção nunca deixe um lote aplicado pela metade. Registros inalterados custam um SELECT mais uma varredura dos registros. Um push() periódico (por exemplo, em um ciclo de cron) mantém o espelho sincronizado; chame flush() com os ids que você gravou durante uma solicitação quando os leitores precisarem ver as próprias gravações no espelho.

Consultas

Consulte o espelho com SQL puro. table() retorna o nome da tabela espelhada:

PHP
$pdo->query(
    'SELECT p.slug, COUNT(*) AS sales
     FROM ' . $mirror->table('purchases') . ' o
     INNER JOIN ' . $mirror->table('products') . ' p ON p.id = o.product_id
     GROUP BY p.slug
     ORDER BY sales DESC'
);

Recuperação dos arquivos

pull() lê as linhas do espelho e as grava no armazenamento registrado, em ordem de id. Ele cobre dois fluxos:

  • Restauração: os arquivos desapareceram, mas o espelho sobreviveu. Registre um armazenamento novo com o mesmo nome de coleção e faça a recuperação.
  • Inicialização: linhas carregadas na tabela espelhada por ferramentas externas tornam-se registros storh. As linhas devem conter ids UUIDv7 válidos e um objeto na coluna data; as colunas tipadas são ignoradas na recuperação porque data é o registro completo.
PHP
$restored = new Storh\DocStore($root, 'pages');
$mirror   = new SqlMirror($pdo);
$mirror->collection($restored, 'pages');
$mirror->pull();          // ['written' => n, 'unchanged' => m]

As gravações recuperadas passam pelo fluxo normal do armazenamento, portanto a validação de esquema e os índices exclusivos são aplicados, e cada registro é gravado de forma durável. pull() nunca exclui registros locais, e os registros cujos dados já correspondem à linha do espelho são ignorados. Após inicializar com linhas inseridas manualmente, execute push() uma vez: ele regrava essas linhas no formato canônico para que os hashes sejam reconciliados.

Contrato e limites

  • Os arquivos são canônicos. O espelho é uma saída somente de leitura; qualquer conteúdo gravado manualmente nele será substituído ou excluído no próximo push().
  • Colunas exclusivas no espelho servem como proteção adicional, não como validação no momento da gravação: uma duplicata permitida pelos arquivos resulta em um envio com falha (e reversão completa). Use índices exclusivos de storh para validar no momento da gravação.
  • O espelho pode ficar até um intervalo de envio atrasado em relação aos arquivos, exceto para os ids que você flush() explicitamente.
  • O espelhamento não adiciona transações ao próprio storh. Gravações atômicas de vários registros continuam sendo tarefa de um banco de dados real como sistema de registro.
  • As alterações de esquema são aplicadas removendo e reinstalando o espelho: uninstall(), install(), push().