Table of contents

Replication

Replication is the ability of a cozy-stack to copy / move all or a subset of its data to another support. It should cover 2 use cases

Replication will not be used for Moving, nor Backup. See associated docs in this folder.

CouchDB replication is a well-understood, safe and stable algorithm ensuring replication between two couchdb databases. It is delta-based, and is stateful: we sync changes since a given checkpoint.

Files synchronization

Replication of too heavy database (with a lot of attachments) has been, in cozy experience, the source of some bugs. To avoid that (and some other issues), in cozy-stack, the attachments are stored outside of couchdb.

This means we need a way to synchronize files in parallel to couchdb replication.

Rsync is a well-understood, safe and stable algorithm to replicate files hierarchy from one hosts to the other by only transfering changes. It is one-shot and stateless. It could be an inspiration if we have to focus on syncing small changes to big files. The option to use rsync/zsync have been discussed internally (https://github.com/cozy/cozy-stack/pull/57 & Sprint Start 2016-10-24), but for now we should focus on using the files/folders couchdb document for synchronization purpose and upload/download the actual binaries with existing files routes or considering webdav.

Useful golang package: http://rclone.org/ sync folder in FS / Swift (may be we can add a driver for cozy)

Couchdb replication & limitation

Couchdb 2 replication protocol is described in details here.

Quick summary

  1. The replicator figures out what was the last sequence number lastseqno which was correctly synced from the source database, using a _local/:replicationid document.
  2. The replicator GET :source/changes?since=lastseqno&limit=batchsize and identify which docs have changed (and their open_revs).
  3. The replicator POST :target/_revs_diff to get a list of all revisions of the changed documents the target does not know.
  4. The replicator GET :source/:docid?open_revs=['rev1', ...] several times to retrieve the missing documents revisions.
  5. The replicator POST :target/_bulk_docs with these documents revisions.
  6. Store the new last sequence number

Repeat 2-5 until there is no more changes.

Details

Routes used by replication

To be a source of replication, the stack only need to support the following route (and query parameters):

To be a target of replication, the stack need to support the following routes:

In both case, we need to support

Stack Sync API exploration

Easy part: 1 db/doctype on stack AND remote, no binaries

We just need to implement the routes described above by proxying to the underlying couchdb database.

var db = new PouchDB("my-local-contacts");
db.replicate.from("https://bob.cozycloud.cc/data/contacts");
db.replicate.to("https://bob.cozycloud.cc/data/contacts");

To suport this we need to:

This will cover documents part of the Devices use case.

Continuous replication

It is impossible to implement it by simply proxying to couchdb (see unlimited inactive users).

The current version of cozy-desktop uses it. TODISCUSS It could be replaced by 3-minutes polling without big losses in functionality, eventually with some more triggers based on user activity.

The big use case for which we might want it is Sharing. But, because Sharing will (first) be stack to stack, we might imagine having the source of event “ping” the remote through a separate route.

Conclusion: Start with polling replication. Consider alternative notifications mechanism when the usecase appears and eventually use time-limited continuous replication for a few special case (collaborative edit).

Realtime

One of the cool feature of cozy apps was how changes are sent to the client in realtime through websocket. However this have a cost: every cozy and every apps open in a browser, even while not used keep one socket open on the server, this is not scalable to thousands of users.

Several technology can be used for realtime: Websocket, SSE or COMET like long-polling. Goroutines makes all solution similar performances wise. COMET is a hack, websocket seems more popular and tested (x/net/websocket vs html5-sse-example). SSE is not widely available and has some limitations (headers…)

Depending on benchmarking, we can do some optimization on the feed:

To have some form of couchdb-to-stack continuous changes monitoring, we can monitor _db_udpates (Doc) which gives us an update when a couchdb has changed. We can then perform a _changes query on this database to get the changed docs and proxy that to the stack-to-client change feed.

Conclusion: We will use Websocket from the client to the stack. We will try to avoid using continuous changes feed from couchdb to the stack. We will optimize if proven needed by benchmarks, starting with “useless” changes and eventually some multiplexing.

Sharing

To be completed by discussing with Science team.

Current ideas (as understood by Romain)

Proposal by Romain, if we find _selector filter replication performances to be acceptable on very large / very old databases.

TODO experiment with performance of _selector filtered replication in couchdb2