-
Notifications
You must be signed in to change notification settings - Fork 452
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Document.create callback called twice #272
Comments
My thoughts replicated for completeness: I've had a very quick dig, and it looks like it has to do with the combination of
We've got something like this happening:
I don't have a lot of time to think about how best to handle this right now - may be best to chat with @nateps and @ericyhwang in Wednesday's meeting. Potentially best to also move this to a new issue. My first instinct is that when we invoke a callback, we should first dissociate it from the op it was attached to, so that we don't get this repeat call (that or mark it as called, so we don't try to call it again), but I don't know what the ramifications of that behaviour might be. Actually, now thinking about it a bit more, perhaps we should be moving more towards having a light |
Thanks Nick for writing a nice self-contained repro code, and Alec for doing the sleuthing! Looking at the code for In the test code, the first callback submits another op, so at the point when Potential fixI tested having This is the diff in diff --git a/lib/client/doc.js b/lib/client/doc.js
index 95776db..f7a621f 100644
--- a/lib/client/doc.js
+++ b/lib/client/doc.js
@@ -920,9 +920,11 @@ Doc.prototype._hardRollback = function(err) {
};
Doc.prototype._clearInflightOp = function(err) {
- var called = callEach(this.inflightOp.callbacks, err);
-
+ var inflightOp = this.inflightOp;
this.inflightOp = null;
+
+ var called = callEach(inflightOp.callbacks, err);
+
this.flush();
this._emitNothingPending(); I'd still like to check with @nateps on Wednesday, to see if he can think of any subtle issues with clearing |
@ericyhwang I think that seems like a pretty good solution to me. The other thing that suddenly struck me is that this kind of "smells" a bit - we're getting synchronous behaviour leaking into an asynchronous action. Maybe the "real" solution is something like this? diff --git a/lib/client/doc.js b/lib/client/doc.js
index 95776db..26288c4 100644
--- a/lib/client/doc.js
+++ b/lib/client/doc.js
@@ -658,16 +658,18 @@ Doc.prototype._submit = function(op, source, callback) {
if (this.type.normalize) op.op = this.type.normalize(op.op);
}
+ var doc = this;
try {
this._pushOp(op, callback);
this._otApply(op, source);
} catch (error) {
- return this._hardRollback(error);
+ return process.nextTick(function () {
+ doc._hardRollback(error);
+ });
}
// The call to flush is delayed so if submit() is called multiple times
// synchronously, all the ops are combined before being sent to the server.
- var doc = this;
process.nextTick(function() {
doc.flush();
}); |
I've done a bit more digging and there's a related issue we should address. If I use the code I outlined above, the following test case still fails (NB: all other tests pass): it('issue test', function (done) {
var connection = this.backend.connect();
var doc = connection.get('files', '0');
doc.create({ content: 'asdf' }, (error) => {
if (error) return done(error);
var ops = [{ p: 0, d: '0' }];
var op = [{ p: ['content'], t: 'text0', o: ops }];
doc.submitOp(op, (error) => {
expect(error).to.be.ok();
done();
});
});
}); It fails because an error gets emitted outside of the callbacks. This is because we get something like the following:
|
Fixes double-callback issue for chained op submissions where the second op is invalid: #272
Fix for this was just published in [email protected] Closing this out now! |
In the following code, both
error 1
anderror 2
get logged, which looks like the callback todoc.create
is getting called twice, and both times the error isError: Delete component '0' does not match deleted text 'a'
. Some initial analysis done by @alecgibson in #162.The text was updated successfully, but these errors were encountered: