diff --git a/packages/ddp-client/livedata_connection.js b/packages/ddp-client/livedata_connection.js index 331a7cf1d6..cd27c5b2b5 100644 --- a/packages/ddp-client/livedata_connection.js +++ b/packages/ddp-client/livedata_connection.js @@ -299,29 +299,22 @@ var Connection = function (url, options) { if (self._outstandingMethodBlocks.length > 0) { // If there is an outstanding method block, we only care about the first one as that is the // one that could have already sent messages with no response, that are not allowed to retry. - _.each(self._outstandingMethodBlocks[0].methods, function(methodInvoker) { - // If the message wasn't sent or it's allowed to retry, do nothing. + const currentMethodBlock = self._outstandingMethodBlocks[0].methods; + self._outstandingMethodBlocks[0].methods = currentMethodBlock.filter((methodInvoker) => { + + // Methods with 'noRetry' option set are not allowed to re-send after + // recovering dropped connection. if (methodInvoker.sentMessage && methodInvoker.noRetry) { - // The next loop serves to get the index in the current method block of this method. - var currentMethodBlock = self._outstandingMethodBlocks[0].methods; - var loopMethod; - for (var i = 0; i < currentMethodBlock.length; i++) { - loopMethod = currentMethodBlock[i]; - if (loopMethod.methodId === methodInvoker.methodId) { - break; - } - } - - // Remove from current method block. This may leave the block empty, but we - // don't move on to the next block until the callback has been delivered, in - // _outstandingMethodFinished. - currentMethodBlock.splice(i, 1); - // Make sure that the method is told that it failed. methodInvoker.receiveResult(new Meteor.Error('invocation-failed', 'Method invocation might have failed due to dropped connection. ' + 'Failing because `noRetry` option was passed to Meteor.apply.')); } + + // Only keep a method if it wasn't sent or it's allowed to retry. + // This may leave the block empty, but we don't move on to the next + // block until the callback has been delivered, in _outstandingMethodFinished. + return !(methodInvoker.sentMessage && methodInvoker.noRetry); }); } diff --git a/packages/ddp-client/livedata_connection_tests.js b/packages/ddp-client/livedata_connection_tests.js index 28819115b3..7b13f3fd51 100644 --- a/packages/ddp-client/livedata_connection_tests.js +++ b/packages/ddp-client/livedata_connection_tests.js @@ -819,34 +819,48 @@ if (Meteor.isClient) { startAndConnect(test, stream); - var methodCallbackFired = false; - var methodCallbackErrored = false; + var firstMethodCallbackFired = false; + var firstMethodCallbackErrored = false; + var secondMethodCallbackFired = false; + var secondMethodCallbackErrored = false; + // call with noRetry true so that the method should fail to retry on reconnect. conn.apply('do_something', [], {noRetry: true}, function(error) { - methodCallbackFired = true; + firstMethodCallbackFired = true; // failure on reconnect should trigger an error. if (error && error.error === 'invocation-failed') { - methodCallbackErrored = true; + firstMethodCallbackErrored = true; + } + }); + conn.apply('do_something_else', [], {noRetry: true}, function(error) { + secondMethodCallbackFired = true; + // failure on reconnect should trigger an error. + if (error && error.error === 'invocation-failed') { + secondMethodCallbackErrored = true; } }); - //The method has not succeeded yet - test.isFalse(methodCallbackFired); - // reconnect. + // The method has not succeeded yet + test.isFalse(firstMethodCallbackFired); + test.isFalse(secondMethodCallbackFired); + + // send the methods stream.sent.shift(); - // "receive the message" + stream.sent.shift(); + // reconnect stream.reset(); // verify that a reconnect message was sent. testGotMessage(test, stream, makeConnectMessage(SESSION_ID)); - // Make sure that the stream triggers connection. stream.receive({msg: 'connected', session: SESSION_ID + 1}); //The method callback should fire even though the stream has not sent a response. //the callback should have been fired with an error. - test.isTrue(methodCallbackFired); - test.isTrue(methodCallbackErrored); + test.isTrue(firstMethodCallbackFired); + test.isTrue(firstMethodCallbackErrored); + test.isTrue(secondMethodCallbackFired); + test.isTrue(secondMethodCallbackErrored); // verify that the method message was not sent. test.isUndefined(stream.sent.shift());