From ae30f32481ca7ebbeeed34c260409aa3cec41a3a Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 9 Jan 2017 10:49:03 +0100 Subject: [PATCH] Use deterministic ids based on a seed --- .../public/coffee/ide/editor/Document.coffee | 7 ++++ .../coffee/ide/editor/EditorManager.coffee | 3 -- .../coffee/ide/editor/ShareJsDoc.coffee | 6 ++-- .../editor/sharejs/vendor/client/doc.coffee | 2 ++ .../ide/review-panel/RangesTracker.coffee | 34 ++++++++----------- .../controllers/ReviewPanelController.coffee | 21 +++++++++++- 6 files changed, 47 insertions(+), 26 deletions(-) diff --git a/services/web/public/coffee/ide/editor/Document.coffee b/services/web/public/coffee/ide/editor/Document.coffee index 229e281dd8..6984479b21 100644 --- a/services/web/public/coffee/ide/editor/Document.coffee +++ b/services/web/public/coffee/ide/editor/Document.coffee @@ -83,6 +83,9 @@ define [ setTrackingChanges: (track_changes) -> @doc.track_changes = track_changes + + setTrackChangesIdSeeds: (id_seeds) -> + @doc.track_changes_id_seeds = id_seeds _bindToSocketEvents: () -> @_onUpdateAppliedHandler = (update) => @_onUpdateApplied(update) @@ -319,6 +322,8 @@ define [ v: version @doc.on "change", (ops, oldSnapshot, msg) => @_applyOpsToRanges(ops, oldSnapshot, msg) + @doc.on "flipped_pending_to_inflight", () => + @trigger "flipped_pending_to_inflight" _onError: (error, meta = {}) -> meta.doc_id = @doc_id @@ -335,6 +340,8 @@ define [ _applyOpsToRanges: (ops = [], oldSnapshot, msg) -> track_changes_as = null remote_op = msg? + if msg.meta?.tc? + @ranges.setIdSeed(msg.meta.tc) if remote_op and msg.meta?.tc track_changes_as = msg.meta.user_id else if !remote_op and @track_changes_as? diff --git a/services/web/public/coffee/ide/editor/EditorManager.coffee b/services/web/public/coffee/ide/editor/EditorManager.coffee index b7fccc29af..629e1a4cb3 100644 --- a/services/web/public/coffee/ide/editor/EditorManager.coffee +++ b/services/web/public/coffee/ide/editor/EditorManager.coffee @@ -166,14 +166,11 @@ define [ if want == have return - console.log "Trying to set track changes to:", want do tryToggle = () => saved = !doc.getInflightOp()? and !doc.getPendingOp()? if saved - console.log "SUCCESS, changing value", want doc.setTrackingChanges(want) @$scope.$apply () => @$scope.editor.trackChanges = want else - console.log "Still in flight, will try soon" @_syncTimeout = setTimeout tryToggle, 100 diff --git a/services/web/public/coffee/ide/editor/ShareJsDoc.coffee b/services/web/public/coffee/ide/editor/ShareJsDoc.coffee index 48a7bbf3c6..f580c56f77 100644 --- a/services/web/public/coffee/ide/editor/ShareJsDoc.coffee +++ b/services/web/public/coffee/ide/editor/ShareJsDoc.coffee @@ -24,7 +24,7 @@ define [ return if @track_changes update.meta ?= {} - update.meta.tc = 1 + update.meta.tc = @track_changes_id_seeds.inflight @socket.emit "applyOtUpdate", @doc_id, update, (error) => return @_handleError(error) if error? state: "ok" @@ -44,6 +44,8 @@ define [ # ops as quickly as possible for low latency. @_doc.setFlushDelay(0) @trigger "remoteop", args... + @_doc.on "flipped_pending_to_inflight", () => + @trigger "flipped_pending_to_inflight" @_doc.on "error", (e) => @_handleError(e) @@ -117,7 +119,7 @@ define [ attachToAce: (ace) -> @_doc.attach_ace(ace, false, window.maxDocLength) detachFromAce: () -> @_doc.detach_ace?() - + INFLIGHT_OP_TIMEOUT: 5000 # Retry sending ops after 5 seconds without an ack WAIT_FOR_CONNECTION_TIMEOUT: 500 # If we're waiting for the project to join, try again in 0.5 seconds _startInflightOpTimeout: (update) -> diff --git a/services/web/public/coffee/ide/editor/sharejs/vendor/client/doc.coffee b/services/web/public/coffee/ide/editor/sharejs/vendor/client/doc.coffee index 301c4d5e04..aca3560b49 100644 --- a/services/web/public/coffee/ide/editor/sharejs/vendor/client/doc.coffee +++ b/services/web/public/coffee/ide/editor/sharejs/vendor/client/doc.coffee @@ -266,6 +266,8 @@ class Doc @pendingOp = null @pendingCallbacks = [] + @emit "flipped_pending_to_inflight" + #console.log "SENDING OP TO SERVER", @inflightOp, @version @connection.send {doc:@name, op:@inflightOp, v:@version} diff --git a/services/web/public/coffee/ide/review-panel/RangesTracker.coffee b/services/web/public/coffee/ide/review-panel/RangesTracker.coffee index 6ff301e803..36ef621493 100644 --- a/services/web/public/coffee/ide/review-panel/RangesTracker.coffee +++ b/services/web/public/coffee/ide/review-panel/RangesTracker.coffee @@ -36,24 +36,18 @@ load = (EventEmitter) -> # middle of a previous insert by the first user, the original insert will be split into two. constructor: (@changes = [], @comments = []) -> - @_increment: 0 - @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++ - - 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; + 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 @@ -99,7 +93,7 @@ load = (EventEmitter) -> addComment: (op, metadata) -> # TODO: Don't allow overlapping comments? @comments.push comment = { - id: RangesTracker.newId() + id: @newId() op: # Copy because we'll modify in place c: op.c p: op.p @@ -390,7 +384,7 @@ load = (EventEmitter) -> _addOp: (op, metadata) -> change = { - id: RangesTracker.newId() + id: @newId() op: op metadata: metadata } diff --git a/services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee b/services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee index 103f566617..4a5c02ba01 100644 --- a/services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee +++ b/services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee @@ -75,12 +75,17 @@ define [ if view == $scope.SubViews.OVERVIEW refreshOverviewPanel() - $scope.$watch "editor.sharejs_doc", (doc) -> + $scope.$watch "editor.sharejs_doc", (doc, old_doc) -> return if !doc? # The open doc range tracker is kept up to date in real-time so # replace any outdated info with this rangesTrackers[doc.doc_id] = doc.ranges $scope.reviewPanel.rangesTracker = rangesTrackers[doc.doc_id] + if old_doc? + old_doc.off "flipped_pending_to_inflight" + doc.on "flipped_pending_to_inflight", () -> + regenerateTrackChangesId(doc) + regenerateTrackChangesId(doc) $scope.$watch (() -> entries = $scope.reviewPanel.entries[$scope.editor.open_doc_id] or {} @@ -94,6 +99,20 @@ define [ $scope.$broadcast "review-panel:toggle" $scope.$broadcast "review-panel:layout" + regenerateTrackChangesId = (doc) -> + old_id = getChangeTracker(doc.doc_id).getIdSeed() + # Generate a the first 18 characters of Mongo ObjectId, leaving 6 for the increment part + # Reference: https://github.com/dreampulse/ObjectId.js/blob/master/src/main/javascript/Objectid.js + pid = Math.floor(Math.random() * (32767)).toString(16) + machine = Math.floor(Math.random() * (16777216)).toString(16) + timestamp = Math.floor(new Date().valueOf() / 1000).toString(16) + new_id = '00000000'.substr(0, 8 - timestamp.length) + timestamp + + '000000'.substr(0, 6 - machine.length) + machine + + '0000'.substr(0, 4 - pid.length) + pid + + getChangeTracker(doc.doc_id).setIdSeed(new_id) + doc.setTrackChangesIdSeeds({pending: new_id, inflight: old_id}) + refreshOverviewPanel = () -> $scope.reviewPanel.overview.loading = true $http.get "/project/#{$scope.project_id}/ranges"