Preskoči na vsebino

SQL zrcalo

SqlMirror prenese zbirke storh v tabele SQLite ali MySQL, da lahko v SQL izvajate združevanja, razvrščanje med tabelami, iskanje podnizov in pripravo poročil, datoteke pa ostanejo kanonične. Predstavljajte si ga kot sekundarni indeks v obliki podatkovne zbirke: zrcalo je izpeljano, ga lahko zavržete in kadar koli znova zgradite. ext-pdo z gonilnikom sqlite ali mysql potrebujete samo, ko ga uporabljate; osnovni paket ostane brez odvisnosti.

Nastavitev

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();

Zrcalite lahko oba pogona: zbirke DocStore in tokove SegmentedLog (prenesejo se samo aktivni zapisi). Posredovanje Schema je izbirno in neodvisno od shrambe: deklarirana polja string/int/float/bool postanejo tipizirani, indeksirani stolpci; vse drugo ostane poizvedljivo v stolpcu dataJSON. Polj mixed ni mogoče označiti za indeksiranje.

Povezave

Konstruktor sprejme ročico PDO (gonilnik sqlite ali mysql, samodejno zaznan), ročico mysqli ali katero koli implementacijo 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_');

Pot mysqli deluje v obeh načinih poročanja o napakah mysqli, zato lahko obstoječo ročico iz gostiteljske aplikacije (na primer $wpdb->dbh v WordPress) ponovno uporabite brez sprememb. Delovanje je pri vseh gonilnikih enako; neuspešni prenosi pri vseh razveljavijo transakcijo zbirke.

Vsaka tabela dobi id (primarni ključ), hash (omogoča postopne prenose), tipizirane stolpce in data (celoten zapis JSON).

Sinhronizacija

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() primerja zgoščene vrednosti zapisov z zrcalom in zapiše vsako zbirko v eni transakciji, zato ob sesutju ali neuspešnem prenosu nikoli ne ostane delno uveljavljen paket. Nespremenjeni zapisi zahtevajo en SELECT in pregled zapisov. Redni push() (na primer ob sprožilcu cron) ohranja usklajenost zrcala; pokličite flush() z ID-ji, ki ste jih zapisali med zahtevo, kadar bralci potrebujejo takojšen dostop do svojih zapisov v zrcalu.

Poizvedovanje

Poizvedujte po zrcalu z navadnim SQL. table() vrne ime zrcaljene tabele:

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'
);

Prenos nazaj v datoteke

pull() prebere vrstice zrcala in jih po ID-ju zapiše v registrirano shrambo. Podpira dva poteka dela:

  • Obnovitev: datotek ni več, zrcalo pa je ohranjeno. Registrirajte novo shrambo pod istim imenom zbirke in izvedite prenos.
  • Začetno polnjenje: vrstice, ki jih v tabelo zrcala naložijo zunanja orodja, postanejo zapisi storh. Vrstice morajo vsebovati veljavne ID-je UUIDv7 in objekt v stolpcu data; tipizirani stolpci se pri prenosu prezrejo, ker je data celoten zapis.
PHP
$restored = new Storh\DocStore($root, 'pages');
$mirror   = new SqlMirror($pdo);
$mirror->collection($restored, 'pages');
$mirror->pull();          // ['written' => n, 'unchanged' => m]

Zapisi, preneseni nazaj, gredo skozi običajne mehanizme shrambe, zato veljajo preverjanje sheme in enolični indeksi, vsak zapis pa se shrani trajno. pull() nikoli ne izbriše lokalnih zapisov, zapisi, katerih podatki se že ujemajo z vrstico zrcala, pa so preskočeni. Po začetnem polnjenju z ročno vstavljenimi vrsticami enkrat zaženite push(): te vrstice prepiše v kanonični obliki, da se njihove zgoščene vrednosti uskladijo.

Pogodba in omejitve

  • Kanonične so datoteke. Zrcalo je izhod samo za branje; vse, kar vanj zapišete ročno, bo prepisal ali izbrisal naslednji push().
  • Enolični stolpci v zrcalu so varovalka, ne preverjanje ob zapisu: podvojena vrednost, ki so jo datoteke sprejele, se pokaže kot neuspešen prenos, ki se v celoti razveljavi. Za preverjanje enoličnosti ob zapisu uporabite enolične indekse storh.
  • Zrcalo za datotekami zaostaja za največ en interval prenosa, razen za ID-je, ki jih izrecno flush().
  • Zrcaljenje ne dodaja transakcij v sam storh. Atomski zapisi nad več zapisi ostajajo naloga prave podatkovne zbirke, ki služi kot sistem evidenc.
  • Spremembe sheme se uveljavijo tako, da se zrcalo izbriše in znova namesti: uninstall(), install(), push().