Дзеркало SQL
SqlMirror передає колекції storh до таблиць у SQLite або MySQL, щоб ви могли
виконувати з’єднання, сортування між таблицями, пошук підрядків і створювати звіти в SQL, поки
файли залишаються канонічними. Це можна уявити як вторинний індекс, який водночас є базою даних: дзеркало похідне, його можна видалити й у будь-який час відновити.
ext-pdo із драйвером sqlite або mysql потрібен лише під час використання дзеркала; основний пакет не має залежностей.
Налаштування
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();
Можна створювати дзеркала для обох рушіїв: колекцій DocStore і
потоків SegmentedLog (передаються лише активні записи). Параметр Schema необов’язковий
і не залежить від сховища: оголошені поля string/int/float/bool стають типізованими стовпцями з індексами; до всіх інших можна й далі виконувати запити у стовпці data JSON. Для полів mixed не можна вказати прапорець індексації.
Підключення
Конструктор приймає дескриптор PDO (драйвер sqlite або mysql визначається автоматично), дескриптор mysqli або будь-яку реалізацію 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_');
Шлях mysqli працює в обох режимах повідомлення про помилки mysqli, тож можна повторно використати наявний дескриптор із хост-застосунку (наприклад, WordPress: $wpdb->dbh) без змін. Поведінка однакова в усіх драйверах; у разі помилки передавання транзакція колекції відкочується в кожному з них.
Кожна таблиця містить id (первинний ключ), hash (використовується для інкрементального передавання), типізовані стовпці та data (повний запис у форматі JSON).
Синхронізація
$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() порівнює хеші записів із дзеркалом і записує кожну колекцію в межах однієї транзакції, тож збій або аварійне завершення передавання не залишає частково застосованого пакета. Для незмінених записів потрібен один SELECT і сканування записів. Періодичний виклик push() (наприклад, під час циклу cron) підтримує синхронність дзеркала; викликайте flush() з ідентифікаторами, які ви записали під час запиту, коли читачам потрібен доступ до щойно записаних даних у дзеркалі.
Запити
Виконуйте запити до дзеркала за допомогою звичайного SQL. table() повертає назву дзеркальної таблиці:
$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'
);
Повернення даних у файли
pull() читає рядки дзеркала й записує їх у зареєстроване сховище в порядку ідентифікаторів. Функція підтримує два сценарії:
- Відновлення: файли зникли, але дзеркало збереглося. Зареєструйте нове сховище під тією самою назвою колекції та виконайте повернення.
- Початкове заповнення: рядки, завантажені до таблиці дзеркала за допомогою зовнішніх інструментів, стають записами storh. Рядки мають містити коректні ідентифікатори UUIDv7 та об’єкт у стовпці
data; типізовані стовпці під час повернення ігноруються, оскількиdataмістить увесь запис.
$restored = new Storh\DocStore($root, 'pages');
$mirror = new SqlMirror($pdo);
$mirror->collection($restored, 'pages');
$mirror->pull(); // ['written' => n, 'unchanged' => m]
Дані, повернуті у сховище, проходять через звичайні механізми запису, тож застосовуються перевірка схеми та унікальні індекси, а кожен запис зберігається довговічно. pull() ніколи не видаляє локальні записи й пропускає записи, дані яких уже збігаються з рядком дзеркала. Після додавання рядків вручну виконайте push() один раз: це перезапише їх у канонічному форматі, щоб їхні хеші синхронізувалися.
Умови та обмеження
- Файли мають канонічний формат. Дзеркало — це вивід лише для читання; усе, що записано в нього вручну, буде перезаписано або видалено під час наступного
push(). - Унікальні стовпці дзеркала слугують додатковим захистом, а не перевіркою під час запису: дублікат, дозволений у файлах, призведе до помилки передавання з повним відкотом змін. Для перевірки під час запису використовуйте унікальні індекси storh.
- Дзеркало може відставати від файлів на один інтервал між передаваннями, крім ідентифікаторів, які ви явно передаєте через
flush(). - Дзеркалювання не додає транзакцій до самого storh. Атомарний запис кількох записів і далі потребує повноцінної бази даних як основного сховища.
- Зміни схеми застосовуються видаленням і повторним встановленням дзеркала:
uninstall(),install(),push().