All Projects → rla → sql-sync

rla / sql-sync

Licence: other
Offline replication between SQLite (clients) and MySQL (master).

Programming Languages

javascript
184084 projects - #8 most used programming language
shell
77523 projects

Projects that are alternatives of or similar to sql-sync

Tunnel
PG数据同步工具(Java实现)
Stars: ✭ 122 (+134.62%)
Mutual labels:  sync, replication
Ron Cxx
RON2.1 C++ implementation
Stars: ✭ 33 (-36.54%)
Mutual labels:  sync, replication
Librg
🚀 Making multi-player gamedev simpler since 2017
Stars: ✭ 813 (+1463.46%)
Mutual labels:  sync, replication
Amazonriver
amazonriver 是一个将postgresql的实时数据同步到es或kafka的服务
Stars: ✭ 198 (+280.77%)
Mutual labels:  sync, replication
stock scan1
Working Demo of Barcode/QR code scanner using VueJs+Quasaar+Cordova
Stars: ✭ 42 (-19.23%)
Mutual labels:  sync
aw-server-rust
High-performance implementation of the ActivityWatch server, written in Rust
Stars: ✭ 76 (+46.15%)
Mutual labels:  sync
syncthing ynh
Syncthing package for YunoHost
Stars: ✭ 27 (-48.08%)
Mutual labels:  sync
readmeio-sync
↕️ A tool for syncing local markdown files to Readme.io
Stars: ✭ 18 (-65.38%)
Mutual labels:  sync
cossync
腾讯云COS同步(批量上传)工具
Stars: ✭ 23 (-55.77%)
Mutual labels:  sync
syncify
🤝 Shopify theme upload, download and watch development tool.
Stars: ✭ 50 (-3.85%)
Mutual labels:  sync
rdme
ReadMe's official CLI and GitHub Action
Stars: ✭ 44 (-15.38%)
Mutual labels:  sync
py-mongo-sync
Oplog-based data sync tool that synchronizes data from a replica set to another deployment, e.g.: standalone, replica set, and sharded cluster.
Stars: ✭ 102 (+96.15%)
Mutual labels:  sync
serverless-s3bucket-sync
Serverless Plugin to sync local folders with an S3 bucket
Stars: ✭ 24 (-53.85%)
Mutual labels:  sync
dotfiles
dotfiles to provision a new macOS with cosy dev setups
Stars: ✭ 88 (+69.23%)
Mutual labels:  sync
mono
Collaborative and distributed Markdown text editor, started as a bachelor thesis project at HEIG-VD.
Stars: ✭ 41 (-21.15%)
Mutual labels:  replication
docker-wordmove
Docker image to run Wordmove
Stars: ✭ 16 (-69.23%)
Mutual labels:  sync
vscode-syncify
A reliable way of syncing your VSCode settings and extensions
Stars: ✭ 13 (-75%)
Mutual labels:  sync
ansible-role-glusterfs
Ansible Role - GlusterFS
Stars: ✭ 95 (+82.69%)
Mutual labels:  sync
sleepover
💤 Sleep, snooze & step methods
Stars: ✭ 13 (-75%)
Mutual labels:  sync
bftdb
Tendermint + Sqlite3 = BFT Database Replication
Stars: ✭ 35 (-32.69%)
Mutual labels:  replication

sql-sync

Offline replication between SQLite (clients) and MySQL (master). This project is not a library, rather it is example code. Some parts of it (code in lib) could be reused in other projects. The code mainly just tests that the approach described here works. The code is built for Node.JS.

Assumes the following:

  • Primary keys are UUIDs (here 36-character strings);
  • No foreign key constraints.

It is possible to use foreign key constraints but then table updates must be reordered correctly (complicates the code a lot!).

It is possible to use natural keys instead of UUIDs but there do not always exist good natural keys. In most cases large composite keys would have to be used. It is not possible to use autoincremented keys because of key value conflicts.

Metainfo

On the client side table actions (INSERT/UPDATE/DELETE) are recorded into the metadata table sync with the following structure:

CREATE TABLE sync (
    action INTEGER NOT NULL,
    keyval CHARACTER(36) NOT NULL,
    tid INTEGER NOT NULL,
    PRIMARY KEY (keyval) ON CONFLICT REPLACE
);

In the table:

  • action - 0 marks insert/update, 1 marks delete;
  • keyval - primary key value in the row;
  • tid - table id (used by triggers below).

Synced tables are kept in the table sync_table:

CREATE TABLE sync_table (
    tid INTEGER NOT NULL,
    name VARCHAR(255) NOT NULL,
    keycol VARCHAR(255) NOT NULL,
    PRIMARY KEY (tid),
    UNIQUE (name)
);

In the table:

  • tid - table id.
  • name - table name.
  • keycol - name of primary key column in the table.

For each table in sync_table the following triggers have to be created:

CREATE TRIGGER <table>_insert
AFTER INSERT ON <table> FOR EACH ROW
BEGIN
    INSERT INTO sync (action, keyval, tid)
    VALUES (0, NEW.<keycol>, <tableid>);
END;

CREATE TRIGGER <table>_update
AFTER UPDATE ON <table> FOR EACH ROW
BEGIN
    INSERT INTO sync(action, keyval, tid)
    VALUES (1, OLD.<keycol>, <tableid>);
    INSERT INTO sync(action, keyval, tid)
    VALUES (0, NEW.<keycol>, <tableid>);
