Skip to content
Browse files

Fix bug where half-completed wait methods could block future method e…

…xecution.
  • Loading branch information...
1 parent 1eec537 commit c1c1fd9b8fa846f6fa72f26e2d8032c9720ce414 @glasser glasser committed Nov 16, 2012
Showing with 83 additions and 9 deletions.
  1. +19 −9 packages/livedata/livedata_connection.js
  2. +64 −0 packages/livedata/livedata_connection_tests.js
View
28 packages/livedata/livedata_connection.js
@@ -210,6 +210,13 @@ Meteor._LivedataConnection = function (url, options) {
// (in either direction) since we were disconnected (TCP being
// sloppy about that.)
+ // If the current block of methods all got their results (but didn't all get
+ // their data visible), discard the empty block now.
+ if (! _.isEmpty(self._outstandingMethodBlocks) &&
+ _.isEmpty(self._outstandingMethodBlocks[0].methods)) {
+ self._outstandingMethodBlocks.shift();
+ }
+
// If an `onReconnect` handler is set, call it first. Go through
// some hoops to ensure that methods that are called from within
// `onReconnect` get executed _before_ ones that were originally
@@ -1055,15 +1062,18 @@ _.extend(Meteor._LivedataConnection.prototype, {
return;
// No methods are outstanding. This should mean that the first block of
- // methods is empty.
- var firstBlock = self._outstandingMethodBlocks.shift();
- if (! _.isEmpty(firstBlock.methods))
- throw new Error("No methods outstanding but nonempty block: " +
- JSON.stringify(firstBlock));
-
- // Send the outstanding methods now in the first block.
- if (!_.isEmpty(self._outstandingMethodBlocks))
- self._sendOutstandingMethods();
+ // methods is empty. (Or it might not exist, if this was a method that
+ // half-finished before disconnect/reconnect.)
+ if (! _.isEmpty(self._outstandingMethodBlocks)) {
+ var firstBlock = self._outstandingMethodBlocks.shift();
+ if (! _.isEmpty(firstBlock.methods))
+ throw new Error("No methods outstanding but nonempty block: " +
+ JSON.stringify(firstBlock));
+
+ // Send the outstanding methods now in the first block.
+ if (!_.isEmpty(self._outstandingMethodBlocks))
+ self._sendOutstandingMethods();
+ }
// Maybe accept a hot code push.
self._maybeMigrate();
View
64 packages/livedata/livedata_connection_tests.js
@@ -1051,6 +1051,70 @@ Tinytest.add("livedata connection - onReconnect prepends messages correctly with
]);
});
+Tinytest.add("livedata stub - reconnect double wait method", function (test) {
+ var stream = new Meteor._StubStream;
+ var conn = newConnection(stream);
+ startAndConnect(test, stream);
+
+ var output = [];
+ conn.onReconnect = function () {
+ conn.apply('reconnectMethod', [], {wait: true}, function (err, result) {
+ output.push('reconnect');
+ });
+ };
+
+ conn.apply('halfwayMethod', [], {wait: true}, function (err, result) {
+ output.push('halfway');
+ });
+
+ test.equal(output, []);
+ // Method sent.
+ var halfwayId = testGotMessage(
+ test, stream, {msg: 'method', method: 'halfwayMethod',
+ params: [], id: '*'});
+ test.equal(stream.sent.length, 0);
+
+ // Get the result. This means it will not be resent.
+ stream.receive({msg: 'result', id: halfwayId, result: 'bla'});
+ // Callback not called.
+ test.equal(output, []);
+
+ // Reset stream. halfwayMethod does NOT get resent, but reconnectMethod does!
+ // Reconnect quiescence happens when reconnectMethod is done.
+ stream.reset();
+ testGotMessage(test, stream, {msg: 'connect', session: SESSION_ID});
+ var reconnectId = testGotMessage(
+ test, stream, {msg: 'method', method: 'reconnectMethod',
+ params: [], id: '*'});
+ test.length(stream.sent, 0);
+ // Still holding out hope for session resumption, so no callbacks yet.
+ test.equal(output, []);
+
+ // Receive 'connected', but reconnect quiescence is blocking on
+ // reconnectMethod.
+ stream.receive({msg: 'connected', session: SESSION_ID + 1});
+ test.equal(output, []);
+
+ // Data-done for reconnectMethod. This gets us to reconnect quiescence, so
+ // halfwayMethod's callback fires. reconnectMethod's is still waiting on its
+ // result.
+ stream.receive({msg: 'data', methods: [reconnectId]});
+ test.equal(output.shift(), 'halfway');
+ test.equal(output, []);
+
+ // Get result of reconnectMethod. Its callback fires.
+ stream.receive({msg: 'result', id: reconnectId, result: 'foo'});
+ test.equal(output.shift(), 'reconnect');
+ test.equal(output, []);
+
+ // Call another method. It should be delivered immediately. This is a
+ // regression test for a case where it never got delivered because there was
+ // an empty block in _outstandingMethodBlocks blocking it from being sent.
+ conn.call('lastMethod');
+ testGotMessage(test, stream,
+ {msg: 'method', method: 'lastMethod', params: [], id: '*'});
+});
+
// XXX also test:
// - reconnect, with session resume.
// - restart on update flag

0 comments on commit c1c1fd9

Please sign in to comment.
Something went wrong with that request. Please try again.