From 7cac2f7d76079150e078a33a2c9e8c4c7c597bb0 Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 9 Jan 2017 10:46:58 +0100 Subject: [PATCH] Generate deterministic range ids based on seed --- .../app/coffee/RangesManager.coffee | 2 + .../app/coffee/RangesTracker.coffee | 63 +++++-------------- .../test/acceptance/coffee/RangesTests.coffee | 7 ++- 3 files changed, 23 insertions(+), 49 deletions(-) diff --git a/services/document-updater/app/coffee/RangesManager.coffee b/services/document-updater/app/coffee/RangesManager.coffee index 1e19a63b0d..ac1dcbe75f 100644 --- a/services/document-updater/app/coffee/RangesManager.coffee +++ b/services/document-updater/app/coffee/RangesManager.coffee @@ -8,6 +8,8 @@ module.exports = RangesManager = rangesTracker = new RangesTracker(changes, comments) for update in updates rangesTracker.track_changes = !!update.meta.tc + if !!update.meta.tc + rangesTracker.setIdSeed(update.meta.tc) for op in update.op rangesTracker.applyOp(op, { user_id: update.meta?.user_id }) diff --git a/services/document-updater/app/coffee/RangesTracker.coffee b/services/document-updater/app/coffee/RangesTracker.coffee index 1b865a600d..36ef621493 100644 --- a/services/document-updater/app/coffee/RangesTracker.coffee +++ b/services/document-updater/app/coffee/RangesTracker.coffee @@ -35,18 +35,19 @@ load = (EventEmitter) -> # * Inserts by another user will not combine with inserts by the first user. If they are in the # middle of a previous insert by the first user, the original insert will be split into two. constructor: (@changes = [], @comments = []) -> - # Change objects have the following structure: - # { - # id: ... # Uniquely generated by us - # op: { # ShareJs style op tracking the offset (p) and content inserted (i) or deleted (d) - # i: "..." - # p: 42 - # } - # } - # - # Ids are used to uniquely identify a change, e.g. for updating it in the database, or keeping in - # sync with Ace ranges. - @id = 0 + + getIdSeed: () -> + return @id_seed + + setIdSeed: (seed) -> + @id_seed = seed + @id_increment = 0 + + newId: () -> + @id_increment++ + increment = @id_increment.toString(16) + id = @id_seed + '000000'.substr(0, 6 - increment.length) + increment; + return id getComment: (comment_id) -> comment = null @@ -56,19 +57,6 @@ load = (EventEmitter) -> break return comment - resolveCommentId: (comment_id, resolved_data) -> - comment = @getComment(comment_id) - return if !comment? - comment.metadata.resolved = true - comment.metadata.resolved_data = resolved_data - @emit "comment:resolved", comment - - unresolveCommentId: (comment_id) -> - comment = @getComment(comment_id) - return if !comment? - comment.metadata.resolved = false - @emit "comment:unresolved", comment - removeCommentId: (comment_id) -> comment = @getComment(comment_id) return if !comment? @@ -88,7 +76,7 @@ load = (EventEmitter) -> return if !change? @_removeChange(change) - applyOp: (op, metadata) -> + applyOp: (op, metadata = {}) -> metadata.ts ?= new Date() # Apply an op that has been applied to the document to our changes to keep them up to date if op.i? @@ -105,7 +93,7 @@ load = (EventEmitter) -> addComment: (op, metadata) -> # TODO: Don't allow overlapping comments? @comments.push comment = { - id: @_newId() + id: @newId() op: # Copy because we'll modify in place c: op.c p: op.p @@ -394,28 +382,9 @@ load = (EventEmitter) -> if moved_changes.length > 0 @emit "changes:moved", moved_changes - _newId: () -> - # Generate a Mongo ObjectId - # Reference: https://github.com/dreampulse/ObjectId.js/blob/master/src/main/javascript/Objectid.js - @_pid ?= Math.floor(Math.random() * (32767)) - @_machine ?= Math.floor(Math.random() * (16777216)) - timestamp = Math.floor(new Date().valueOf() / 1000) - @_increment ?= 0 - @_increment++ - - timestamp = timestamp.toString(16) - machine = @_machine.toString(16) - pid = @_pid.toString(16) - increment = @_increment.toString(16) - - return '00000000'.substr(0, 8 - timestamp.length) + timestamp + - '000000'.substr(0, 6 - machine.length) + machine + - '0000'.substr(0, 4 - pid.length) + pid + - '000000'.substr(0, 6 - increment.length) + increment; - _addOp: (op, metadata) -> change = { - id: @_newId() + id: @newId() op: op metadata: metadata } diff --git a/services/document-updater/test/acceptance/coffee/RangesTests.coffee b/services/document-updater/test/acceptance/coffee/RangesTests.coffee index 0cee1598aa..8da51c0899 100644 --- a/services/document-updater/test/acceptance/coffee/RangesTests.coffee +++ b/services/document-updater/test/acceptance/coffee/RangesTests.coffee @@ -12,6 +12,7 @@ describe "Ranges", -> before (done) -> @project_id = DocUpdaterClient.randomId() @user_id = DocUpdaterClient.randomId() + @id_seed = "587357bd35e64f6157" @doc = { id: DocUpdaterClient.randomId() lines: ["aaa"] @@ -25,7 +26,7 @@ describe "Ranges", -> doc: @doc.id op: [{ i: "456", p: 5 }] v: 1 - meta: { user_id: @user_id, tc: 1 } + meta: { user_id: @user_id, tc: @id_seed } }, { doc: @doc.id op: [{ d: "12", p: 1 }] @@ -52,6 +53,7 @@ describe "Ranges", -> ranges = data.ranges change = ranges.changes[0] change.op.should.deep.equal { i: "456", p: 3 } + change.id.should.equal @id_seed + "000001" change.metadata.user_id.should.equal @user_id done() @@ -135,6 +137,7 @@ describe "Ranges", -> before (done) -> @project_id = DocUpdaterClient.randomId() @user_id = DocUpdaterClient.randomId() + @id_seed = "587357bd35e64f6157" @doc = { id: DocUpdaterClient.randomId() lines: ["a123aa"] @@ -143,7 +146,7 @@ describe "Ranges", -> doc: @doc.id op: [{ i: "456", p: 5 }] v: 0 - meta: { user_id: @user_id, tc: 1 } + meta: { user_id: @user_id, tc: @id_seed } } MockWebApi.insertDoc @project_id, @doc.id, { lines: @doc.lines