mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-05-23 00:06:15 -05:00
Finally fix laggy ladder
This is a major refactor to loginserver.js The only logical change is that instead of batching all requests together, prepreplay/mmr/ladderupdate requests are all batched separately. This means that fast loginserver requests are no longer bottlenecked by slow loginserver requests. It used to be that all loginserver requests took multiple seconds, since 'prepreplay' requests took multiple seconds. Now, 'mmr' and 'updateladder' requests are near instantaneous, since they don't have to worry for 'prepreplay' requests to finish. This is only relevant to the main server, since other servers don't make 'mmr' or 'updateladder' requests, anyway. In addition, the weird LoginServer.loginServers system was replaced with a way-more-straightforward arrangement like Tools, where LoginServer is the main instance and LoginServer.prepreplayInstance is the 'prepreplay' instance. This doesn't actually affect the API (the rest of the code only ever uses LoginServer.request), but it makes debugging with /eval a LOT easier, and in general makes the code much more readable. LoginServer now uses ES6 classes.
This commit is contained in:
parent
83ab0f7837
commit
d208c22221
147
loginserver.js
147
loginserver.js
|
|
@ -9,75 +9,48 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const LOGIN_SERVER_TIMEOUT = 15000;
|
||||
const LOGIN_SERVER_TIMEOUT = 30000;
|
||||
const LOGIN_SERVER_BATCH_TIME = 1000;
|
||||
|
||||
const http = require("http");
|
||||
const url = require('url');
|
||||
|
||||
let LoginServer = module.exports = (function () {
|
||||
function LoginServer(uri) {
|
||||
console.log('Creating LoginServer object for ' + uri + '...');
|
||||
this.uri = uri;
|
||||
let TimeoutError = function (message) {
|
||||
Error.captureStackTrace(this, TimeoutError);
|
||||
this.name = "TimeoutError";
|
||||
this.message = message || "";
|
||||
};
|
||||
TimeoutError.prototype = Object.create(Error.prototype);
|
||||
TimeoutError.prototype.constructor = TimeoutError;
|
||||
TimeoutError.prototype.toString = function () {
|
||||
if (!this.message) return this.name;
|
||||
return this.name + ": " + this.message;
|
||||
};
|
||||
|
||||
function parseJSON(json) {
|
||||
if (json[0] === ']') json = json.substr(1);
|
||||
let data = {error: null};
|
||||
try {
|
||||
data.json = JSON.parse(json);
|
||||
} catch (err) {
|
||||
data.error = err;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
class LoginServerInstance {
|
||||
constructor() {
|
||||
this.uri = Config.loginserver;
|
||||
this.requestQueue = [];
|
||||
LoginServer.loginServers[this.uri] = this;
|
||||
|
||||
this.requestTimer = null;
|
||||
this.requestTimeoutTimer = null;
|
||||
this.requestLog = '';
|
||||
this.lastRequest = 0;
|
||||
this.openRequests = 0;
|
||||
}
|
||||
|
||||
// "static" mapping of URIs to LoginServer objects
|
||||
LoginServer.loginServers = {};
|
||||
|
||||
// "static" flag
|
||||
LoginServer.disabled = false;
|
||||
|
||||
LoginServer.prototype.requestTimer = null;
|
||||
LoginServer.prototype.requestTimeoutTimer = null;
|
||||
LoginServer.prototype.requestLog = '';
|
||||
LoginServer.prototype.lastRequest = 0;
|
||||
LoginServer.prototype.openRequests = 0;
|
||||
|
||||
let getLoginServer = function (action) {
|
||||
let uri;
|
||||
if (Config.loginservers) {
|
||||
uri = Config.loginservers[action] || Config.loginservers[null];
|
||||
} else {
|
||||
uri = Config.loginserver;
|
||||
}
|
||||
if (!uri) {
|
||||
console.error("ERROR: No login server specified for action: " + action);
|
||||
return;
|
||||
}
|
||||
return LoginServer.loginServers[uri] || new LoginServer(uri);
|
||||
};
|
||||
LoginServer.instantRequest = function (action, data, callback) {
|
||||
return getLoginServer(action).instantRequest(action, data, callback);
|
||||
};
|
||||
LoginServer.request = function (action, data, callback) {
|
||||
return getLoginServer(action).request(action, data, callback);
|
||||
};
|
||||
let TimeoutError = LoginServer.TimeoutError = function (message) {
|
||||
Error.captureStackTrace(this, TimeoutError);
|
||||
this.name = "TimeoutError";
|
||||
this.message = message || "";
|
||||
};
|
||||
TimeoutError.prototype = Object.create(Error.prototype);
|
||||
TimeoutError.prototype.constructor = TimeoutError;
|
||||
TimeoutError.prototype.toString = function () {
|
||||
if (!this.message) return this.name;
|
||||
return this.name + ": " + this.message;
|
||||
};
|
||||
|
||||
let parseJSON = function (json) {
|
||||
if (json[0] === ']') json = json.substr(1);
|
||||
let data = {error: null};
|
||||
try {
|
||||
data.json = JSON.parse(json);
|
||||
} catch (err) {
|
||||
data.error = err;
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
LoginServer.prototype.instantRequest = function (action, data, callback) {
|
||||
instantRequest(action, data, callback) {
|
||||
if (typeof data === 'function') {
|
||||
callback = data;
|
||||
data = null;
|
||||
|
|
@ -114,14 +87,19 @@ let LoginServer = module.exports = (function () {
|
|||
});
|
||||
|
||||
req.end();
|
||||
};
|
||||
LoginServer.prototype.request = function (action, data, callback) {
|
||||
}
|
||||
request(action, data, callback) {
|
||||
// ladderupdate and mmr are the most common actions
|
||||
// prepreplay is also common
|
||||
if (this[action + 'Server']) {
|
||||
return this[action + 'Server'].request(action, data, callback);
|
||||
}
|
||||
if (typeof data === 'function') {
|
||||
callback = data;
|
||||
data = null;
|
||||
}
|
||||
if (typeof callback === 'undefined') callback = function () {};
|
||||
if (LoginServer.disabled) {
|
||||
if (this.disabled) {
|
||||
setImmediate(callback, null, null, new Error("Ladder disabled"));
|
||||
return;
|
||||
}
|
||||
|
|
@ -130,8 +108,8 @@ let LoginServer = module.exports = (function () {
|
|||
data.callback = callback;
|
||||
this.requestQueue.push(data);
|
||||
this.requestTimerPoke();
|
||||
};
|
||||
LoginServer.prototype.requestTimerPoke = function () {
|
||||
}
|
||||
requestTimerPoke() {
|
||||
// "poke" the request timer, i.e. make sure it knows it should make
|
||||
// a request soon
|
||||
|
||||
|
|
@ -139,8 +117,8 @@ let LoginServer = module.exports = (function () {
|
|||
if (this.openRequests || this.requestTimer || !this.requestQueue.length) return;
|
||||
|
||||
this.requestTimer = setTimeout(this.makeRequests.bind(this), LOGIN_SERVER_BATCH_TIME);
|
||||
};
|
||||
LoginServer.prototype.makeRequests = function () {
|
||||
}
|
||||
makeRequests() {
|
||||
this.requestTimer = null;
|
||||
let self = this;
|
||||
let requests = this.requestQueue;
|
||||
|
|
@ -174,7 +152,7 @@ let LoginServer = module.exports = (function () {
|
|||
for (let i = 0, len = requestCallbacks.length; i < len; i++) {
|
||||
setImmediate(requestCallbacks[i], null, null, error);
|
||||
}
|
||||
self.requestEnd();
|
||||
self.requestEnd(error);
|
||||
}.once();
|
||||
|
||||
req = http.request(requestOptions, function onResponse(res) {
|
||||
|
|
@ -222,24 +200,33 @@ let LoginServer = module.exports = (function () {
|
|||
|
||||
req.write(postData);
|
||||
req.end();
|
||||
};
|
||||
LoginServer.prototype.requestStart = function (size) {
|
||||
}
|
||||
requestStart(size) {
|
||||
this.lastRequest = Date.now();
|
||||
this.requestLog += ' | ' + size + ' requests: ';
|
||||
this.requestLog += ' | ' + size + ' rqs: ';
|
||||
this.openRequests++;
|
||||
};
|
||||
LoginServer.prototype.requestEnd = function () {
|
||||
}
|
||||
requestEnd(error) {
|
||||
this.openRequests = 0;
|
||||
this.requestLog += '' + (Date.now() - this.lastRequest).duration();
|
||||
if (error && error instanceof TimeoutError) {
|
||||
this.requestLog += 'TIMEOUT';
|
||||
} else {
|
||||
this.requestLog += '' + ((Date.now() - this.lastRequest) / 1000) + 's';
|
||||
}
|
||||
this.requestLog = this.requestLog.substr(-1000);
|
||||
this.requestTimerPoke();
|
||||
};
|
||||
LoginServer.prototype.getLog = function () {
|
||||
}
|
||||
getLog() {
|
||||
return this.requestLog + (this.lastRequest ? ' (' + (Date.now() - this.lastRequest).duration() + ' since last request)' : '');
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return LoginServer;
|
||||
})();
|
||||
let LoginServer = module.exports = new LoginServerInstance();
|
||||
|
||||
LoginServer.TimeoutError = TimeoutError;
|
||||
|
||||
if (Config.remoteladder) LoginServer.ladderupdateServer = new LoginServerInstance();
|
||||
LoginServer.prepreplayServer = new LoginServerInstance();
|
||||
|
||||
require('fs').watchFile('./config/custom.css', function (curr, prev) {
|
||||
LoginServer.request('invalidatecss', {}, function () {});
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user