Move onReset into a method

This commit is contained in:
Sashko Stubailo
2017-11-09 14:34:28 -08:00
parent 9095c7e2d6
commit 90f95641cf

View File

@@ -15,7 +15,6 @@ if (Meteor.isServer) {
var Future = Npm.require('fibers/future');
}
class MongoIDMap extends IdMap {
constructor() {
super(MongoID.idStringify, MongoID.idParse);
@@ -248,93 +247,6 @@ export class Connection {
});
}
var onReset = function() {
// Send a connect message at the beginning of the stream.
// NOTE: reset is called even on the first connection, so this is
// the only place we send this message.
var msg = { msg: 'connect' };
if (self._lastSessionId) msg.session = self._lastSessionId;
msg.version = self._versionSuggestion || self._supportedDDPVersions[0];
self._versionSuggestion = msg.version;
msg.support = self._supportedDDPVersions;
self._send(msg);
// Mark non-retry calls as failed. This has to be done early as getting these methods out of the
// current block is pretty important to making sure that quiescence is properly calculated, as
// well as possibly moving on to another useful block.
// Only bother testing if there is an outstandingMethodBlock (there might not be, especially if
// we are connecting for the first time.
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.
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) {
// 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);
}
);
}
// Now, to minimize setup latency, go ahead and blast out all of
// our pending methods ands subscriptions before we've even taken
// the necessary RTT to know if we successfully reconnected. (1)
// They're supposed to be idempotent, and where they are not,
// they can block retry in apply; (2) even if we did reconnect,
// we're not sure what messages might have gotten lost
// (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();
}
// Mark all messages as unsent, they have not yet been sent on this
// connection.
_.each(self._methodInvokers, function(m) {
m.sentMessage = false;
});
// 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
// outstanding (since `onReconnect` is used to re-establish auth
// certificates)
self._callOnReconnectAndSendAppropriateOutstandingMethods();
// add new subscriptions at the end. this way they take effect after
// the handlers and we don't see flicker.
_.each(self._subscriptions, function(sub, id) {
self._send({
msg: 'sub',
id: id,
name: sub.name,
params: sub.params
});
});
};
var onDisconnect = function() {
if (self._heartbeat) {
self._heartbeat.stop();
@@ -345,11 +257,14 @@ export class Connection {
if (Meteor.isServer) {
self._stream.on(
'message',
Meteor.bindEnvironment(this.onMessage.bind(this), 'handling DDP message')
Meteor.bindEnvironment(
this.onMessage.bind(this),
'handling DDP message'
)
);
self._stream.on(
'reset',
Meteor.bindEnvironment(onReset, 'handling DDP reset')
Meteor.bindEnvironment(this.onReset.bind(this), 'handling DDP reset')
);
self._stream.on(
'disconnect',
@@ -357,7 +272,7 @@ export class Connection {
);
} else {
self._stream.on('message', this.onMessage.bind(this));
self._stream.on('reset', onReset);
self._stream.on('reset', this.onReset.bind(this));
self._stream.on('disconnect', onDisconnect);
}
}
@@ -437,7 +352,7 @@ export class Connection {
// onStop with an error callback instead.
_.any(
[lastParam.onReady, lastParam.onError, lastParam.onStop],
(f) => typeof f === 'function'
f => typeof f === 'function'
)
) {
callbacks = params.pop();
@@ -1698,4 +1613,91 @@ export class Connection {
else if (msg.msg === 'error') this._livedata_error(msg);
else Meteor._debug('discarding unknown livedata message type', msg);
}
onReset() {
// Send a connect message at the beginning of the stream.
// NOTE: reset is called even on the first connection, so this is
// the only place we send this message.
var msg = { msg: 'connect' };
if (this._lastSessionId) msg.session = this._lastSessionId;
msg.version = this._versionSuggestion || this._supportedDDPVersions[0];
this._versionSuggestion = msg.version;
msg.support = this._supportedDDPVersions;
this._send(msg);
// Mark non-retry calls as failed. This has to be done early as getting these methods out of the
// current block is pretty important to making sure that quiescence is properly calculated, as
// well as possibly moving on to another useful block.
// Only bother testing if there is an outstandingMethodBlock (there might not be, especially if
// we are connecting for the first time.
if (this._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.
const currentMethodBlock = this._outstandingMethodBlocks[0].methods;
this._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) {
// 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);
}
);
}
// Now, to minimize setup latency, go ahead and blast out all of
// our pending methods ands subscriptions before we've even taken
// the necessary RTT to know if we successfully reconnected. (1)
// They're supposed to be idempotent, and where they are not,
// they can block retry in apply; (2) even if we did reconnect,
// we're not sure what messages might have gotten lost
// (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(this._outstandingMethodBlocks) &&
_.isEmpty(this._outstandingMethodBlocks[0].methods)
) {
this._outstandingMethodBlocks.shift();
}
// Mark all messages as unsent, they have not yet been sent on this
// connection.
_.each(this._methodInvokers, m => {
m.sentMessage = false;
});
// 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
// outstanding (since `onReconnect` is used to re-establish auth
// certificates)
this._callOnReconnectAndSendAppropriateOutstandingMethods();
// add new subscriptions at the end. this way they take effect after
// the handlers and we don't see flicker.
_.each(this._subscriptions, (sub, id) => {
this._send({
msg: 'sub',
id: id,
name: sub.name,
params: sub.params
});
});
}
}