END;

CREATE TRIGGER <table>_delete
AFTER DELETE ON <table> FOR EACH ROW
BEGIN
    INSERT INTO sync(action, keyval, tid)
    VALUES (1, OLD.<keycol>, <tableid>);
END;

A special table is used for storing the last revision number (given by the server at the end of sync). This is sent with each sync request (but is not updated on each data table action on the client):

CREATE TABLE revision (
    rev UNSIGNED BIG INT NOT NULL
);

Metainfo tables on the server are similar. The main difference is in the sync table:

CREATE TABLE sync (
    action TINYINT UNSIGNED NOT NULL,
    keyval CHAR(36) NOT NULL,
    tid TINYINT UNSIGNED NOT NULL,
    rev BIGINT UNSIGNED NOT NULL,
    PRIMARY KEY (keyval)
);

The rev field is updated with each data table action. This is done with the help of the following stored procedure:

CREATE PROCEDURE sync_mark (
    in_keyval CHAR(36),
    in_tid TINYINT UNSIGNED,
    in_action VARCHAR(10)
)
BEGIN
    INSERT INTO sync (action, keyval, tid, rev)
    VALUES (
        in_action,
        in_keyval,
        in_tid,
        (SELECT rev + 1 FROM revision)
    ) ON DUPLICATE KEY UPDATE action = VALUES(action), rev = VALUES(rev);
    UPDATE revision SET rev = rev + 1;
END

The procedure is called by triggers. They have to be created for each data table:

CREATE TRIGGER <table>_insert
AFTER INSERT ON <table> FOR EACH ROW
BEGIN CALL sync_mark(NEW.<keycol>, <tableid>, 0); END

CREATE TRIGGER <table>_update
AFTER UPDATE ON <table> FOR EACH ROW
BEGIN
    CALL sync_mark(OLD.<keycol>, <tableid>, 1);
    CALL sync_mark(NEW.<keycol>, <tableid>, 0);
END

CREATE TRIGGER <table>_delete
AFTER DELETE ON <table> FOR EACH ROW
BEGIN CALL sync_mark(OLD.<keycol>, <tableid>, 1); END

General algorithm

  1. Client finds all changes.
  2. Client finds all deletes.
  3. Client finds clrev, the last revision the client synced with the server.
  4. Client sends changes, deletes and clrev to server.
  5. Server locks meta and data tables.
  6. Server finds current revision crrev.
  7. Server applies client changes and deletes.
  8. Server finds current revison again, nwrev.
  9. Server finds all changes between clrev and crrev.
  10. Server finds all deletes between clrev and crrev.
  11. Server unlocks tables.
  12. Server sends changes, deletes and nwrev back to the client.
  13. Client stores nwrev.

Finding changes on the client (this and others have to be executed per data table):

SELECT <table>.* FROM <table>
JOIN sync ON (<table>.<keycol> = sync.keyval)
WHERE sync.action = 0 AND sync.tid = <tableid>
ORDER BY <keycol>;

Finding deletes on the client:

SELECT keyval FROM sync
WHERE action = 1 AND tid = <tableid> ORDER BY keyval;

Applying changes on the server (per row):

INSERT INTO <table> (col1, col2, ...)
VALUES (val1, val2, ...)
ON DUPLICATE KEY UPDATE col1 = VALUES(col1),
col2 = VALUES(col2), ...;

Applying deletes on the server (per deleted row):

DELETE FROM <table> WHERE <keycol> = value;

Finding changes on the server:

SELECT <table>.* FROM <table>
JOIN sync ON (<table>.<keycol> = sync.keyval
WHERE sync.action = 0 AND sync.rev > <clrev>
AND sync.rev <= <crrev>
AND sync.tid = <tableid> ORDER BY <keycol>;

Finding deletes on the server:

SELECT keyval FROM sync
WHERE action = 1 AND rev > <clrev>
AND rev <= <crrev> AND tid = <tableid>
ORDER BY keyval;

Applying changes on the client (per row, both queries are needed):

INSERT OR IGNORE INTO <table> (col1, col2, ...)
VALUES (val1, val2, ...);
UPDATE <table> SET col1 = val1, col2 = val2, ...
WHERE <keycol> = value;

Applying deletes on the server (per deleted row):

DELETE FROM <table>
WHERE <keycol> = value;

Multiuser case

When data is kept by user (data tables contain some sort of user id) then the revision table on the server side must also contain user id. Queries on the server must take it into account (add to WHERE clauses or SET user id when inserting). The procedure sync_mark has to be rewritten to update by-user rev value.

Sync data over HTTP

Both the server and the client send JSON object in the form:

{
    "<table>": {
        "deletes": [ "keyval1", ... ],
        "changes": [
            {
                "prop1": "value1",
                "prop2": 23
            }
        ]
    },
    "revision": 120
}

Running tests

Install dependencies:

npm install

And install Mocha:

npm install mocha -g

Modify MySQL connection details in Makefile and tests/helpers/server.js. Run make test. Tests will create test schema in two clients and the server and run various random operations in each. Then synchronize and check for data consistency.

License

The MIT License.

Copyright (c) 2013 Raivo Laanemets

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].