Generate deterministic range ids based on seed

This commit is contained in:
James Allen 2017-01-09 10:46:58 +01:00
parent 2c7029cc50
commit 7cac2f7d76
3 changed files with 23 additions and 49 deletions

View file

@ -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 })

View file

@ -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
}

View file

@ -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