diff --git a/eslint.config.js b/eslint.config.js index 8a95c304..2eca2170 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -9,6 +9,7 @@ import controlStructureSpacing from './eslint/control-structure-spacing.js'; import spaceUnaryOpsWithException from './eslint/space-unary-ops-with-exception.js'; export const rules = { + 'no-invalid-this': 'error', 'no-unused-vars': ['error', { vars: 'all', args: 'after-used', diff --git a/eslint/mandatory.eslint.config.js b/eslint/mandatory.eslint.config.js index a5e4ad21..541edd5a 100644 --- a/eslint/mandatory.eslint.config.js +++ b/eslint/mandatory.eslint.config.js @@ -25,6 +25,7 @@ const mandatoryRules = { 'no-use-before-define': ['error', { 'functions': false, }], + 'no-invalid-this': 'error', }; export default defineConfig([ diff --git a/src/backend/src/CoreModule.js b/src/backend/src/CoreModule.js index a4263939..afd8f82f 100644 --- a/src/backend/src/CoreModule.js +++ b/src/backend/src/CoreModule.js @@ -358,9 +358,6 @@ const install = async ({ context, services, app, useapi, modapi }) => { const { SNSService } = require('./services/SNSService'); services.registerService('sns', SNSService); - const { PerformanceMonitor } = require('./monitor/PerformanceMonitor'); - services.registerService('performance-monitor', PerformanceMonitor); - const { WispService } = require('./services/WispService'); services.registerService('wisp', WispService); // const { AWSSecretsPopulator } = require('./services/AWSSecretsPopulator.js'); diff --git a/src/backend/src/codex/CodeUtil.js b/src/backend/src/codex/CodeUtil.js index fded9ef8..13170855 100644 --- a/src/backend/src/codex/CodeUtil.js +++ b/src/backend/src/codex/CodeUtil.js @@ -42,7 +42,8 @@ class CodeUtil { return async function (...a) { const op = new cls(); - op.self = this; + // eslint-disable-next-line no-invalid-this + op.self = this; // TODO: fix this odd structure, what is this even bound to ? return await op.run(...a); }; } diff --git a/src/backend/src/codex/Sequence.js b/src/backend/src/codex/Sequence.js index 07477fa8..db02ab5c 100644 --- a/src/backend/src/codex/Sequence.js +++ b/src/backend/src/codex/Sequence.js @@ -360,7 +360,8 @@ class Sequence { if ( opt_values && opt_values instanceof Sequence.SequenceState ) { opt_values = opt_values.scope_; } - const state = new Sequence.SequenceState(sequence, this); + // eslint-disable-next-line no-invalid-this + const state = new Sequence.SequenceState(sequence, this); // TODO: fix this odd structure, what is this even bound to ? await state.run(opt_values ?? undefined); return state.last_return_; }; diff --git a/src/backend/src/filesystem/FilesystemService.js b/src/backend/src/filesystem/FilesystemService.js index f7857f5b..0f637132 100644 --- a/src/backend/src/filesystem/FilesystemService.js +++ b/src/backend/src/filesystem/FilesystemService.js @@ -243,8 +243,6 @@ class FilesystemService extends BaseService { } async update_child_paths (old_path, new_path, user_id) { - const svc_performanceMonitor = this.services.get('performance-monitor'); - const monitor = svc_performanceMonitor.createContext('update_child_paths'); if ( ! old_path.endsWith('/') ) old_path += '/'; if ( ! new_path.endsWith('/') ) new_path += '/'; @@ -255,7 +253,6 @@ class FilesystemService extends BaseService { const log = this.services.get('log-service').create('update_child_paths'); log.debug(`updated ${old_path} -> ${new_path}`); - monitor.end(); } /** diff --git a/src/backend/src/helpers.js b/src/backend/src/helpers.js index 7580f971..078be44c 100644 --- a/src/backend/src/helpers.js +++ b/src/backend/src/helpers.js @@ -24,10 +24,10 @@ const { ManagedError } = require('./util/errorutil.js'); const { spanify } = require('./util/otelutil.js'); const APIError = require('./api/APIError.js'); const { DB_READ, DB_WRITE } = require('./services/database/consts.js'); -const { BaseDatabaseAccessService } = require('./services/database/BaseDatabaseAccessService.js'); const { Context } = require('./util/context'); const { NodeUIDSelector } = require('./filesystem/node/selectors'); const { object_returned_by_get_app } = require('./annotatedobjects.js'); +const { kv } = require('./util/kvSingleton'); let services = null; const tmp_provide_services = async ss => { @@ -1314,8 +1314,6 @@ function seconds_to_string (seconds) { * @param {*} options */ async function suggest_app_for_fsentry (fsentry, options) { - const svc_performanceMonitor = services.get('performance-monitor'); - const monitor = svc_performanceMonitor.createContext('suggest_app_for_fsentry'); const suggested_apps_promises = []; let content_type = mime.contentType(fsentry.name); @@ -1470,7 +1468,6 @@ async function suggest_app_for_fsentry (fsentry, options) { //--------------------------------------------- const apps = kv.get(`assocs:${file_extension.slice(1)}:apps`) ?? []; - monitor.label('third party associations'); for ( const app_id of apps ) { suggested_apps_promises.push((async () => { // retrieve app from DB @@ -1484,8 +1481,6 @@ async function suggest_app_for_fsentry (fsentry, options) { } })()); } - monitor.stamp(); - monitor.end(); // return list const suggested_apps = await Promise.all(suggested_apps_promises); diff --git a/src/backend/src/modules/core/LogService.js b/src/backend/src/modules/core/LogService.js index 82ae9f77..221b90e9 100644 --- a/src/backend/src/modules/core/LogService.js +++ b/src/backend/src/modules/core/LogService.js @@ -480,31 +480,22 @@ class LogService extends BaseService { new winston.transports.DailyRotateFile({ filename: `${this.log_directory}/%DATE%.log`, datePattern: 'YYYY-MM-DD', - zippedArchive: true, maxSize: '20m', - - // TODO: uncomment when we have a log backup strategy - // maxFiles: '14d', + maxFiles: '14d', }), new winston.transports.DailyRotateFile({ level: 'error', filename: `${this.log_directory}/error-%DATE%.log`, datePattern: 'YYYY-MM-DD', - zippedArchive: true, maxSize: '20m', - - // TODO: uncomment when we have a log backup strategy - // maxFiles: '14d', + maxFiles: '14d', }), new winston.transports.DailyRotateFile({ level: 'system', filename: `${this.log_directory}/system-%DATE%.log`, datePattern: 'YYYY-MM-DD', - zippedArchive: true, maxSize: '20m', - - // TODO: uncomment when we have a log backup strategy - // maxFiles: '14d', + maxFiles: '14d', }), ], })); diff --git a/src/backend/src/monitor/PerformanceMonitor.js b/src/backend/src/monitor/PerformanceMonitor.js deleted file mode 100644 index 6b970068..00000000 --- a/src/backend/src/monitor/PerformanceMonitor.js +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2024-present Puter Technologies Inc. - * - * This file is part of Puter. - * - * Puter is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -const config = require('../config'); -const BaseService = require('../services/BaseService'); -const { CloudWatchClient } = require('@aws-sdk/client-cloudwatch'); - -class Metric { - constructor (windowSize) { - this.count = 0; - this.cumulative = 0; - this.window = []; - - this.WINDOW_SIZE = windowSize; - } - - pushValue (v) { - this.window.push(v); - this.cumulative += v; - this.count++; - this.update_(); - } - - update_ () { - while ( this.window.length >= this.WINDOW_SIZE ) { - this.window.shift(); - } - - this.windowAverage = this.window.reduce((sum, v) => sum + v) - / this.window.length; - this.cumulativeAverage = this.cumulative / this.count; - } - - getCloudwatchMetrics (prefix) { - const metrics = []; - if ( this.count === 0 ) return []; - - const Timestamp = Math.floor(Date.now() / 1000); - - const Dimensions = [ - { - Name: 'server-id', - Value: config.server_id || 'unknown', - }, - { - Name: 'environment', - Value: config.env || 'unknown', - }, - ]; - - if ( this.cumulativeAverage ) { - metrics.push({ - MetricName: `${prefix }.` + 'cumulative-avg', - Value: this.cumulativeAverage, - Timestamp, - Unit: 'Milliseconds', - Dimensions, - }); - } - - if ( this.windowAverage && this.count >= this.WINDOW_SIZE ) { - metrics.push({ - MetricName: `${prefix }.` + 'window-avg', - Value: this.windowAverage, - Timestamp, - Unit: 'Milliseconds', - Dimensions, - }); - } - - return metrics; - } -} - -class PerformanceMonitorContext { - constructor ({ performanceMonitor, name }) { - this.performanceMonitor = performanceMonitor; - this.name = name; - this.stamps = []; - this.children = []; - - this.stamp('monitor-created'); - } - - branch () { - } - - stamp (name) { - if ( ! name ) { - this.stamps[this.stamps.length - 1].end = Date.now(); - return; - } - this.stamps.push({ - name, - ts: Date.now(), - }); - } - - label (name) { - this.stamps.push({ - name, - start: Date.now(), - }); - } - - end () { - this.stamp('end'); - this.performanceMonitor.logMonitorContext(this); - } -} - -class PerformanceMonitor extends BaseService { - static LOG_DEBUG = true; - - _construct () { - this.performanceMetrics = {}; - - this.operationCounts = {}; - this.lastCountPollTS = Date.now(); - } - - _init () { - if ( config.cloudwatch ) { - this.cw = new CloudWatchClient(config.cloudwatch); - } - - if ( config.monitor ) { - this.config = config.monitor; - } - if ( this.config.metricsInterval > 0 ) { - setInterval(async () => { - await this.recordMetrics_(); - }, this.config.metricsInterval); - } - } - - createContext (name) { - return new PerformanceMonitorContext({ - performanceMonitor: this, - name, - }); - } - - logMonitorContext (ctx) { - if ( ! this.performanceMetrics[ctx.name] ) { - this.performanceMetrics[ctx.name] = - new Metric(config.windowSize ?? 30); - } - - const metricsToUpdate = {}; - - // Update averaging metrics - { - const begin = ctx.stamps[0]; - for ( const stamp of ctx.stamps ) { - let start = stamp.start ?? begin.ts; - let end = stamp.end ?? stamp.ts; - metricsToUpdate[stamp.name] = - (metricsToUpdate[stamp.name] ?? 0) + - (end - start); - } - - for ( const name in metricsToUpdate ) { - const value = metricsToUpdate[name]; - this.updateMetric_(`${ctx.name}.${name}`, value); - } - } - - // Update operation counts - { - if ( ! this.operationCounts[ctx.name] ) { - this.operationCounts[ctx.name] = 0; - } - this.operationCounts[ctx.name]++; - } - - if ( ! config.performance_monitors_stdout ) return; - - // Write to stout - { - console.log('[Monitor Snapshot]', ctx.name); - const begin = ctx.stamps[0]; - for ( const stamp of ctx.stamps ) { - let start = stamp.start ?? begin.ts; - let end = stamp.end ?? stamp.ts; - console.log('|', stamp.name, `${end - start }ms`); - } - } - } - - updateMetric_ (key, value) { - const metric = this.performanceMetrics[key] ?? - (this.performanceMetrics[key] = new Metric(30)); - metric.pushValue(value); - } - - async recordMetrics_ () { - this.log.info('recording metrics'); - // Only record metrics of CloudWatch is enabled - if ( ! this.cw ) return; - - const MetricData = []; - - for ( let key in this.performanceMetrics ) { - const prefix = key.replace(/\s+/g, '-'); - const metric = this.performanceMetrics[key]; - - MetricData.push(...metric.getCloudwatchMetrics(prefix)); - } - - const Dimensions = [ - { - Name: 'server-id', - Value: config.server_id || 'unknown', - }, - { - Name: 'environment', - Value: config.env || 'unknown', - }, - ]; - - const ts = Date.now(); - const periodInSeconds = (ts - this.lastCountPollTS) / 1000; - for ( let key in this.operationCounts ) { - const value = this.operationCounts[key] / periodInSeconds; - if ( Number.isNaN(value) ) continue; - const prefix = key.replace(/\s+/g, '-'); - MetricData.push({ - MetricName: `${prefix }.operations`, - Unit: 'Count/Second', - Value: value, - Dimensions, - }); - this.operationCounts[key] = 0; - } - this.lastCountPollTS = ts; - - if ( MetricData.length === 0 ) { - this.log.info('no metrics to record'); - return; - } - - const params = { - Namespace: 'heyputer', - MetricData, - }; - - // console.log( - // JSON.stringify(params, null, ' ') - // ); - - try { - await this.cw.putMetricData(params).promise(); - } catch (e) { - // TODO: alarm condition - console.error('Failed to send metrics to CloudWatch', - e); - } - } -} - -module.exports = { - PerformanceMonitor, -};