Addresses PERFORMANCE-FINDINGS #1, #4, #6 (and #2 in creategroup flow): - Add app/telnet-pool.js: pool of N connections (config.telnet.poolSize, default 4) with wait-queue, lazy reconnect, and respawn on dead socket. - Remove module-level new Telnet() singleton and startup socket dump from app/routes/index.js; switch all mutating endpoints to telnetPool.send(). - Set telnet debug:false; use config.telnet.timeout (default 5s) instead of hardcoded 30000. - Add telnet.poolSize to config/prosody-muc-rest.js. - await doesMucExist in /creategroup so the existence check is no longer dead. Part-of: <http://gitlab.vnc.biz/uxf/prosody-muc-rest/-/merge_requests/3>
100 lines
2.5 KiB
JavaScript
100 lines
2.5 KiB
JavaScript
var Telnet = require('telnet-client');
|
|
|
|
var STATE = { IDLE: 0, BUSY: 1, DEAD: 2 };
|
|
|
|
function TelnetPool(params, size) {
|
|
this.params = params;
|
|
this.size = size;
|
|
this.conns = [];
|
|
this.queue = [];
|
|
for (var i = 0; i < size; i++) {
|
|
this.conns.push(this._spawn('#' + i));
|
|
}
|
|
}
|
|
|
|
TelnetPool.prototype._spawn = function (label) {
|
|
var self = this;
|
|
var conn = new Telnet();
|
|
conn._poolState = STATE.DEAD;
|
|
conn._poolLabel = label;
|
|
conn.connect(self.params).then(function () {
|
|
conn._poolState = STATE.IDLE;
|
|
console.log('[telnet-pool] connected', label);
|
|
self._drain();
|
|
}).catch(function (error) {
|
|
conn._poolState = STATE.DEAD;
|
|
console.log('[telnet-pool] connect error', label, error);
|
|
});
|
|
return conn;
|
|
};
|
|
|
|
TelnetPool.prototype._drain = function () {
|
|
if (this.queue.length === 0) return;
|
|
for (var i = 0; i < this.conns.length; i++) {
|
|
var conn = this.conns[i];
|
|
if (conn._poolState === STATE.IDLE) {
|
|
conn._poolState = STATE.BUSY;
|
|
var resolve = this.queue.shift();
|
|
resolve(conn);
|
|
if (this.queue.length === 0) return;
|
|
}
|
|
}
|
|
};
|
|
|
|
TelnetPool.prototype._acquire = function () {
|
|
var self = this;
|
|
for (var i = 0; i < self.conns.length; i++) {
|
|
var conn = self.conns[i];
|
|
if (conn._poolState === STATE.IDLE) {
|
|
conn._poolState = STATE.BUSY;
|
|
return Promise.resolve(conn);
|
|
}
|
|
}
|
|
return new Promise(function (resolve) {
|
|
self.queue.push(resolve);
|
|
});
|
|
};
|
|
|
|
TelnetPool.prototype._release = function (conn) {
|
|
conn._poolState = STATE.IDLE;
|
|
this._drain();
|
|
};
|
|
|
|
TelnetPool.prototype._ensureConnected = function (conn) {
|
|
var self = this;
|
|
if (conn && conn.socket && conn.socket.writable) {
|
|
return Promise.resolve(conn);
|
|
}
|
|
console.log('[telnet-pool] socket not writable, reconnecting', conn._poolLabel);
|
|
return conn.connect(self.params).then(function () {
|
|
console.log('[telnet-pool] reconnected', conn._poolLabel);
|
|
return conn;
|
|
});
|
|
};
|
|
|
|
TelnetPool.prototype._replace = function (deadConn) {
|
|
var idx = this.conns.indexOf(deadConn);
|
|
if (idx > -1) {
|
|
this.conns[idx] = this._spawn(deadConn._poolLabel);
|
|
}
|
|
};
|
|
|
|
TelnetPool.prototype.send = function (cmd) {
|
|
var self = this;
|
|
return self._acquire().then(function (conn) {
|
|
return self._ensureConnected(conn).then(function () {
|
|
return conn.send(cmd);
|
|
}).then(function (response) {
|
|
self._release(conn);
|
|
return response;
|
|
}).catch(function (error) {
|
|
self._replace(conn);
|
|
throw error;
|
|
});
|
|
});
|
|
};
|
|
|
|
module.exports = function (params, size) {
|
|
return new TelnetPool(params, size);
|
|
};
|