1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-08-18 05:42:00 +03:00

Implement a keep-alive timer for /sync requests

When a /sync request fails, we spin up a keep-alive poll to /_matrix/client/r0
which 400s. We treat any HTTP response code as a success for the purposes of
polling the server. When a successful poll is done, we shoot the current /sync
request in the head immediately (via a hacky abort() on the promise) and retry
the /sync.
This commit is contained in:
Kegan Dougal
2016-01-21 17:17:27 +00:00
parent ff990914b2
commit 197144dcda
2 changed files with 55 additions and 4 deletions

View File

@@ -43,6 +43,8 @@ module.exports.PREFIX_V2_ALPHA = "/_matrix/client/v2_alpha";
*/ */
module.exports.PREFIX_IDENTITY_V1 = "/_matrix/identity/api/v1"; module.exports.PREFIX_IDENTITY_V1 = "/_matrix/identity/api/v1";
module.exports.PREFIX_R0 = "/_matrix/client/r0";
/** /**
* Construct a MatrixHttpApi. * Construct a MatrixHttpApi.
* @constructor * @constructor
@@ -416,8 +418,10 @@ module.exports.MatrixHttpApi.prototype = {
}, localTimeoutMs); }, localTimeoutMs);
} }
var reqPromise = defer.promise;
try { try {
this.opts.request( var req = this.opts.request(
{ {
uri: uri, uri: uri,
method: method, method: method,
@@ -425,6 +429,7 @@ module.exports.MatrixHttpApi.prototype = {
qs: queryParams, qs: queryParams,
body: data, body: data,
json: true, json: true,
timeout: localTimeoutMs,
_matrix_opts: this.opts _matrix_opts: this.opts
}, },
function(err, response, body) { function(err, response, body) {
@@ -438,6 +443,9 @@ module.exports.MatrixHttpApi.prototype = {
handlerFn(err, response, body); handlerFn(err, response, body);
} }
); );
// FIXME: This is EVIL, but I can't think of a better way to expose
// abort() operations on underlying HTTP requests :(
reqPromise.abort = req.abort.bind(req);
} }
catch (ex) { catch (ex) {
defer.reject(ex); defer.reject(ex);
@@ -445,7 +453,7 @@ module.exports.MatrixHttpApi.prototype = {
callback(ex); callback(ex);
} }
} }
return defer.promise; return reqPromise;
} }
}; };

View File

@@ -66,6 +66,8 @@ function SyncApi(client, opts) {
opts.pendingEventOrdering = opts.pendingEventOrdering || "chronological"; opts.pendingEventOrdering = opts.pendingEventOrdering || "chronological";
this.opts = opts; this.opts = opts;
this._peekRoomId = null; this._peekRoomId = null;
this._syncConnectionLost = false;
this._currentSyncRequest = null;
} }
/** /**
@@ -348,10 +350,13 @@ SyncApi.prototype._sync = function(syncOptions, attempt) {
// normal timeout= plus buffer time // normal timeout= plus buffer time
var clientSideTimeoutMs = this.opts.pollTimeout + BUFFER_PERIOD_MS; var clientSideTimeoutMs = this.opts.pollTimeout + BUFFER_PERIOD_MS;
client._http.authedRequestWithPrefix( this._currentSyncRequest = client._http.authedRequestWithPrefix(
undefined, "GET", "/sync", qps, undefined, httpApi.PREFIX_V2_ALPHA, undefined, "GET", "/sync", qps, undefined, httpApi.PREFIX_V2_ALPHA,
clientSideTimeoutMs clientSideTimeoutMs
).done(function(data) { );
this._currentSyncRequest.done(function(data) {
self._syncConnectionLost = false;
// data looks like: // data looks like:
// { // {
// next_batch: $token, // next_batch: $token,
@@ -525,6 +530,21 @@ SyncApi.prototype._sync = function(syncOptions, attempt) {
self._sync(syncOptions); self._sync(syncOptions);
}, function(err) { }, function(err) {
if (!self._syncConnectionLost) {
debuglog("Starting keep-alive");
self._syncConnectionLost = true;
retryPromise(self._pokeKeepAlive.bind(self), 2000).done(function() {
debuglog("Keep-alive successful.");
// blow away the current /sync request if the connection is still
// dead. It may be black-holed.
if (!self._syncConnectionLost) {
return;
}
// kill the current sync request
debuglog("Aborting current /sync.");
self._currentSyncRequest.abort();
});
}
console.error("/sync error (%s attempts): %s", attempt, err); console.error("/sync error (%s attempts): %s", attempt, err);
console.error(err); console.error(err);
attempt += 1; attempt += 1;
@@ -535,6 +555,21 @@ SyncApi.prototype._sync = function(syncOptions, attempt) {
}); });
}; };
/**
* @return {Promise}
*/
SyncApi.prototype._pokeKeepAlive = function() {
return this.client._http.requestWithPrefix(
undefined, "GET", "/", undefined,
undefined, httpApi.PREFIX_R0, 5 * 1000
).catch(function(err) {
if (err.httpStatus > 0) { // we hit the server alright
return q();
}
throw err;
});
};
/** /**
* @param {string} filterName * @param {string} filterName
* @param {Filter} filter * @param {Filter} filter
@@ -714,6 +749,14 @@ function retryTimeMsForAttempt(attempt) {
return Math.pow(2, Math.min(attempt, 5)) * 1000; return Math.pow(2, Math.min(attempt, 5)) * 1000;
} }
function retryPromise(promiseFn, delay) {
delay = delay || 0;
return promiseFn().catch(function(reason) { // if it fails
// retry after waiting the delay time
return q.delay(delay).then(retryPromise.bind(null, promiseFn, delay));
});
}
function startSyncingRetryTimer(client, attempt, fn) { function startSyncingRetryTimer(client, attempt, fn) {
client._syncingRetry = {}; client._syncingRetry = {};
client._syncingRetry.fn = fn; client._syncingRetry.fn = fn;