summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordrebs <drebs@leap.se>2014-04-24 16:33:29 -0300
committerdrebs <drebs@leap.se>2014-05-22 18:43:48 -0300
commitf3abf619ddd6be9dee7ed5807b967e06a6d7ef93 (patch)
treeb64bdddfd5a72ceca51d08348f19345dd0667167
parent8e750af78bd8f9a37d1b095712b66e6ecdb3e102 (diff)
Add splitted POST sync design docs (#5571).
-rw-r--r--common/src/leap/soledad/common/ddocs/syncs/updates/state.js99
-rw-r--r--common/src/leap/soledad/common/ddocs/syncs/views/changes_to_return/map.js18
-rw-r--r--common/src/leap/soledad/common/ddocs/syncs/views/seen_ids/map.js9
-rw-r--r--common/src/leap/soledad/common/ddocs/syncs/views/state/map.js16
4 files changed, 142 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..cb2b6b7b
--- /dev/null
+++ b/common/src/leap/soledad/common/ddocs/syncs/updates/state.js
@@ -0,0 +1,99 @@
+/**
+ * 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>': {
+ * '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'];
+
+ // trash outdated sync data for that replica if that exists
+ if (doc['ongoing_syncs'][source_replica_uid] != null &&
+ doc['ongoing_syncs'][source_replica_uid] == null)
+ 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] = {
+ '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'];
+}
+
diff --git a/common/src/leap/soledad/common/ddocs/syncs/views/changes_to_return/map.js b/common/src/leap/soledad/common/ddocs/syncs/views/changes_to_return/map.js
new file mode 100644
index 00000000..220345dc
--- /dev/null
+++ b/common/src/leap/soledad/common/ddocs/syncs/views/changes_to_return/map.js
@@ -0,0 +1,18 @@
+function(doc) {
+ if (doc['_id'] == 'u1db_sync_state' && doc['ongoing_syncs'] != null)
+ for (var source_replica_uid in doc['ongoing_syncs']) {
+ var changes = doc['ongoing_syncs'][source_replica_uid]['changes_to_return'];
+ if (changes == null)
+ emit([source_replica_uid, 0], null);
+ else if (changes.length == 0)
+ emit([source_replica_uid, 0], []);
+ for (var i = 0; i < changes['changes_to_return'].length; i++)
+ emit(
+ [source_replica_uid, i],
+ {
+ 'gen': changes['gen'],
+ 'trans_id': changes['trans_id'],
+ 'next_change_to_return': changes['changes_to_return'][i],
+ });
+ }
+}
diff --git a/common/src/leap/soledad/common/ddocs/syncs/views/seen_ids/map.js b/common/src/leap/soledad/common/ddocs/syncs/views/seen_ids/map.js
new file mode 100644
index 00000000..34c65b3f
--- /dev/null
+++ b/common/src/leap/soledad/common/ddocs/syncs/views/seen_ids/map.js
@@ -0,0 +1,9 @@
+function(doc) {
+ if (doc['_id'] == 'u1db_sync_state' && doc['ongoing_syncs'] != null)
+ for (var source_replica_uid in doc['ongoing_syncs'])
+ emit(
+ source_replica_uid,
+ {
+ 'seen_ids': doc['ongoing_syncs'][source_replica_uid]['seen_ids'],
+ });
+}
diff --git a/common/src/leap/soledad/common/ddocs/syncs/views/state/map.js b/common/src/leap/soledad/common/ddocs/syncs/views/state/map.js
new file mode 100644
index 00000000..1d8f8e84
--- /dev/null
+++ b/common/src/leap/soledad/common/ddocs/syncs/views/state/map.js
@@ -0,0 +1,16 @@
+function(doc) {
+ if (doc['_id'] == 'u1db_sync_state' && doc['ongoing_syncs'] != null)
+ for (var source_replica_uid in doc['ongoing_syncs']) {
+ var changes = doc['ongoing_syncs'][source_replica_uid]['changes_to_return'];
+ if (changes == null)
+ emit(source_replica_uid, null);
+ else
+ emit(
+ source_replica_uid,
+ {
+ 'gen': changes['gen'],
+ 'trans_id': changes['trans_id'],
+ 'number_of_changes': changes['changes_to_return'].length
+ });
+ }
+}