From 207bb70983c0a4499cf53186853f4a189d3d2e72 Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Thu, 21 Mar 2024 01:53:24 +0300 Subject: [PATCH] [tenant] Add services.CoAuthoring.server.editorStatStorage config param for multitenant server;For bug 66591 --- Common/config/default.json | 9 +- Common/sources/utils.js | 1 - DocService/sources/DocsCoServer.js | 114 +++++---- DocService/sources/canvasservice.js | 4 +- DocService/sources/changes2forgotten.js | 12 +- DocService/sources/editorDataMemory.js | 293 +++++++++++------------- DocService/sources/shutdown.js | 8 +- 7 files changed, 223 insertions(+), 218 deletions(-) diff --git a/Common/config/default.json b/Common/config/default.json index c962d7a8..61ef36ee 100644 --- a/Common/config/default.json +++ b/Common/config/default.json @@ -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 diff --git a/Common/sources/utils.js b/Common/sources/utils.js index f5f2b0f5..ae3eab59 100644 --- a/Common/sources/utils.js +++ b/Common/sources/utils.js @@ -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'); diff --git a/DocService/sources/DocsCoServer.js b/DocService/sources/DocsCoServer.js index 9c870fc0..f834ac25 100644 --- a/DocService/sources/DocsCoServer.js +++ b/DocService/sources/DocsCoServer.js @@ -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 { diff --git a/DocService/sources/canvasservice.js b/DocService/sources/canvasservice.js index cd700d42..755a7aae 100644 --- a/DocService/sources/canvasservice.js +++ b/DocService/sources/canvasservice.js @@ -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 { diff --git a/DocService/sources/changes2forgotten.js b/DocService/sources/changes2forgotten.js index 99fb01aa..ff9e08b2 100644 --- a/DocService/sources/changes2forgotten.js +++ b/DocService/sources/changes2forgotten.js @@ -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(); diff --git a/DocService/sources/editorDataMemory.js b/DocService/sources/editorDataMemory.js index 0951c40b..b6f3ac56 100644 --- a/DocService/sources/editorDataMemory.js +++ b/DocService/sources/editorDataMemory.js @@ -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 +} diff --git a/DocService/sources/shutdown.js b/DocService/sources/shutdown.js index edd92320..d85e9c1d 100644 --- a/DocService/sources/shutdown.js +++ b/DocService/sources/shutdown.js @@ -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');