SQL Mirror
SqlMirror pusht storh-collecties naar tabellen in SQLite of MySQL, zodat u joins, sortering tussen tabellen, zoeken in subreeksen en rapportages kunt uitvoeren in SQL, terwijl de bestanden de canonieke bron blijven. Zie het als een secundaire index die toevallig een database is: de mirror is afgeleid, wegwerpbaar en op elk moment opnieuw op te bouwen. ext-pdo met de driver sqlite of mysql is alleen nodig wanneer u de mirror gebruikt; het kernpakket blijft vrij van afhankelijkheden.
Instellen
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();
Beide engines kunnen worden gespiegeld: DocStore-collecties en SegmentedLog-streams (alleen actieve records worden gepusht). Een Schema meegeven is optioneel en staat los van de store: gedeclareerde velden van het type string/int/float/bool worden getypeerde, geïndexeerde kolommen; al het andere blijft doorzoekbaar in de kolom data JSON. Velden van het type mixed kunnen niet worden gemarkeerd voor indexering.
Verbindingen
De constructor accepteert een PDO-handle (driver sqlite of mysql, automatisch gedetecteerd), een mysqli-handle of elke implementatie van SqlMirrorConnection:
$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_');
Het mysqli-pad werkt met beide foutmeldingsmodi van mysqli, dus een bestaande handle van een hosttoepassing (bijvoorbeeld $wpdb->dbh van WordPress) kan zo worden hergebruikt. Het gedrag is voor alle drivers hetzelfde; mislukte pushbewerkingen rollen de transactie van de collectie bij elke driver terug.
Elke tabel krijgt id (primaire sleutel), hash (stuurt incrementele pushes aan), de getypeerde kolommen en data (de volledige record-JSON).
Synchroniseren
$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() vergelijkt hashes van records met de mirror en schrijft elke collectie in één transactie, zodat een gecrashte of mislukte push nooit een gedeeltelijk toegepaste batch achterlaat. Voor ongewijzigde records is één SELECT nodig, plus een scan van de records. Een periodieke push() (bijvoorbeeld vanuit een cron-tick) houdt de mirror synchroon; roep flush() aan met de ID's die u tijdens een request hebt geschreven wanneer lezers de zojuist geschreven gegevens meteen in de mirror moeten kunnen lezen.
Query's uitvoeren
Voer query's op de mirror uit met gewone SQL. table() retourneert de naam van de gespiegelde tabel:
$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'
);
Terugzetten naar bestanden
pull() leest rijen uit de mirror en schrijft ze in ID-volgorde naar de geregistreerde store. Dit ondersteunt twee scenario's:
- Herstellen: de bestanden zijn weg, maar de mirror is er nog. Registreer een lege store onder dezelfde collectienaam en haal de gegevens terug.
- Gegevens initialiseren: rijen die met externe tools in de mirrortabel zijn geladen, worden storh-records. De rijen moeten geldige UUIDv7-ID's en een object in de kolom
databevatten; getypeerde kolommen worden bij het terugzetten genegeerd, omdatdatade volledige record is.
$restored = new Storh\DocStore($root, 'pages');
$mirror = new SqlMirror($pdo);
$mirror->collection($restored, 'pages');
$mirror->pull(); // ['written' => n, 'unchanged' => m]
Teruggezette schrijfbewerkingen lopen via de normale storeverwerking. Daardoor zijn schemavalidatie en unieke indexen van toepassing en wordt elk record duurzaam opgeslagen. pull() verwijdert nooit lokale records en slaat records over waarvan de gegevens al overeenkomen met de rij in de mirror. Voer na het initialiseren met handmatig ingevoegde rijen één keer push() uit: die herschrijft de rijen in canonieke vorm, zodat de hashes overeenkomen.
Afspraken en beperkingen
- Bestanden zijn canoniek. De mirror is alleen-lezen uitvoer; alles wat er handmatig in wordt geschreven, wordt overschreven of verwijderd door de volgende
push(). - Unieke kolommen in de mirror zijn een vangnet, geen controle tijdens het schrijven: een duplicaat dat in de bestanden was toegestaan, leidt tot een mislukte push die volledig wordt teruggedraaid. Gebruik unieke indexen in storh om duplicaten tijdens het schrijven te voorkomen.
- De mirror loopt maximaal één pushinterval achter op de bestanden, behalve voor ID's die u expliciet
flush(). - Mirroring voegt zelf geen transacties toe aan storh. Atomair schrijven van meerdere records blijft een taak voor een echte database die als bronsysteem dient.
- Schemawijzigingen worden toegepast door de mirror te verwijderen en opnieuw te installeren:
uninstall(),install(),push().