Как стать автором
Обновить

Синхронные клоны или простые распределенные транзакции (PHP)

Время на прочтение2 мин
Количество просмотров726
Я долго пытался выразить словами зачем мне всё это нужно но потом отказался от этой идеи. Кому интересно — отвечу в комментариях. Итак суть:

Существует несколько web-сайтов с похожей базой данных, с похожим функционалом (допустим магазины, продающие одни и те же товары (один владелец).

Нужно: добавить новый товар на все магазины одновременно. Или не добавлять никуда в случае ошибки хотя бы на одном. Если уж совсем по-простому, то ID записей в определенных таблицах должны совпадать на всех сайтах. Например product_id. Опять же скажу — я упростил задачу, на деле все намного сложнее.


Допустим, на каждом сайте существует класс Product и соответствующий метод

class Product
{
...
public static function createNewProduct($productName)
{
if ( self::exists($productName) ) {
throw new Exception('product exists in database');
//выбрасывание исключения добавлено неслучайно
// а для того, чтобы показать что «иногда что-то может пойти не так»
}

$db->execute(
'INSERT INTO products
SET name={$productName}');
$newId = $db->lastInsertId;

return new self($newId);
}
...
}


То есть на каждом сайте новая запись создается так:

<?php
$product = Product::createNewProduct('new product name');


и нам нужно выполнить этот код на всех сайтах-клонах, учитывая, что на одном из них возможна исключительная ситуация, в случае которой запись не должна быть добавлена ни на один из сайтов.

Например:

beginTransaction();

try {
$product = Product::crateateNewProduct('new product name);
commitTransaction();
} catch (Exception $e) {
rollbackTransaction();

throw new Exception('clone-wide code execution failure');
}


Вот так вот это и должно выглядеть! Проблема сходиться к тому, что:

  1. точно такой же код нужно выполнить одновременно на всех сайтах-клонах;
  2. функция commitTransaction() не должна сделать «настоящий» commit до тех пор, пока все сайты-клоны не «дадут добро»
  3. при попадании в функцию rollbackTransaction() хотя бы на одном из клонов, на всех клонах должно сработать исключение, для того, чтобы все сайты «откатились», даже если у них не было ошибок.


Для этого:

  1. сделаем так, чтобы функции для поддержки «распределенных транзакций» извещали о состоянии транзакции. Например — файл со строкой BEGIN/READY/COMMITED/ROLLBACK доступный извне, например example1.com/transaction/status.txt
  2. сделаем так, чтобы функция commitTransaction() прежде чем подтвердить MySQL транзакцию «пробежалась» по всем известным сайтам-клонам и проверила, «все ли в порядке» и лишь в этом случае выполнила COMMIT. В противном случае — выбросила бы исключение;
  3. сделаем так, чтобы функция rollbackTransaction() также сохраняла статус в файл status.txt, для того чтобы другие сайты-клоны могли узнать об этом и «откатиться»


Картинка иллюстрирует это:

image

Подобным образом можно выполнять любой код, например изменение статуса продукта или редактирование карточки. Нужно лишь найти способ передавать код 4code, который нужно выполнить «синхронно на всех клонах» и вызывать eval($code).

Осталась ещё небольшая проблема — как сделать так, чтобы код выполнился [более-менее] одновременно на всех сайтах. Я решил это при помощи nohup, но, думаю, есть и другие способы.

Хотелось бы обсудить всё это с хабрачеловеками.
Теги:
Хабы:
Всего голосов 8: ↑8 и ↓0+8
Комментарии9

Публикации