summaryrefslogtreecommitdiff
path: root/common/src/leap/soledad/common/ddocs/syncs/updates/state.js
diff options
context:
space:
mode:
Diffstat (limited to 'common/src/leap/soledad/common/ddocs/syncs/updates/state.js')
-rw-r--r--common/src/leap/soledad/common/ddocs/syncs/updates/state.js105
1 files changed, 105 insertions, 0 deletions
diff --git a/common/src/leap/soledad/common/ddocs/syncs/updates/state.js b/common/src/leap/soledad/common/ddocs/syncs/updates/state.js
new file mode 100644
index 00000000..d62aeb40
--- /dev/null
+++ b/common/src/leap/soledad/common/ddocs/syncs/updates/state.js
@@ -0,0 +1,105 @@
+/**
+ * This update handler stores information about ongoing synchronization
+ * attempts from distinct source replicas.
+ *
+ * Normally, u1db synchronization occurs during one POST request. In order to
+ * split that into many serial POST requests, we store the state of each sync
+ * in the server, using a document with id 'u1db_sync_state'. To identify
+ * each sync attempt, we use a sync_id sent by the client. If we ever receive
+ * a new sync_id, we trash current data for that source replica and start
+ * over.
+ *
+ * We expect the following in the document body:
+ *
+ * {
+ * 'source_replica_uid': '<source_replica_uid>',
+ * 'sync_id': '<sync_id>',
+ * 'seen_ids': [['<doc_id>', <at_gen>], ...], // optional
+ * 'changes_to_return': [ // optional
+ * 'gen': <gen>,
+ * 'trans_id': '<trans_id>',
+ * 'changes_to_return': [[<doc_id>', <gen>, '<trans_id>'], ...]
+ * ],
+ * }
+ *
+ * The format of the final document stored on server is:
+ *
+ * {
+ * '_id': '<str>',
+ * '_rev' '<str>',
+ * 'ongoing_syncs': {
+ * '<source_replica_uid>': {
+ * 'sync_id': '<sync_id>',
+ * 'seen_ids': [['<doc_id>', <at_gen>[, ...],
+ * 'changes_to_return': {
+ * 'gen': <gen>,
+ * 'trans_id': '<trans_id>',
+ * 'changes_to_return': [
+ * ['<doc_id>', <gen>, '<trans_id>'],
+ * ...,
+ * ],
+ * },
+ * },
+ * ... // info about other source replicas here
+ * }
+ * }
+ */
+function(doc, req) {
+
+ // prevent updates to alien documents
+ if (doc != null && doc['_id'] != 'u1db_sync_state')
+ return [null, 'invalid data'];
+
+ // create the document if it doesn't exist
+ if (!doc)
+ doc = {
+ '_id': 'u1db_sync_state',
+ 'ongoing_syncs': {},
+ };
+
+ // parse and validate incoming data
+ var body = JSON.parse(req.body);
+ if (body['source_replica_uid'] == null)
+ return [null, 'invalid data'];
+ var source_replica_uid = body['source_replica_uid'];
+
+ if (body['sync_id'] == null)
+ return [null, 'invalid data'];
+ var sync_id = body['sync_id'];
+
+ // trash outdated sync data for that replica if that exists
+ if (doc['ongoing_syncs'][source_replica_uid] != null &&
+ doc['ongoing_syncs'][source_replica_uid]['sync_id'] != sync_id)
+ delete doc['ongoing_syncs'][source_replica_uid];
+
+ // create an entry for that source replica
+ if (doc['ongoing_syncs'][source_replica_uid] == null)
+ doc['ongoing_syncs'][source_replica_uid] = {
+ 'sync_id': sync_id,
+ 'seen_ids': {},
+ 'changes_to_return': null,
+ };
+
+ // incoming meta-data values should be exclusive, so we count how many
+ // arrived and deny to accomplish the transaction if the count is high.
+ var incoming_values = 0;
+ var info = doc['ongoing_syncs'][source_replica_uid]
+
+ // add incoming seen id
+ if ('seen_id' in body) {
+ info['seen_ids'][body['seen_id'][0]] = body['seen_id'][1];
+ incoming_values += 1;
+ }
+
+ // add incoming changes_to_return
+ if ('changes_to_return' in body) {
+ info['changes_to_return'] = body['changes_to_return'];
+ incoming_values += 1;
+ }
+
+ if (incoming_values != 1)
+ return [null, 'invalid data'];
+
+ return [doc, 'ok'];
+}
+