The litereplica is injected in the SQLite code at the pager level. It is where the database pages are handled and where the transactions occur.
When master and slave programs connect the master generates hashes of each page and sends the list to the slave. The slave makes hashes for each db page too and compares them. It requests the modified pages to the master, who sends them. Then the slave starts a transaction in the pager level and updates the pages in the database file. Doing so the SQLite engine creates a rollback journal file that contains the original db pages.
In the case the transaction is rolled back in the master, the slave do a rollback too and the db returns to the original state.
If the connection drops or a timeout occurs the slave rolls it back and closes the connection.
From this point on both are considered in a "sync" state. Each time a change is made in the master db (when a page is modified and saved) it will be replicated to the slave, sending only the modified pages to the slave. If the master rolls back, the slave does the same.
If the connection drops, they fall out of the "sync" state and then a new re-connection process is required, and all the pages must be check again.
So all the updates are atomic and the database integrity is guaranteed on the slave side. Also the database in the slave is binary equivalent to the master db file, byte by byte.
Note that the master db is locked for writes when both connect and start the sync process. So this is the time in which the apps trying to write to the db will face a delay or a SQLITE_BUSY error, and have to try the update again.
But after they are connected and in sync there are no more locks in the master db (at least due to litereplica).
This is an example function in C that deals with the SQLITE_BUSY:
int exec_sql(sqlite3 *db, char *sql) {
char *errmsg=0;
int rc;
while(1) {
switch (rc = sqlite3_exec(db, sql, 0, 0, &errmsg)) {
case SQLITE_OK:
return rc;
case SQLITE_BUSY:
sqlite3_free(errmsg);
sleep_ms(5);
break;
default:
printf("sqlite3_exec failed: (%d) %s
", rc, errmsg);
sqlite3_free(errmsg);
return rc;
}
}
}
The litereplica also has a point-in-time recovery system. It is implemented just renaming the journal file to another name in the end of each transaction instead of deleting it. If a restore is needed the journal is renamed back and a rollback is fired in the SQLite engine. Simple!
If you use the PITR system, do not change the "PRAGMA journal_mode" as it is set internally as BINLOG.
Also remember that the WAL mode is not (currently) supported by litereplica.
Feel free to ask questions in the general discussion forum or via e-mail.