ACID транзакции по нескольким технологиям - PullRequest
0 голосов
/ 12 марта 2020

У меня есть приложение, которое использует локальную базу данных и удаленную базу данных для синхронизации. Локальная база данных использует SQLite, а для удаленной базы данных я использую postgres. Мне нужно переместить данные из одной базы данных в другую и избежать дублирования информации.

Примерно то, что я делаю сейчас:

BEGIN;                                       //remote database  (start transaction)
SELECT * FROM local.queued TOP 1;            //local database   (select first queued element)
INSERT INTO remote.queued VALUES ( element ) //remote database  (insert first queued element on remote database)
BEGIN;                                       //local database   (start transaction)
DELETE * FROM local.queued LIMIT 1;          //local database   (delete first queued element on local database)
END;                                         //local database   (finalize transaction local database)
END;                                         //remote database  (finalize transaction remote database)

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

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

Ответы [ 2 ]

1 голос
/ 13 марта 2020

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

К сожалению, SQLite, похоже, не поддерживает его, но поскольку PostgreSQL делает, вы все равно можете использовать его, если задействованы только две базы данных:

BEGIN;  -- on PostgreSQL

              BEGIN;  -- on SQLite

   /*
    * Do work on both databases.
    * On error, ROLLBACK both transactions.
    */

PREPARE TRANSACTION 'somename';  -- PostgreSQL

              COMMIT;  -- SQLite

COMMIT PREPARED 'somename';  -- PostgreSQL

Теперь, если во время SQLite возникает ошибка COMMIT, вы запускаете ROLLBACK PREPARED 'sonename' на PostgreSQL. Идея состоит в том, что все, что может произойти сбой во время фиксации, выполняется во время PREPARE TRANSACTION, и состояние транзакции сохраняется, так что она остается открытой, но все равно переживает перезапуск сервера.

Это безопасно, но есть предостережение. Подготовленные транзакции опасны, потому что они удерживают блокировки и удерживают VACUUM от очистки (как и все другие транзакции), но они являются постоянными и сохраняются до тех пор, пока вы их явно не удалите. Таким образом, вам нужно некоторое программное обеспечение, диспетчер распределенных транзакций , которое является безопасным sh и отслеживает все распределенные транзакции. Этот менеджер транзакций может очистить все подготовленные транзакции после некоторого сбоя.

0 голосов
/ 12 марта 2020

Я думаю, что имеет смысл сделать ваши действия DML идемпотентными - то есть, если вы вызываете их несколько раз, они имеют одинаковый общий эффект. Например, мы можем сделать INSERT неактивным, если данные существуют:

INSERT INTO x(id, name)
SELECT nu.id, nu.name
FROM
  (SELECT 1 as id, 'a' as name) as nu
  LEFT JOIN x ON nu.id = x.id
WHERE
  x.id IS NULL

Вы можете запускать это столько раз, сколько захотите, и будет вставлена ​​только одна запись

* 1007. *https://www.db-fiddle.com/f/nbHmy3PVDQ3RrGMqLni1su/0

Вам необходимо решить, что делать, если запись существует в измененном состоянии - например, хотите ли вы оставить ее в покое или сбросить ее на входящие значения - вопрос в другой раз

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...