[tenant] Add services.CoAuthoring.server.editorStatStorage config param for multitenant server;For bug 66591

This commit is contained in:
Sergey Konovalov
2024-03-21 01:53:24 +03:00
parent 5bf886017d
commit 207bb70983
7 changed files with 223 additions and 218 deletions

View File

@ -162,6 +162,7 @@
"openProtectedFile": true,
"isAnonymousSupport": true,
"editorDataStorage": "editorDataMemory",
"editorStatStorage": "",
"assemblyFormatAsOrigin": true,
"newFileTemplate" : "../../document-templates/new",
"downloadFileAllowExt": ["pdf", "xlsx"],
@ -235,10 +236,14 @@
"port": 6379,
"options": {},
"optionsCluster": {},
"iooptions": {},
"iooptions": {
"lazyConnect": true
},
"iooptionsClusterNodes": [
],
"iooptionsClusterOptions": {}
"iooptionsClusterOptions": {
"lazyConnect": true
}
},
"pubsub": {
"maxChanges": 1000

View File

@ -51,7 +51,6 @@ const NodeCache = require( "node-cache" );
const ms = require('ms');
const constants = require('./constants');
const commonDefines = require('./commondefines');
const logger = require('./logger');
const forwarded = require('forwarded');
const { RequestFilteringHttpAgent, RequestFilteringHttpsAgent } = require("request-filtering-agent");
const openpgp = require('openpgp');

View File

@ -102,7 +102,10 @@ const queueService = require('./../../Common/sources/taskqueueRabbitMQ');
const operationContext = require('./../../Common/sources/operationContext');
const tenantManager = require('./../../Common/sources/tenantManager');
const editorDataStorage = require('./' + config.get('services.CoAuthoring.server.editorDataStorage'));
const cfgEditorDataStorage = config.get('services.CoAuthoring.server.editorDataStorage');
const cfgEditorStatStorage = config.get('services.CoAuthoring.server.editorStatStorage');
const editorDataStorage = require('./' + cfgEditorDataStorage);
const editorStatStorage = require('./' + (cfgEditorStatStorage || cfgEditorDataStorage));
const cfgEditSingleton = config.get('services.CoAuthoring.server.edit_singleton');
const cfgEditor = config.get('services.CoAuthoring.editor');
@ -152,7 +155,9 @@ const EditorTypes = {
};
const defaultHttpPort = 80, defaultHttpsPort = 443; // Default ports (for http and https)
const editorData = new editorDataStorage();
//todo remove editorDataStorage constructor usage after 8.1
const editorData = editorDataStorage.EditorData ? new editorDataStorage.EditorData() : new editorDataStorage();
const editorStat = editorStatStorage.EditorStat ? new editorStatStorage.EditorStat() : new editorDataStorage();
const clientStatsD = statsDClient.getClient();
let connections = []; // Active connections
let lockDocumentsTimerId = {};//to drop connection that can't unlockDocument
@ -435,30 +440,30 @@ function updatePresenceCounters(ctx, conn, val) {
//yield ctx.initTenantCache(); //no need.only global config
}
if (utils.isLiveViewer(conn)) {
yield editorData.incrLiveViewerConnectionsCountByShard(ctx, SHARD_ID, val);
yield editorStat.incrLiveViewerConnectionsCountByShard(ctx, SHARD_ID, val);
if (aggregationCtx) {
yield editorData.incrLiveViewerConnectionsCountByShard(aggregationCtx, SHARD_ID, val);
yield editorStat.incrLiveViewerConnectionsCountByShard(aggregationCtx, SHARD_ID, val);
}
if (clientStatsD) {
let countLiveView = yield editorData.getLiveViewerConnectionsCount(ctx, connections);
let countLiveView = yield editorStat.getLiveViewerConnectionsCount(ctx, connections);
clientStatsD.gauge('expireDoc.connections.liveview', countLiveView);
}
} else if (conn.isCloseCoAuthoring || (conn.user && conn.user.view)) {
yield editorData.incrViewerConnectionsCountByShard(ctx, SHARD_ID, val);
yield editorStat.incrViewerConnectionsCountByShard(ctx, SHARD_ID, val);
if (aggregationCtx) {
yield editorData.incrViewerConnectionsCountByShard(aggregationCtx, SHARD_ID, val);
yield editorStat.incrViewerConnectionsCountByShard(aggregationCtx, SHARD_ID, val);
}
if (clientStatsD) {
let countView = yield editorData.getViewerConnectionsCount(ctx, connections);
let countView = yield editorStat.getViewerConnectionsCount(ctx, connections);
clientStatsD.gauge('expireDoc.connections.view', countView);
}
} else {
yield editorData.incrEditorConnectionsCountByShard(ctx, SHARD_ID, val);
yield editorStat.incrEditorConnectionsCountByShard(ctx, SHARD_ID, val);
if (aggregationCtx) {
yield editorData.incrEditorConnectionsCountByShard(aggregationCtx, SHARD_ID, val);
yield editorStat.incrEditorConnectionsCountByShard(aggregationCtx, SHARD_ID, val);
}
if (clientStatsD) {
let countEditors = yield editorData.getEditorConnectionsCount(ctx, connections);
let countEditors = yield editorStat.getEditorConnectionsCount(ctx, connections);
clientStatsD.gauge('expireDoc.connections.edit', countEditors);
}
}
@ -625,11 +630,11 @@ function* updateEditUsers(ctx, licenseInfo, userId, anonym, isLiveViewer) {
licenseInfo.usersExpire - 1;
let period = utils.getLicensePeriod(licenseInfo.startDate, now);
if (isLiveViewer) {
yield editorData.addPresenceUniqueViewUser(ctx, userId, expireAt, {anonym: anonym});
yield editorData.addPresenceUniqueViewUsersOfMonth(ctx, userId, period, {anonym: anonym, firstOpenDate: now.toISOString()});
yield editorStat.addPresenceUniqueViewUser(ctx, userId, expireAt, {anonym: anonym});
yield editorStat.addPresenceUniqueViewUsersOfMonth(ctx, userId, period, {anonym: anonym, firstOpenDate: now.toISOString()});
} else {
yield editorData.addPresenceUniqueUser(ctx, userId, expireAt, {anonym: anonym});
yield editorData.addPresenceUniqueUsersOfMonth(ctx, userId, period, {anonym: anonym, firstOpenDate: now.toISOString()});
yield editorStat.addPresenceUniqueUser(ctx, userId, expireAt, {anonym: anonym});
yield editorStat.addPresenceUniqueUsersOfMonth(ctx, userId, period, {anonym: anonym, firstOpenDate: now.toISOString()});
}
}
function* getEditorsCount(ctx, docId, opt_hvals) {
@ -1474,6 +1479,7 @@ function getOpenFormatByEditor(editorType) {
exports.c_oAscServerStatus = c_oAscServerStatus;
exports.editorData = editorData;
exports.editorStat = editorStat;
exports.sendData = sendData;
exports.modifyConnectionForPassword = modifyConnectionForPassword;
exports.parseUrl = parseUrl;
@ -3481,13 +3487,13 @@ exports.install = function(server, callbackFunction) {
if (licenseInfo.usersCount) {
const nowUTC = getLicenseNowUtc();
if(isLiveViewer) {
const arrUsers = yield editorData.getPresenceUniqueViewUser(ctx, nowUTC);
const arrUsers = yield editorStat.getPresenceUniqueViewUser(ctx, nowUTC);
if (arrUsers.length >= licenseInfo.usersViewCount && (-1 === arrUsers.findIndex((element) => {return element.userid === userId}))) {
licenseType = c_LR.UsersViewCount;
}
licenseWarningLimitUsersView = licenseInfo.usersViewCount * tenWarningLimitPercents <= arrUsers.length;
} else {
const arrUsers = yield editorData.getPresenceUniqueUser(ctx, nowUTC);
const arrUsers = yield editorStat.getPresenceUniqueUser(ctx, nowUTC);
if (arrUsers.length >= licenseInfo.usersCount && (-1 === arrUsers.findIndex((element) => {return element.userid === userId}))) {
licenseType = c_LR.UsersCount;
}
@ -3495,14 +3501,14 @@ exports.install = function(server, callbackFunction) {
}
} else if(isLiveViewer) {
const connectionsLiveCount = licenseInfo.connectionsView;
const liveViewerConnectionsCount = yield editorData.getLiveViewerConnectionsCount(ctx, connections);
const liveViewerConnectionsCount = yield editorStat.getLiveViewerConnectionsCount(ctx, connections);
if (liveViewerConnectionsCount >= connectionsLiveCount) {
licenseType = c_LR.ConnectionsLive;
}
licenseWarningLimitConnectionsLive = connectionsLiveCount * tenWarningLimitPercents <= liveViewerConnectionsCount;
} else {
const connectionsCount = licenseInfo.connections;
const editConnectionsCount = yield editorData.getEditorConnectionsCount(ctx, connections);
const editConnectionsCount = yield editorStat.getEditorConnectionsCount(ctx, connections);
if (editConnectionsCount >= connectionsCount) {
licenseType = c_LR.Connections;
}
@ -3698,7 +3704,7 @@ exports.install = function(server, callbackFunction) {
case commonDefines.c_oPublishType.shutdown:
//flag prevent new socket connections and receive data from exist connections
shutdownFlag = data.status;
ctx.logger.warn('start shutdown:%b', shutdownFlag);
ctx.logger.warn('start shutdown:%s', shutdownFlag);
if (shutdownFlag) {
ctx.logger.warn('active connections: %d', connections.length);
//do not stop the server, because sockets and all requests will be unavailable
@ -3766,7 +3772,7 @@ exports.install = function(server, callbackFunction) {
function* collectStats(ctx, countEdit, countLiveView, countView) {
let now = Date.now();
yield editorData.setEditorConnections(ctx, countEdit, countLiveView, countView, now, PRECISION);
yield editorStat.setEditorConnections(ctx, countEdit, countLiveView, countView, now, PRECISION);
}
function expireDoc() {
return co(function* () {
@ -3843,16 +3849,16 @@ exports.install = function(server, callbackFunction) {
ctx.setTenant(tenantId);
let tenant = tenants[tenantId];
yield* collectStats(ctx, tenant.countEditByShard, tenant.countLiveViewByShard, tenant.countViewByShard);
yield editorData.setEditorConnectionsCountByShard(ctx, SHARD_ID, tenant.countEditByShard);
yield editorData.setLiveViewerConnectionsCountByShard(ctx, SHARD_ID, tenant.countLiveViewByShard);
yield editorData.setViewerConnectionsCountByShard(ctx, SHARD_ID, tenant.countViewByShard);
yield editorStat.setEditorConnectionsCountByShard(ctx, SHARD_ID, tenant.countEditByShard);
yield editorStat.setLiveViewerConnectionsCountByShard(ctx, SHARD_ID, tenant.countLiveViewByShard);
yield editorStat.setViewerConnectionsCountByShard(ctx, SHARD_ID, tenant.countViewByShard);
if (clientStatsD) {
//todo with multitenant
let countEdit = yield editorData.getEditorConnectionsCount(ctx, connections);
let countEdit = yield editorStat.getEditorConnectionsCount(ctx, connections);
clientStatsD.gauge('expireDoc.connections.edit', countEdit);
let countLiveView = yield editorData.getLiveViewerConnectionsCount(ctx, connections);
let countLiveView = yield editorStat.getLiveViewerConnectionsCount(ctx, connections);
clientStatsD.gauge('expireDoc.connections.liveview', countLiveView);
let countView = yield editorData.getViewerConnectionsCount(ctx, connections);
let countView = yield editorStat.getViewerConnectionsCount(ctx, connections);
clientStatsD.gauge('expireDoc.connections.view', countView);
}
}
@ -3863,9 +3869,9 @@ exports.install = function(server, callbackFunction) {
aggregationCtx.init(tenantManager.getDefautTenant(), ctx.docId, ctx.userId);
//yield ctx.initTenantCache();//no need
yield* collectStats(aggregationCtx, countEditByShard, countLiveViewByShard, countViewByShard);
yield editorData.setEditorConnectionsCountByShard(aggregationCtx, SHARD_ID, countEditByShard);
yield editorData.setLiveViewerConnectionsCountByShard(aggregationCtx, SHARD_ID, countLiveViewByShard);
yield editorData.setViewerConnectionsCountByShard(aggregationCtx, SHARD_ID, countViewByShard);
yield editorStat.setEditorConnectionsCountByShard(aggregationCtx, SHARD_ID, countEditByShard);
yield editorStat.setLiveViewerConnectionsCountByShard(aggregationCtx, SHARD_ID, countLiveViewByShard);
yield editorStat.setViewerConnectionsCountByShard(aggregationCtx, SHARD_ID, countViewByShard);
}
ctx.initDefault();
} catch (err) {
@ -3945,11 +3951,16 @@ exports.install = function(server, callbackFunction) {
}
});
if (-1 !== index || 0 === res.length) {
return editorData.connect().then(function() {
callbackFunction();
}).catch(err => {
operationContext.global.logger.error('editorData error: %s', err.stack);
});
return editorData.connect()
.then(function () {
return editorStat.connect();
})
.then(function () {
callbackFunction();
})
.catch(err => {
operationContext.global.logger.error('editorData error: %s', err.stack);
});
} else {
operationContext.global.logger.error('DB table "%s" does not contain %s column, columns info: %j', tableName, tableRequiredColumn, res);
}
@ -3974,13 +3985,18 @@ exports.healthCheck = function(req, res) {
yield sqlBase.healthCheck(ctx);
ctx.logger.debug('healthCheck database');
//check redis connection
if (editorData.isConnected()) {
yield editorData.ping();
const healthData = yield editorData.healthCheck();
if (healthData) {
ctx.logger.debug('healthCheck editorData');
} else {
throw new Error('redis disconnected');
throw new Error('editorData');
}
const healthStat = yield editorStat.healthCheck();
if (healthStat) {
ctx.logger.debug('healthCheck editorStat');
} else {
throw new Error('editorStat');
}
const healthPubsub = yield pubsub.healthCheck();
if (healthPubsub) {
ctx.logger.debug('healthCheck pubsub');
@ -4068,7 +4084,7 @@ exports.licenseInfo = function(req, res) {
view: {min: 0, avr: 0, max: 0}
};
}
var redisRes = yield editorData.getEditorConnections(ctx);
var redisRes = yield editorStat.getEditorConnections(ctx);
const now = Date.now();
if (redisRes.length > 0) {
let expDocumentsStep95 = expDocumentsStep * 0.95;
@ -4129,8 +4145,8 @@ exports.licenseInfo = function(req, res) {
}
const nowUTC = getLicenseNowUtc();
let execRes;
execRes = yield editorData.getPresenceUniqueUser(ctx, nowUTC);
output.quota.edit.connectionsCount = yield editorData.getEditorConnectionsCount(ctx, connections);
execRes = yield editorStat.getPresenceUniqueUser(ctx, nowUTC);
output.quota.edit.connectionsCount = yield editorStat.getEditorConnectionsCount(ctx, connections);
output.quota.edit.usersCount.unique = execRes.length;
execRes.forEach(function(elem) {
if (elem.anonym) {
@ -4138,8 +4154,8 @@ exports.licenseInfo = function(req, res) {
}
});
execRes = yield editorData.getPresenceUniqueViewUser(ctx, nowUTC);
output.quota.view.connectionsCount = yield editorData.getLiveViewerConnectionsCount(ctx, connections);
execRes = yield editorStat.getPresenceUniqueViewUser(ctx, nowUTC);
output.quota.view.connectionsCount = yield editorStat.getLiveViewerConnectionsCount(ctx, connections);
output.quota.view.usersCount.unique = execRes.length;
execRes.forEach(function(elem) {
if (elem.anonym) {
@ -4147,8 +4163,8 @@ exports.licenseInfo = function(req, res) {
}
});
let byMonth = yield editorData.getPresenceUniqueUsersOfMonth(ctx);
let byMonthView = yield editorData.getPresenceUniqueViewUsersOfMonth(ctx);
let byMonth = yield editorStat.getPresenceUniqueUsersOfMonth(ctx);
let byMonthView = yield editorStat.getPresenceUniqueViewUsersOfMonth(ctx);
let byMonthMerged = [];
for (let i in byMonth) {
if (byMonth.hasOwnProperty(i)) {
@ -4230,8 +4246,8 @@ function* findForgottenFile(ctx, docId) {
function* commandLicense(ctx) {
const nowUTC = getLicenseNowUtc();
const users = yield editorData.getPresenceUniqueUser(ctx, nowUTC);
const users_view = yield editorData.getPresenceUniqueViewUser(ctx, nowUTC);
const users = yield editorStat.getPresenceUniqueUser(ctx, nowUTC);
const users_view = yield editorStat.getPresenceUniqueViewUser(ctx, nowUTC);
const licenseInfo = yield tenantManager.getTenantLicense(ctx);
return {
@ -4389,7 +4405,7 @@ exports.shutdown = function(req, res) {
ctx.initFromRequest(req);
yield ctx.initTenantCache();
ctx.logger.info('shutdown start');
output = yield shutdown.shutdown(ctx, editorData, req.method === 'PUT');
output = yield shutdown.shutdown(ctx, editorStat, req.method === 'PUT');
} catch (err) {
ctx.logger.error('shutdown error %s', err.stack);
} finally {

View File

@ -1194,7 +1194,7 @@ const commandSfcCallback = co.wrap(function*(ctx, cmd, isSfcm, isEncrypted) {
if ((docsCoServer.getIsShutdown() && !isSfcm) || cmd.getRedisKey()) {
let keyRedis = cmd.getRedisKey() ? cmd.getRedisKey() : redisKeyShutdown;
yield docsCoServer.editorData.removeShutdown(keyRedis, docId);
yield docsCoServer.editorStat.removeShutdown(keyRedis, docId);
}
ctx.logger.debug('End commandSfcCallback');
return replyStr;
@ -1720,7 +1720,7 @@ exports.saveFromChanges = function(ctx, docId, statusInfo, optFormat, opt_userId
queueData.setFromChanges(true);
yield* docsCoServer.addTask(queueData, constants.QUEUE_PRIORITY_NORMAL, opt_queue);
if (docsCoServer.getIsShutdown()) {
yield docsCoServer.editorData.addShutdown(redisKeyShutdown, docId);
yield docsCoServer.editorStat.addShutdown(redisKeyShutdown, docId);
}
ctx.logger.debug('AddTask saveFromChanges');
} else {

View File

@ -44,7 +44,9 @@ const operationContext = require('./../../Common/sources/operationContext');
const sqlBase = require('./databaseConnectors/baseConnector');
const docsCoServer = require('./DocsCoServer');
const taskResult = require('./taskresult');
const editorDataStorage = require('./' + config.get('services.CoAuthoring.server.editorDataStorage'));
const cfgEditorDataStorage = config.get('services.CoAuthoring.server.editorDataStorage');
const cfgEditorStatStorage = config.get('services.CoAuthoring.server.editorStatStorage');
const editorStatStorage = require('./' + (cfgEditorStatStorage || cfgEditorDataStorage));
const cfgForgottenFiles = config.get('services.CoAuthoring.server.forgottenfiles');
const cfgTableResult = config.get('services.CoAuthoring.sql.tableResult');
@ -81,7 +83,7 @@ function shutdown() {
var res = true;
let ctx = new operationContext.Context();
try {
let editorData = new editorDataStorage();
let editorStat = editorStatStorage.EditorStat ? new editorStatStorage.EditorStat() : new editorStatStorage();
ctx.logger.debug('shutdown start:' + EXEC_TIMEOUT);
//redisKeyShutdown is not a simple counter, so it doesn't get decremented by a build that started before Shutdown started
@ -130,7 +132,7 @@ function shutdown() {
yield ctx.initTenantCache();
yield updateDoc(ctx, docId, commonDefines.FileStatus.Ok, "");
yield editorData.addShutdown(redisKeyShutdown, docId);
yield editorStat.addShutdown(redisKeyShutdown, docId);
ctx.logger.debug('shutdown createSaveTimerPromise %s', docId);
yield docsCoServer.createSaveTimer(ctx, docId, null, null, queue, true);
}
@ -140,7 +142,7 @@ function shutdown() {
let startTime = new Date().getTime();
while (true) {
let remainingFiles = yield editorData.getShutdownCount(redisKeyShutdown);
let remainingFiles = yield editorStat.getShutdownCount(redisKeyShutdown);
ctx.logger.debug('shutdown remaining files:%d', remainingFiles);
let curTime = new Date().getTime() - startTime;
if (curTime >= EXEC_TIMEOUT || remainingFiles <= 0) {
@ -169,7 +171,7 @@ function shutdown() {
//todo needs to check queues, because there may be long conversions running before Shutdown
//clean up
yield editorData.cleanupShutdown(redisKeyShutdown);
yield editorStat.cleanupShutdown(redisKeyShutdown);
yield pubsub.close();
yield queue.close();

View File

@ -39,19 +39,29 @@ const tenantManager = require('./../../Common/sources/tenantManager');
const cfgExpMonthUniqueUsers = ms(config.get('services.CoAuthoring.expire.monthUniqueUsers'));
function EditorCommon() {
}
EditorCommon.prototype.connect = async function () {};
EditorCommon.prototype.isConnected = function() {
return true;
};
EditorCommon.prototype.ping = async function() {return "PONG"};
EditorCommon.prototype.close = async function() {};
EditorCommon.prototype.healthCheck = async function() {
if (this.isConnected()) {
await this.ping();
return true;
}
return false;
};
function EditorData() {
EditorCommon.call(this);
this.data = {};
this.forceSaveTimer = {};
this.uniqueUser = {};
this.uniqueUsersOfMonth = {};
this.uniqueViewUser = {};
this.uniqueViewUsersOfMonth = {};
this.shutdown = {};
this.stat = {};
}
EditorData.prototype.connect = function() {
return Promise.resolve();
};
EditorData.prototype = Object.create(EditorCommon.prototype);
EditorData.prototype.constructor = EditorData;
EditorData.prototype._getDocumentData = function(ctx, docId) {
let tenantData = this.data[ctx.tenant];
if (!tenantData) {
@ -73,7 +83,7 @@ EditorData.prototype._checkAndLock = function(ctx, name, docId, fencingToken, tt
const expireAt = now + ttl * 1000;
data[name] = {fencingToken: fencingToken, expireAt: expireAt};
}
return Promise.resolve(res);
return res;
};
EditorData.prototype._checkAndUnlock = function(ctx, name, docId, fencingToken) {
let data = this._getDocumentData(ctx, docId);
@ -90,106 +100,94 @@ EditorData.prototype._checkAndUnlock = function(ctx, name, docId, fencingToken)
res = commonDefines.c_oAscUnlockRes.Empty;
delete data[name];
}
return Promise.resolve(res);
return res;
};
EditorData.prototype.addPresence = function(ctx, docId, userId, userInfo) {
return Promise.resolve();
};
EditorData.prototype.updatePresence = function(ctx, docId, userId) {
return Promise.resolve();
};
EditorData.prototype.removePresence = function(ctx, docId, userId) {
return Promise.resolve();
};
EditorData.prototype.getPresence = function(ctx, docId, connections) {
EditorData.prototype.addPresence = async function(ctx, docId, userId, userInfo) {};
EditorData.prototype.updatePresence = async function(ctx, docId, userId) {};
EditorData.prototype.removePresence = async function(ctx, docId, userId) {};
EditorData.prototype.getPresence = async function(ctx, docId, connections) {
let hvals = [];
for (let i = 0; i < connections.length; ++i) {
let conn = connections[i];
if (conn.docId === docId && ctx.tenant === tenantManager.getTenantByConnection(ctx, conn)) {
hvals.push(utils.getConnectionInfoStr(conn));
if (connections) {
for (let i = 0; i < connections.length; ++i) {
let conn = connections[i];
if (conn.docId === docId && ctx.tenant === tenantManager.getTenantByConnection(ctx, conn)) {
hvals.push(utils.getConnectionInfoStr(conn));
}
}
}
return Promise.resolve(hvals);
return hvals;
};
EditorData.prototype.lockSave = function(ctx, docId, userId, ttl) {
EditorData.prototype.lockSave = async function(ctx, docId, userId, ttl) {
return this._checkAndLock(ctx, 'lockSave', docId, userId, ttl);
};
EditorData.prototype.unlockSave = function(ctx, docId, userId) {
EditorData.prototype.unlockSave = async function(ctx, docId, userId) {
return this._checkAndUnlock(ctx, 'lockSave', docId, userId);
};
EditorData.prototype.lockAuth = function(ctx, docId, userId, ttl) {
EditorData.prototype.lockAuth = async function(ctx, docId, userId, ttl) {
return this._checkAndLock(ctx, 'lockAuth', docId, userId, ttl);
};
EditorData.prototype.unlockAuth = function(ctx, docId, userId) {
EditorData.prototype.unlockAuth = async function(ctx, docId, userId) {
return this._checkAndUnlock(ctx, 'lockAuth', docId, userId);
};
EditorData.prototype.getDocumentPresenceExpired = function(now) {
return Promise.resolve([]);
};
EditorData.prototype.removePresenceDocument = function(ctx, docId) {
return Promise.resolve();
EditorData.prototype.getDocumentPresenceExpired = async function(now) {
return [];
};
EditorData.prototype.removePresenceDocument = async function(ctx, docId) {};
EditorData.prototype.addLocks = function(ctx, docId, locks) {
EditorData.prototype.addLocks = async function(ctx, docId, locks) {
let data = this._getDocumentData(ctx, docId);
if (!data.locks) {
data.locks = [];
}
data.locks = data.locks.concat(locks);
return Promise.resolve();
};
EditorData.prototype.removeLocks = function(ctx, docId) {
EditorData.prototype.removeLocks = async function(ctx, docId) {
let data = this._getDocumentData(ctx, docId);
data.locks = undefined;
return Promise.resolve();
};
EditorData.prototype.getLocks = function(ctx, docId) {
EditorData.prototype.getLocks = async function(ctx, docId) {
let data = this._getDocumentData(ctx, docId);
return Promise.resolve(data.locks || []);
return data.locks || [];
};
EditorData.prototype.addMessage = function(ctx, docId, msg) {
EditorData.prototype.addMessage = async function(ctx, docId, msg) {
let data = this._getDocumentData(ctx, docId);
if (!data.messages) {
data.messages = [];
}
data.messages.push(msg);
return Promise.resolve();
};
EditorData.prototype.removeMessages = function(ctx, docId) {
EditorData.prototype.removeMessages = async function(ctx, docId) {
let data = this._getDocumentData(ctx, docId);
data.messages = undefined;
return Promise.resolve();
};
EditorData.prototype.getMessages = function(ctx, docId) {
EditorData.prototype.getMessages = async function(ctx, docId) {
let data = this._getDocumentData(ctx, docId);
return Promise.resolve(data.messages || []);
return data.messages || [];
};
EditorData.prototype.setSaved = function(ctx, docId, status) {
EditorData.prototype.setSaved = async function(ctx, docId, status) {
let data = this._getDocumentData(ctx, docId);
data.saved = status;
return Promise.resolve();
};
EditorData.prototype.getdelSaved = function(ctx, docId) {
EditorData.prototype.getdelSaved = async function(ctx, docId) {
let data = this._getDocumentData(ctx, docId);
let res = data.saved;
data.saved = undefined;
return Promise.resolve(res);
data.saved = null;
return res;
};
EditorData.prototype.setForceSave = function(ctx, docId, time, index, baseUrl, changeInfo, convertInfo) {
EditorData.prototype.setForceSave = async function(ctx, docId, time, index, baseUrl, changeInfo, convertInfo) {
let data = this._getDocumentData(ctx, docId);
data.forceSave = {time, index, baseUrl, changeInfo, started: false, ended: false, convertInfo};
return Promise.resolve();
};
EditorData.prototype.getForceSave = function(ctx, docId) {
EditorData.prototype.getForceSave = async function(ctx, docId) {
let data = this._getDocumentData(ctx, docId);
return Promise.resolve(data.forceSave || null);
return data.forceSave || null;
};
EditorData.prototype.checkAndStartForceSave = function(ctx, docId) {
EditorData.prototype.checkAndStartForceSave = async function(ctx, docId) {
let data = this._getDocumentData(ctx, docId);
let res;
if (data.forceSave && !data.forceSave.started) {
@ -197,9 +195,9 @@ EditorData.prototype.checkAndStartForceSave = function(ctx, docId) {
data.forceSave.ended = false;
res = data.forceSave;
}
return Promise.resolve(res);
return res;
};
EditorData.prototype.checkAndSetForceSave = function(ctx, docId, time, index, started, ended, convertInfo) {
EditorData.prototype.checkAndSetForceSave = async function(ctx, docId, time, index, started, ended, convertInfo) {
let data = this._getDocumentData(ctx, docId);
let res;
if (data.forceSave && time === data.forceSave.time && index === data.forceSave.index) {
@ -208,15 +206,14 @@ EditorData.prototype.checkAndSetForceSave = function(ctx, docId, time, index, st
data.forceSave.convertInfo = convertInfo;
res = data.forceSave;
}
return Promise.resolve(res);
return res;
};
EditorData.prototype.removeForceSave = function(ctx, docId) {
EditorData.prototype.removeForceSave = async function(ctx, docId) {
let data = this._getDocumentData(ctx, docId);
data.forceSave = undefined;
return Promise.resolve();
};
EditorData.prototype.cleanDocumentOnExit = function(ctx, docId) {
EditorData.prototype.cleanDocumentOnExit = async function(ctx, docId) {
let tenantData = this.data[ctx.tenant];
if (tenantData) {
delete tenantData[docId];
@ -225,10 +222,9 @@ EditorData.prototype.cleanDocumentOnExit = function(ctx, docId) {
if (tenantTimer) {
delete tenantTimer[docId];
}
return Promise.resolve();
};
EditorData.prototype.addForceSaveTimerNX = function(ctx, docId, expireAt) {
EditorData.prototype.addForceSaveTimerNX = async function(ctx, docId, expireAt) {
let tenantTimer = this.forceSaveTimer[ctx.tenant];
if (!tenantTimer) {
this.forceSaveTimer[ctx.tenant] = tenantTimer = {};
@ -236,9 +232,8 @@ EditorData.prototype.addForceSaveTimerNX = function(ctx, docId, expireAt) {
if (!tenantTimer[docId]) {
tenantTimer[docId] = expireAt;
}
return Promise.resolve();
};
EditorData.prototype.getForceSaveTimer = function(now) {
EditorData.prototype.getForceSaveTimer = async function(now) {
let res = [];
for (let tenant in this.forceSaveTimer) {
if (this.forceSaveTimer.hasOwnProperty(tenant)) {
@ -253,18 +248,29 @@ EditorData.prototype.getForceSaveTimer = function(now) {
}
}
}
return Promise.resolve(res);
return res;
};
EditorData.prototype.addPresenceUniqueUser = function(ctx, userId, expireAt, userInfo) {
function EditorStat() {
EditorCommon.call(this);
this.uniqueUser = {};
this.uniqueUsersOfMonth = {};
this.uniqueViewUser = {};
this.uniqueViewUsersOfMonth = {};
this.stat = {};
this.shutdown = {};
this.license = {};
}
EditorStat.prototype = Object.create(EditorCommon.prototype);
EditorStat.prototype.constructor = EditorStat;
EditorStat.prototype.addPresenceUniqueUser = async function(ctx, userId, expireAt, userInfo) {
let tenantUser = this.uniqueUser[ctx.tenant];
if (!tenantUser) {
this.uniqueUser[ctx.tenant] = tenantUser = {};
}
tenantUser[userId] = {expireAt: expireAt, userInfo: userInfo};
return Promise.resolve();
};
EditorData.prototype.getPresenceUniqueUser = function(ctx, nowUTC) {
EditorStat.prototype.getPresenceUniqueUser = async function(ctx, nowUTC) {
let res = [];
let tenantUser = this.uniqueUser[ctx.tenant];
if (!tenantUser) {
@ -282,9 +288,9 @@ EditorData.prototype.getPresenceUniqueUser = function(ctx, nowUTC) {
}
}
}
return Promise.resolve(res);
return res;
};
EditorData.prototype.addPresenceUniqueUsersOfMonth = function(ctx, userId, period, userInfo) {
EditorStat.prototype.addPresenceUniqueUsersOfMonth = async function(ctx, userId, period, userInfo) {
let tenantUser = this.uniqueUsersOfMonth[ctx.tenant];
if (!tenantUser) {
this.uniqueUsersOfMonth[ctx.tenant] = tenantUser = {};
@ -294,9 +300,8 @@ EditorData.prototype.addPresenceUniqueUsersOfMonth = function(ctx, userId, perio
tenantUser[period] = {expireAt: expireAt, data: {}};
}
tenantUser[period].data[userId] = userInfo;
return Promise.resolve();
};
EditorData.prototype.getPresenceUniqueUsersOfMonth = function(ctx) {
EditorStat.prototype.getPresenceUniqueUsersOfMonth = async function(ctx) {
let res = {};
let nowUTC = Date.now();
let tenantUser = this.uniqueUsersOfMonth[ctx.tenant];
@ -313,18 +318,17 @@ EditorData.prototype.getPresenceUniqueUsersOfMonth = function(ctx) {
}
}
}
return Promise.resolve(res);
return res;
};
EditorData.prototype.addPresenceUniqueViewUser = function(ctx, userId, expireAt, userInfo) {
EditorStat.prototype.addPresenceUniqueViewUser = async function(ctx, userId, expireAt, userInfo) {
let tenantUser = this.uniqueViewUser[ctx.tenant];
if (!tenantUser) {
this.uniqueViewUser[ctx.tenant] = tenantUser = {};
}
tenantUser[userId] = {expireAt: expireAt, userInfo: userInfo};
return Promise.resolve();
};
EditorData.prototype.getPresenceUniqueViewUser = function(ctx, nowUTC) {
EditorStat.prototype.getPresenceUniqueViewUser = async function(ctx, nowUTC) {
let res = [];
let tenantUser = this.uniqueViewUser[ctx.tenant];
if (!tenantUser) {
@ -342,9 +346,9 @@ EditorData.prototype.getPresenceUniqueViewUser = function(ctx, nowUTC) {
}
}
}
return Promise.resolve(res);
return res;
};
EditorData.prototype.addPresenceUniqueViewUsersOfMonth = function(ctx, userId, period, userInfo) {
EditorStat.prototype.addPresenceUniqueViewUsersOfMonth = async function(ctx, userId, period, userInfo) {
let tenantUser = this.uniqueViewUsersOfMonth[ctx.tenant];
if (!tenantUser) {
this.uniqueViewUsersOfMonth[ctx.tenant] = tenantUser = {};
@ -354,9 +358,8 @@ EditorData.prototype.addPresenceUniqueViewUsersOfMonth = function(ctx, userId, p
tenantUser[period] = {expireAt: expireAt, data: {}};
}
tenantUser[period].data[userId] = userInfo;
return Promise.resolve();
};
EditorData.prototype.getPresenceUniqueViewUsersOfMonth = function(ctx) {
EditorStat.prototype.getPresenceUniqueViewUsersOfMonth = async function(ctx) {
let res = {};
let nowUTC = Date.now();
let tenantUser = this.uniqueViewUsersOfMonth[ctx.tenant];
@ -373,10 +376,9 @@ EditorData.prototype.getPresenceUniqueViewUsersOfMonth = function(ctx) {
}
}
}
return Promise.resolve(res);
return res;
};
EditorData.prototype.setEditorConnections = function(ctx, countEdit, countLiveView, countView, now, precision) {
EditorStat.prototype.setEditorConnections = async function(ctx, countEdit, countLiveView, countView, now, precision) {
let tenantStat = this.stat[ctx.tenant];
if (!tenantStat) {
this.stat[ctx.tenant] = tenantStat = [];
@ -387,79 +389,69 @@ EditorData.prototype.setEditorConnections = function(ctx, countEdit, countLiveVi
i++;
}
tenantStat.splice(0, i);
return Promise.resolve();
};
EditorData.prototype.getEditorConnections = function(ctx) {
EditorStat.prototype.getEditorConnections = async function(ctx) {
let tenantStat = this.stat[ctx.tenant];
if (!tenantStat) {
this.stat[ctx.tenant] = tenantStat = [];
}
return Promise.resolve(tenantStat);
return tenantStat;
};
EditorData.prototype.setEditorConnectionsCountByShard = function(ctx, shardId, count) {
return Promise.resolve();
};
EditorData.prototype.incrEditorConnectionsCountByShard = function(ctx, shardId, count) {
return Promise.resolve();
};
EditorData.prototype.getEditorConnectionsCount = function(ctx, connections) {
EditorStat.prototype.setEditorConnectionsCountByShard = async function(ctx, shardId, count) {};
EditorStat.prototype.incrEditorConnectionsCountByShard = async function(ctx, shardId, count) {};
EditorStat.prototype.getEditorConnectionsCount = async function(ctx, connections) {
let count = 0;
for (let i = 0; i < connections.length; ++i) {
let conn = connections[i];
if (!(conn.isCloseCoAuthoring || (conn.user && conn.user.view)) && ctx.tenant === tenantManager.getTenantByConnection(ctx, conn)) {
count++;
if (connections) {
for (let i = 0; i < connections.length; ++i) {
let conn = connections[i];
if (!(conn.isCloseCoAuthoring || (conn.user && conn.user.view)) && ctx.tenant === tenantManager.getTenantByConnection(ctx, conn)) {
count++;
}
}
}
return Promise.resolve(count);
return count;
};
EditorData.prototype.setViewerConnectionsCountByShard = function(ctx, shardId, count) {
return Promise.resolve();
};
EditorData.prototype.incrViewerConnectionsCountByShard = function(ctx, shardId, count) {
return Promise.resolve();
};
EditorData.prototype.getViewerConnectionsCount = function(ctx, connections) {
EditorStat.prototype.setViewerConnectionsCountByShard = async function(ctx, shardId, count) {};
EditorStat.prototype.incrViewerConnectionsCountByShard = async function(ctx, shardId, count) {};
EditorStat.prototype.getViewerConnectionsCount = async function(ctx, connections) {
let count = 0;
for (let i = 0; i < connections.length; ++i) {
let conn = connections[i];
if (conn.isCloseCoAuthoring || (conn.user && conn.user.view) && ctx.tenant === tenantManager.getTenantByConnection(ctx, conn)) {
count++;
if (connections) {
for (let i = 0; i < connections.length; ++i) {
let conn = connections[i];
if (conn.isCloseCoAuthoring || (conn.user && conn.user.view) && ctx.tenant === tenantManager.getTenantByConnection(ctx, conn)) {
count++;
}
}
}
return Promise.resolve(count);
return count;
};
EditorData.prototype.setLiveViewerConnectionsCountByShard = function(ctx, shardId, count) {
return Promise.resolve();
};
EditorData.prototype.incrLiveViewerConnectionsCountByShard = function(ctx, shardId, count) {
return Promise.resolve();
};
EditorData.prototype.getLiveViewerConnectionsCount = function(ctx, connections) {
EditorStat.prototype.setLiveViewerConnectionsCountByShard = async function(ctx, shardId, count) {};
EditorStat.prototype.incrLiveViewerConnectionsCountByShard = async function(ctx, shardId, count) {};
EditorStat.prototype.getLiveViewerConnectionsCount = async function(ctx, connections) {
let count = 0;
for (let i = 0; i < connections.length; ++i) {
let conn = connections[i];
if (utils.isLiveViewer(conn) && ctx.tenant === tenantManager.getTenantByConnection(ctx, conn)) {
count++;
if (connections) {
for (let i = 0; i < connections.length; ++i) {
let conn = connections[i];
if (utils.isLiveViewer(conn) && ctx.tenant === tenantManager.getTenantByConnection(ctx, conn)) {
count++;
}
}
}
return Promise.resolve(count);
return count;
};
EditorData.prototype.addShutdown = function(key, docId) {
EditorStat.prototype.addShutdown = async function(key, docId) {
if (!this.shutdown[key]) {
this.shutdown[key] = {};
}
this.shutdown[key][docId] = 1;
return Promise.resolve();
};
EditorData.prototype.removeShutdown = function(key, docId) {
EditorStat.prototype.removeShutdown = async function(key, docId) {
if (!this.shutdown[key]) {
this.shutdown[key] = {};
}
delete this.shutdown[key][docId];
return Promise.resolve();
};
EditorData.prototype.getShutdownCount = function(key) {
EditorStat.prototype.getShutdownCount = async function(key) {
let count = 0;
if (this.shutdown[key]) {
for (let docId in this.shutdown[key]) {
@ -468,31 +460,22 @@ EditorData.prototype.getShutdownCount = function(key) {
}
}
}
return Promise.resolve(count);
return count;
};
EditorData.prototype.cleanupShutdown = function(key) {
EditorStat.prototype.cleanupShutdown = async function(key) {
delete this.shutdown[key];
return Promise.resolve();
};
EditorStat.prototype.setLicense = async function(key, val) {
this.license[key] = val;
};
EditorStat.prototype.getLicense = async function(key) {
return this.license[key] || null;
};
EditorStat.prototype.removeLicense = async function(key) {
delete this.license[key];
};
EditorData.prototype.setLicense = function(key, val) {
return Promise.resolve();
};
EditorData.prototype.getLicense = function(key) {
return Promise.resolve(null);
};
EditorData.prototype.removeLicense = function(key) {
return Promise.resolve();
};
EditorData.prototype.isConnected = function() {
return true;
};
EditorData.prototype.ping = function() {
return Promise.resolve();
};
EditorData.prototype.close = function() {
return Promise.resolve();
};
module.exports = EditorData;
module.exports = {
EditorData,
EditorStat
}

View File

@ -47,7 +47,7 @@ var WAIT_TIMEOUT = 30000;
var LOOP_TIMEOUT = 1000;
var EXEC_TIMEOUT = WAIT_TIMEOUT + utils.getConvertionTimeout(undefined);
exports.shutdown = function(ctx, editorData, status) {
exports.shutdown = function(ctx, editorStat, status) {
return co(function*() {
var res = true;
try {
@ -55,7 +55,7 @@ exports.shutdown = function(ctx, editorData, status) {
//redisKeyShutdown is not a simple counter, so it doesn't get decremented by a build that started before Shutdown started
//reset redisKeyShutdown just in case the previous run didn't finish
yield editorData.cleanupShutdown(redisKeyShutdown);
yield editorStat.cleanupShutdown(redisKeyShutdown);
var pubsub = new pubsubService();
yield pubsub.initPromise();
@ -76,7 +76,7 @@ exports.shutdown = function(ctx, editorData, status) {
ctx.logger.debug('shutdown timeout');
break;
}
var remainingFiles = yield editorData.getShutdownCount(redisKeyShutdown);
var remainingFiles = yield editorStat.getShutdownCount(redisKeyShutdown);
ctx.logger.debug('shutdown remaining files:%d', remainingFiles);
if (!isStartWait && remainingFiles <= 0) {
break;
@ -85,7 +85,7 @@ exports.shutdown = function(ctx, editorData, status) {
}
//todo need to check the queues, because there may be long conversions running before Shutdown
//clean up
yield editorData.cleanupShutdown(redisKeyShutdown);
yield editorStat.cleanupShutdown(redisKeyShutdown);
yield pubsub.close();
ctx.logger.debug('shutdown end');