Merge pull request #33 from sharelatex/bg-add-timestamp-marker-to-project-history-queue

set a timestamp for the first entry in the projectHistory:Ops queue (connects to overleaf/sharelatex#658)
This commit is contained in:
Brian Gough 2018-07-23 13:41:09 +01:00 committed by GitHub
commit c4f1e27627
4 changed files with 49 additions and 6 deletions

View file

@ -4,8 +4,18 @@ rclient = require("redis-sharelatex").createClient(Settings.redis.documentupdate
logger = require('logger-sharelatex') logger = require('logger-sharelatex')
module.exports = ProjectHistoryRedisManager = module.exports = ProjectHistoryRedisManager =
queueOps: (project_id, ops..., callback) -> queueOps: (project_id, ops..., callback = (error, projectUpdateCount) ->) ->
rclient.rpush projectHistoryKeys.projectHistoryOps({project_id}), ops..., callback multi = rclient.multi()
# Push the ops onto the project history queue
multi.rpush projectHistoryKeys.projectHistoryOps({project_id}), ops...
# To record the age of the oldest op on the queue set a timestamp if not
# already present (SETNX).
multi.setnx projectHistoryKeys.projectHistoryFirstOpTimestamp({project_id}), Date.now()
multi.exec (error, result) ->
return callback(error) if error?
# return the number of entries pushed onto the project history queue
callback null, result[0]
queueRenameEntity: (project_id, projectHistoryId, entity_type, entity_id, user_id, projectUpdate, callback) -> queueRenameEntity: (project_id, projectHistoryId, entity_type, entity_id, user_id, projectUpdate, callback) ->
projectUpdate = projectUpdate =

View file

@ -74,6 +74,7 @@ module.exports =
project_history: project_history:
key_schema: key_schema:
projectHistoryOps: ({project_id}) -> "ProjectHistory:Ops:#{project_id}" projectHistoryOps: ({project_id}) -> "ProjectHistory:Ops:#{project_id}"
projectHistoryFirstOpTimestamp: ({project_id}) -> "ProjectHistory:FirstOpTimestamp:#{project_id}"
# cluster: [{ # cluster: [{
# port: "7000" # port: "7000"
# host: "localhost" # host: "localhost"

View file

@ -33,7 +33,7 @@ describe "Applying updates to a doc", ->
before (done) -> before (done) ->
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()] [@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
sinon.spy MockWebApi, "getDocument" sinon.spy MockWebApi, "getDocument"
@startTime = Date.now()
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version} MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) -> DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) ->
throw error if error? throw error if error?
@ -67,6 +67,27 @@ describe "Applying updates to a doc", ->
JSON.parse(updates[0]).op.should.deep.equal @update.op JSON.parse(updates[0]).op.should.deep.equal @update.op
done() done()
it "should set the first op timestamp", (done) ->
rclient_history.get ProjectHistoryKeys.projectHistoryFirstOpTimestamp({@project_id}), (error, result) =>
throw error if error?
result.should.be.within(@startTime, Date.now())
@firstOpTimestamp = result
done()
describe "when sending another update", ->
before (done) ->
@second_update = Object.create(@update)
@second_update.v = @version + 1
DocUpdaterClient.sendUpdate @project_id, @doc_id, @second_update, (error) ->
throw error if error?
setTimeout done, 200
it "should not change the first op timestamp", (done) ->
rclient_history.get ProjectHistoryKeys.projectHistoryFirstOpTimestamp({@project_id}), (error, result) =>
throw error if error?
result.should.equal @firstOpTimestamp
done()
describe "when the document is loaded", -> describe "when the document is loaded", ->
before (done) -> before (done) ->
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()] [@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]

View file

@ -20,6 +20,7 @@ describe "ProjectHistoryRedisManager", ->
project_history: project_history:
key_schema: key_schema:
projectHistoryOps: ({project_id}) -> "ProjectHistory:Ops:#{project_id}" projectHistoryOps: ({project_id}) -> "ProjectHistory:Ops:#{project_id}"
projectHistoryFirstOpTimestamp: ({project_id}) -> "ProjectHistory:FirstOpTimestamp:#{project_id}"
} }
"redis-sharelatex": "redis-sharelatex":
createClient: () => @rclient createClient: () => @rclient
@ -32,16 +33,26 @@ describe "ProjectHistoryRedisManager", ->
describe "queueOps", -> describe "queueOps", ->
beforeEach -> beforeEach ->
@ops = ["mock-op-1", "mock-op-2"] @ops = ["mock-op-1", "mock-op-2"]
@rclient.rpush = sinon.stub() @multi = exec: sinon.stub()
@multi.rpush = sinon.stub()
@multi.setnx = sinon.stub()
@rclient.multi = () => @multi
# @rclient = multi: () => @multi
@ProjectHistoryRedisManager.queueOps @project_id, @ops..., @callback @ProjectHistoryRedisManager.queueOps @project_id, @ops..., @callback
it "should queue an update", -> it "should queue an update", ->
@rclient.rpush @multi.rpush
.calledWithExactly( .calledWithExactly(
"ProjectHistory:Ops:#{@project_id}" "ProjectHistory:Ops:#{@project_id}"
@ops[0] @ops[0]
@ops[1] @ops[1]
@callback ).should.equal true
it "should set the queue timestamp if not present", ->
@multi.setnx
.calledWithExactly(
"ProjectHistory:FirstOpTimestamp:#{@project_id}"
Date.now()
).should.equal true ).should.equal true
describe "queueRenameEntity", -> describe "queueRenameEntity", ->