Skip to content

Commit 762496a

Browse files
committed
Remove cached ops without using setTimeout
See share#219
1 parent 56b726b commit 762496a

File tree

2 files changed

+41
-33
lines changed

2 files changed

+41
-33
lines changed

lib/client/doc.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ function Doc(connection, collection, id) {
8383
this.requestReplyPresence = true;
8484
// A list of ops sent by the server. These are needed for transforming presence data,
8585
// if we get that presence data for an older version of the document.
86-
// The ops are cached for 1 minute by default, which should be lots, considering that the presence
86+
// The ops are cached for at least 1 minute by default, which should be lots, considering that the presence
8787
// data is supposed to be synced in real-time.
8888
this.cachedOps = [];
8989
this.cachedOpsTimeout = 60000;
@@ -362,6 +362,7 @@ Doc.prototype._handleOp = function(err, message) {
362362

363363
var serverOp = {
364364
src: message.src,
365+
time: Date.now(),
365366
create: !!message.create,
366367
op: message.op,
367368
del: !!message.del
@@ -914,6 +915,7 @@ Doc.prototype._opAcknowledged = function(message) {
914915
this.version++;
915916
this._cacheOp({
916917
src: this.inflightOp.src,
918+
time: Date.now(),
917919
create: !!this.inflightOp.create,
918920
op: this.inflightOp.op,
919921
del: !!this.inflightOp.del
@@ -1283,8 +1285,18 @@ Doc.prototype._emitPresence = function(srcList, submitted) {
12831285
};
12841286

12851287
Doc.prototype._cacheOp = function(op) {
1288+
// Remove the old ops.
1289+
var oldOpTime = Date.now() - this.cachedOpsTimeout;
1290+
var i;
1291+
for (i = 0; i < this.cachedOps.length; i++) {
1292+
if (this.cachedOps[i].time >= oldOpTime) {
1293+
break;
1294+
}
1295+
}
1296+
if (i > 0) {
1297+
this.cachedOps.splice(0, i);
1298+
}
1299+
1300+
// Cache the new op.
12861301
this.cachedOps.push(op);
1287-
setTimeout(function() {
1288-
if (this.cachedOps[0] === op) this.cachedOps.shift();
1289-
}.bind(this), this.cachedOpsTimeout);
12901302
};

test/client/presence.js

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -473,48 +473,44 @@ types.register(presenceType.type3);
473473
], allDone);
474474
});
475475

476-
it('removes cached ops', function(allDone) {
477-
var op = { index: 1, value: 'b' };
478-
this.doc.cachedOpsTimeout = 0;
476+
it('expires cached ops', function(allDone) {
477+
var op1 = { index: 1, value: 'b' };
478+
var op2 = { index: 2, value: 'b' };
479+
var op3 = { index: 3, value: 'b' };
480+
this.doc.cachedOpsTimeout = 60;
479481
async.series([
482+
// Cache 2 ops.
480483
this.doc.create.bind(this.doc, [ 'a' ], typeName),
481-
this.doc.submitOp.bind(this.doc, op),
482-
this.doc.del.bind(this.doc),
484+
this.doc.submitOp.bind(this.doc, op1),
483485
function(done) {
484-
expect(this.doc.cachedOps.length).to.equal(3);
486+
expect(this.doc.cachedOps.length).to.equal(2);
485487
expect(this.doc.cachedOps[0].create).to.equal(true);
486-
expect(this.doc.cachedOps[1].op).to.equal(op);
487-
expect(this.doc.cachedOps[2].del).to.equal(true);
488+
expect(this.doc.cachedOps[1].op).to.equal(op1);
488489
done();
489490
}.bind(this),
490-
setTimeout,
491-
function(done) {
492-
expect(this.doc.cachedOps.length).to.equal(0);
493-
done();
494-
}.bind(this)
495-
], allDone);
496-
});
497491

498-
it('removes correct cached ops', function(allDone) {
499-
var op = { index: 1, value: 'b' };
500-
this.doc.cachedOpsTimeout = 0;
501-
async.series([
502-
this.doc.create.bind(this.doc, [ 'a' ], typeName),
503-
this.doc.submitOp.bind(this.doc, op),
504-
this.doc.del.bind(this.doc),
492+
// Cache another op before the first 2 expire.
493+
function (callback) {
494+
setTimeout(callback, 30);
495+
},
496+
this.doc.submitOp.bind(this.doc, op2),
505497
function(done) {
506498
expect(this.doc.cachedOps.length).to.equal(3);
507499
expect(this.doc.cachedOps[0].create).to.equal(true);
508-
expect(this.doc.cachedOps[1].op).to.equal(op);
509-
expect(this.doc.cachedOps[2].del).to.equal(true);
510-
this.doc.cachedOps.shift();
511-
this.doc.cachedOps.push({ op: true });
500+
expect(this.doc.cachedOps[1].op).to.equal(op1);
501+
expect(this.doc.cachedOps[2].op).to.equal(op2);
512502
done();
513503
}.bind(this),
514-
setTimeout,
504+
505+
// Cache another op after the first 2 expire.
506+
function (callback) {
507+
setTimeout(callback, 31);
508+
},
509+
this.doc.submitOp.bind(this.doc, op3),
515510
function(done) {
516-
expect(this.doc.cachedOps.length).to.equal(1);
517-
expect(this.doc.cachedOps[0].op).to.equal(true);
511+
expect(this.doc.cachedOps.length).to.equal(2);
512+
expect(this.doc.cachedOps[0].op).to.equal(op2);
513+
expect(this.doc.cachedOps[1].op).to.equal(op3);
518514
done();
519515
}.bind(this)
520516
], allDone);

0 commit comments

Comments
 (0)