From d05c0bb6532eef7acf2695918e3da1a9e14ef924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6ran=20Sander?= Date: Tue, 16 Dec 2025 07:28:31 +0100 Subject: [PATCH] refactor(influxdb): Modernized shared InfluxDB code, better sharing of code across InfluxDB versions --- docs/TEST_COVERAGE_SUMMARY.md | 5 ++- src/config/production_template.yaml | 2 +- .../__tests__/v3-butler-memory.test.js | 10 ++--- .../__tests__/v3-event-counts.test.js | 24 +++++------ .../__tests__/v3-health-metrics.test.js | 14 +++---- .../influxdb/__tests__/v3-log-events.test.js | 22 +++++----- .../__tests__/v3-queue-metrics.test.js | 24 ++++++----- .../influxdb/__tests__/v3-sessions.test.js | 4 +- .../__tests__/v3-shared-utils.test.js | 41 ++++++++++++------- .../influxdb/__tests__/v3-user-events.test.js | 20 ++++----- .../v3/__tests__/health-metrics.test.js | 3 -- src/lib/influxdb/v3/butler-memory.js | 8 ++-- src/lib/influxdb/v3/event-counts.js | 20 +++++---- src/lib/influxdb/v3/health-metrics.js | 8 ++-- src/lib/influxdb/v3/log-events.js | 8 ++-- src/lib/influxdb/v3/queue-metrics.js | 14 ++++--- src/lib/influxdb/v3/sessions.js | 8 ++-- src/lib/influxdb/v3/user-events.js | 8 ++-- 18 files changed, 139 insertions(+), 104 deletions(-) delete mode 100644 src/lib/influxdb/v3/__tests__/health-metrics.test.js diff --git a/docs/TEST_COVERAGE_SUMMARY.md b/docs/TEST_COVERAGE_SUMMARY.md index c32be85..1ad854c 100644 --- a/docs/TEST_COVERAGE_SUMMARY.md +++ b/docs/TEST_COVERAGE_SUMMARY.md @@ -17,12 +17,13 @@ Tests for shared utility functions used across v3 implementations. - `getInfluxDbVersion()` - Returns configured InfluxDB version - `useRefactoredInfluxDb()` - Feature flag checking (true/false/undefined) - `isInfluxDbEnabled()` - Validates InfluxDB initialization -- `writeToInfluxV3WithRetry()` - Comprehensive retry logic tests: +- `writeToInfluxWithRetry()` - Comprehensive unified retry logic tests for all InfluxDB versions: - Success on first attempt - Single retry on timeout with success - Multiple retries (2 attempts) before success - Max retries exceeded (throws after all attempts) - - Non-timeout errors throw immediately without retry + - Non-retryable errors throw immediately without retry + - Network error detection (ETIMEDOUT, ECONNREFUSED, etc.) - Timeout detection from error.name - Timeout detection from error message content - Timeout detection from constructor.name diff --git a/src/config/production_template.yaml b/src/config/production_template.yaml index 8af9578..ced0787 100644 --- a/src/config/production_template.yaml +++ b/src/config/production_template.yaml @@ -510,7 +510,7 @@ Butler-SOS: # Items below are mandatory if influxdbConfig.enable=true host: influxdb.mycompany.com # InfluxDB host, hostname, FQDN or IP address port: 8086 # Port where InfluxDBdb is listening, usually 8086 - version: 1 # Is the InfluxDB instance version 1.x or 2.x? Valid values are 1, 2, or 3 + version: 2 # Is the InfluxDB instance version 1.x or 2.x? Valid values are 1, 2, or 3 v3Config: # Settings for InfluxDB v3.x only, i.e. Butler-SOS.influxdbConfig.version=3 database: mydatabase description: Butler SOS metrics diff --git a/src/lib/influxdb/__tests__/v3-butler-memory.test.js b/src/lib/influxdb/__tests__/v3-butler-memory.test.js index e7172b7..997a586 100644 --- a/src/lib/influxdb/__tests__/v3-butler-memory.test.js +++ b/src/lib/influxdb/__tests__/v3-butler-memory.test.js @@ -26,7 +26,7 @@ jest.unstable_mockModule('../../../globals.js', () => ({ // Mock shared utils const mockUtils = { isInfluxDbEnabled: jest.fn(), - writeToInfluxV3WithRetry: jest.fn(), + writeToInfluxWithRetry: jest.fn(), }; jest.unstable_mockModule('../shared/utils.js', () => mockUtils); @@ -58,7 +58,7 @@ describe('v3/butler-memory', () => { // Setup default mocks globals.config.get.mockReturnValue('test-db'); utils.isInfluxDbEnabled.mockReturnValue(true); - utils.writeToInfluxV3WithRetry.mockResolvedValue(); + utils.writeToInfluxWithRetry.mockResolvedValue(); }); describe('postButlerSOSMemoryUsageToInfluxdbV3', () => { @@ -75,7 +75,7 @@ describe('v3/butler-memory', () => { await postButlerSOSMemoryUsageToInfluxdbV3(memory); - expect(utils.writeToInfluxV3WithRetry).not.toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).not.toHaveBeenCalled(); }); test('should successfully write memory usage metrics', async () => { @@ -95,7 +95,7 @@ describe('v3/butler-memory', () => { expect(mockPoint.setFloatField).toHaveBeenCalledWith('heap_total', 200.75); expect(mockPoint.setFloatField).toHaveBeenCalledWith('external', 50.25); expect(mockPoint.setFloatField).toHaveBeenCalledWith('process_memory', 250.5); - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).toHaveBeenCalled(); }); test('should handle write errors', async () => { @@ -108,7 +108,7 @@ describe('v3/butler-memory', () => { }; const writeError = new Error('Write failed'); - utils.writeToInfluxV3WithRetry.mockRejectedValue(writeError); + utils.writeToInfluxWithRetry.mockRejectedValue(writeError); await postButlerSOSMemoryUsageToInfluxdbV3(memory); diff --git a/src/lib/influxdb/__tests__/v3-event-counts.test.js b/src/lib/influxdb/__tests__/v3-event-counts.test.js index df33e7b..ab48b61 100644 --- a/src/lib/influxdb/__tests__/v3-event-counts.test.js +++ b/src/lib/influxdb/__tests__/v3-event-counts.test.js @@ -39,7 +39,7 @@ jest.unstable_mockModule('../../../globals.js', () => ({ // Mock shared utils const mockUtils = { isInfluxDbEnabled: jest.fn(), - writeToInfluxV3WithRetry: jest.fn(), + writeToInfluxWithRetry: jest.fn(), }; jest.unstable_mockModule('../shared/utils.js', () => mockUtils); @@ -82,7 +82,7 @@ describe('v3/event-counts', () => { }); globals.config.has.mockReturnValue(false); utils.isInfluxDbEnabled.mockReturnValue(true); - utils.writeToInfluxV3WithRetry.mockResolvedValue(); + utils.writeToInfluxWithRetry.mockResolvedValue(); }); describe('storeEventCountInfluxDBV3', () => { @@ -95,7 +95,7 @@ describe('v3/event-counts', () => { expect(globals.logger.verbose).toHaveBeenCalledWith( expect.stringContaining('No events to store') ); - expect(utils.writeToInfluxV3WithRetry).not.toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).not.toHaveBeenCalled(); }); test('should return early when InfluxDB is disabled', async () => { @@ -105,7 +105,7 @@ describe('v3/event-counts', () => { await storeEventCountInfluxDBV3(); - expect(utils.writeToInfluxV3WithRetry).not.toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).not.toHaveBeenCalled(); }); test('should store log events successfully', async () => { @@ -128,7 +128,7 @@ describe('v3/event-counts', () => { await storeEventCountInfluxDBV3(); - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalledTimes(2); + expect(utils.writeToInfluxWithRetry).toHaveBeenCalledTimes(2); expect(mockPoint.setTag).toHaveBeenCalledWith('event_type', 'log'); expect(mockPoint.setTag).toHaveBeenCalledWith('source', 'qseow-engine'); expect(mockPoint.setIntegerField).toHaveBeenCalledWith('counter', 10); @@ -148,7 +148,7 @@ describe('v3/event-counts', () => { await storeEventCountInfluxDBV3(); - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalledTimes(1); + expect(utils.writeToInfluxWithRetry).toHaveBeenCalledTimes(1); expect(mockPoint.setTag).toHaveBeenCalledWith('event_type', 'user'); expect(mockPoint.setIntegerField).toHaveBeenCalledWith('counter', 15); }); @@ -166,7 +166,7 @@ describe('v3/event-counts', () => { await storeEventCountInfluxDBV3(); - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalledTimes(2); + expect(utils.writeToInfluxWithRetry).toHaveBeenCalledTimes(2); }); test('should apply config tags when available', async () => { @@ -199,7 +199,7 @@ describe('v3/event-counts', () => { globals.udpEvents.getUserEvents.mockResolvedValue([]); const writeError = new Error('Write failed'); - utils.writeToInfluxV3WithRetry.mockRejectedValue(writeError); + utils.writeToInfluxWithRetry.mockRejectedValue(writeError); await storeEventCountInfluxDBV3(); @@ -218,7 +218,7 @@ describe('v3/event-counts', () => { expect(globals.logger.verbose).toHaveBeenCalledWith( expect.stringContaining('No events to store') ); - expect(utils.writeToInfluxV3WithRetry).not.toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).not.toHaveBeenCalled(); }); test('should return early when InfluxDB is disabled', async () => { @@ -227,7 +227,7 @@ describe('v3/event-counts', () => { await storeRejectedEventCountInfluxDBV3(); - expect(utils.writeToInfluxV3WithRetry).not.toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).not.toHaveBeenCalled(); }); test('should store rejected log events successfully', async () => { @@ -248,7 +248,7 @@ describe('v3/event-counts', () => { await storeRejectedEventCountInfluxDBV3(); // Should have written the rejected event - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).toHaveBeenCalled(); expect(globals.logger.debug).toHaveBeenCalledWith( expect.stringContaining('Wrote data to InfluxDB v3') ); @@ -266,7 +266,7 @@ describe('v3/event-counts', () => { globals.rejectedEvents.getRejectedLogEvents.mockResolvedValue(logEvents); const writeError = new Error('Write failed'); - utils.writeToInfluxV3WithRetry.mockRejectedValue(writeError); + utils.writeToInfluxWithRetry.mockRejectedValue(writeError); await storeRejectedEventCountInfluxDBV3(); diff --git a/src/lib/influxdb/__tests__/v3-health-metrics.test.js b/src/lib/influxdb/__tests__/v3-health-metrics.test.js index f7b45f8..7b29662 100644 --- a/src/lib/influxdb/__tests__/v3-health-metrics.test.js +++ b/src/lib/influxdb/__tests__/v3-health-metrics.test.js @@ -33,7 +33,7 @@ const mockUtils = { processAppDocuments: jest.fn(), isInfluxDbEnabled: jest.fn(), applyTagsToPoint3: jest.fn(), - writeToInfluxV3WithRetry: jest.fn(), + writeToInfluxWithRetry: jest.fn(), }; jest.unstable_mockModule('../shared/utils.js', () => mockUtils); @@ -86,7 +86,7 @@ describe('v3/health-metrics', () => { sessionAppNames: ['SessionApp1'], }); utils.isInfluxDbEnabled.mockReturnValue(true); - utils.writeToInfluxV3WithRetry.mockResolvedValue(); + utils.writeToInfluxWithRetry.mockResolvedValue(); utils.applyTagsToPoint3.mockImplementation(() => {}); // Setup influxWriteApi @@ -145,7 +145,7 @@ describe('v3/health-metrics', () => { await postHealthMetricsToInfluxdbV3('test-server', 'test-host', body, {}); - expect(utils.writeToInfluxV3WithRetry).not.toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).not.toHaveBeenCalled(); }); test('should warn and return when influxWriteApi is not initialized', async () => { @@ -157,7 +157,7 @@ describe('v3/health-metrics', () => { expect(globals.logger.warn).toHaveBeenCalledWith( expect.stringContaining('Influxdb write API object not initialized') ); - expect(utils.writeToInfluxV3WithRetry).not.toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).not.toHaveBeenCalled(); }); test('should warn and return when writeApi not found for server', async () => { @@ -168,7 +168,7 @@ describe('v3/health-metrics', () => { expect(globals.logger.warn).toHaveBeenCalledWith( expect.stringContaining('Influxdb write API object not found for host test-host') ); - expect(utils.writeToInfluxV3WithRetry).not.toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).not.toHaveBeenCalled(); }); test('should process and write all health metrics successfully', async () => { @@ -199,7 +199,7 @@ describe('v3/health-metrics', () => { expect(utils.applyTagsToPoint3).toHaveBeenCalledTimes(8); // Should write all 8 measurements - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalledTimes(8); + expect(utils.writeToInfluxWithRetry).toHaveBeenCalledTimes(8); }); test('should call getFormattedTime with started timestamp', async () => { @@ -228,7 +228,7 @@ describe('v3/health-metrics', () => { test('should handle write errors with error tracking', async () => { const body = createMockBody(); const writeError = new Error('Write failed'); - utils.writeToInfluxV3WithRetry.mockRejectedValue(writeError); + utils.writeToInfluxWithRetry.mockRejectedValue(writeError); await postHealthMetricsToInfluxdbV3('test-server', 'test-host', body, {}); diff --git a/src/lib/influxdb/__tests__/v3-log-events.test.js b/src/lib/influxdb/__tests__/v3-log-events.test.js index a7b2060..a6c25cf 100644 --- a/src/lib/influxdb/__tests__/v3-log-events.test.js +++ b/src/lib/influxdb/__tests__/v3-log-events.test.js @@ -29,7 +29,7 @@ jest.unstable_mockModule('../../../globals.js', () => ({ // Mock shared utils const mockUtils = { isInfluxDbEnabled: jest.fn(), - writeToInfluxV3WithRetry: jest.fn(), + writeToInfluxWithRetry: jest.fn(), }; jest.unstable_mockModule('../shared/utils.js', () => mockUtils); @@ -64,7 +64,7 @@ describe('v3/log-events', () => { // Setup default mocks globals.config.get.mockReturnValue('test-db'); utils.isInfluxDbEnabled.mockReturnValue(true); - utils.writeToInfluxV3WithRetry.mockResolvedValue(); + utils.writeToInfluxWithRetry.mockResolvedValue(); }); describe('postLogEventToInfluxdbV3', () => { @@ -78,7 +78,7 @@ describe('v3/log-events', () => { await postLogEventToInfluxdbV3(msg); - expect(utils.writeToInfluxV3WithRetry).not.toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).not.toHaveBeenCalled(); }); test('should warn and return for unknown log event source', async () => { @@ -92,7 +92,7 @@ describe('v3/log-events', () => { expect(globals.logger.warn).toHaveBeenCalledWith( expect.stringContaining('Unknown log event source: unknown-source') ); - expect(utils.writeToInfluxV3WithRetry).not.toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).not.toHaveBeenCalled(); }); test('should successfully write qseow-engine log event', async () => { @@ -110,7 +110,7 @@ describe('v3/log-events', () => { expect(mockPoint.setTag).toHaveBeenCalledWith('source', 'qseow-engine'); expect(mockPoint.setTag).toHaveBeenCalledWith('level', 'INFO'); expect(mockPoint.setStringField).toHaveBeenCalledWith('message', 'Test message'); - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).toHaveBeenCalled(); }); test('should successfully write qseow-proxy log event', async () => { @@ -126,7 +126,7 @@ describe('v3/log-events', () => { expect(mockPoint.setTag).toHaveBeenCalledWith('host', 'server1'); expect(mockPoint.setTag).toHaveBeenCalledWith('source', 'qseow-proxy'); expect(mockPoint.setTag).toHaveBeenCalledWith('level', 'WARN'); - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).toHaveBeenCalled(); }); test('should successfully write qseow-scheduler log event', async () => { @@ -140,7 +140,7 @@ describe('v3/log-events', () => { await postLogEventToInfluxdbV3(msg); expect(mockPoint.setTag).toHaveBeenCalledWith('source', 'qseow-scheduler'); - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).toHaveBeenCalled(); }); test('should successfully write qseow-repository log event', async () => { @@ -154,7 +154,7 @@ describe('v3/log-events', () => { await postLogEventToInfluxdbV3(msg); expect(mockPoint.setTag).toHaveBeenCalledWith('source', 'qseow-repository'); - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).toHaveBeenCalled(); }); test('should successfully write qseow-qix-perf log event', async () => { @@ -178,7 +178,7 @@ describe('v3/log-events', () => { await postLogEventToInfluxdbV3(msg); expect(mockPoint.setTag).toHaveBeenCalledWith('source', 'qseow-qix-perf'); - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).toHaveBeenCalled(); }); test('should handle write errors', async () => { @@ -190,7 +190,7 @@ describe('v3/log-events', () => { }; const writeError = new Error('Write failed'); - utils.writeToInfluxV3WithRetry.mockRejectedValue(writeError); + utils.writeToInfluxWithRetry.mockRejectedValue(writeError); await postLogEventToInfluxdbV3(msg); @@ -221,7 +221,7 @@ describe('v3/log-events', () => { 'exception_message', 'Exception details' ); - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).toHaveBeenCalled(); }); }); }); diff --git a/src/lib/influxdb/__tests__/v3-queue-metrics.test.js b/src/lib/influxdb/__tests__/v3-queue-metrics.test.js index d1b980e..2f68f07 100644 --- a/src/lib/influxdb/__tests__/v3-queue-metrics.test.js +++ b/src/lib/influxdb/__tests__/v3-queue-metrics.test.js @@ -45,7 +45,7 @@ jest.unstable_mockModule('@influxdata/influxdb3-client', () => ({ // Mock shared utils jest.unstable_mockModule('../shared/utils.js', () => ({ isInfluxDbEnabled: jest.fn(), - writeToInfluxV3WithRetry: jest.fn(), + writeToInfluxWithRetry: jest.fn(), })); describe('InfluxDB v3 Queue Metrics', () => { @@ -66,7 +66,7 @@ describe('InfluxDB v3 Queue Metrics', () => { // Setup default mocks utils.isInfluxDbEnabled.mockReturnValue(true); - utils.writeToInfluxV3WithRetry.mockResolvedValue(); + utils.writeToInfluxWithRetry.mockResolvedValue(); }); describe('postUserEventQueueMetricsToInfluxdbV3', () => { @@ -76,7 +76,7 @@ describe('InfluxDB v3 Queue Metrics', () => { await queueMetrics.postUserEventQueueMetricsToInfluxdbV3(); expect(Point3).not.toHaveBeenCalled(); - expect(utils.writeToInfluxV3WithRetry).not.toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).not.toHaveBeenCalled(); }); test('should warn when queue manager is not initialized', async () => { @@ -99,7 +99,7 @@ describe('InfluxDB v3 Queue Metrics', () => { await queueMetrics.postUserEventQueueMetricsToInfluxdbV3(); expect(Point3).not.toHaveBeenCalled(); - expect(utils.writeToInfluxV3WithRetry).not.toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).not.toHaveBeenCalled(); }); test('should successfully write queue metrics', async () => { @@ -150,9 +150,11 @@ describe('InfluxDB v3 Queue Metrics', () => { await queueMetrics.postUserEventQueueMetricsToInfluxdbV3(); expect(Point3).toHaveBeenCalledWith('user_events_queue'); - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalledWith( + expect(utils.writeToInfluxWithRetry).toHaveBeenCalledWith( expect.any(Function), - 'User event queue metrics' + 'User event queue metrics', + 'v3', + 'user-events-queue' ); expect(globals.logger.verbose).toHaveBeenCalledWith( 'USER EVENT QUEUE METRICS INFLUXDB V3: Sent queue metrics data to InfluxDB v3' @@ -189,7 +191,7 @@ describe('InfluxDB v3 Queue Metrics', () => { await queueMetrics.postLogEventQueueMetricsToInfluxdbV3(); expect(Point3).not.toHaveBeenCalled(); - expect(utils.writeToInfluxV3WithRetry).not.toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).not.toHaveBeenCalled(); }); test('should warn when queue manager is not initialized', async () => { @@ -252,9 +254,11 @@ describe('InfluxDB v3 Queue Metrics', () => { await queueMetrics.postLogEventQueueMetricsToInfluxdbV3(); expect(Point3).toHaveBeenCalledWith('log_events_queue'); - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalledWith( + expect(utils.writeToInfluxWithRetry).toHaveBeenCalledWith( expect.any(Function), - 'Log event queue metrics' + 'Log event queue metrics', + 'v3', + 'log-events-queue' ); expect(globals.logger.verbose).toHaveBeenCalledWith( 'LOG EVENT QUEUE METRICS INFLUXDB V3: Sent queue metrics data to InfluxDB v3' @@ -305,7 +309,7 @@ describe('InfluxDB v3 Queue Metrics', () => { clearMetrics: jest.fn(), }; - utils.writeToInfluxV3WithRetry.mockRejectedValue(new Error('Write failed')); + utils.writeToInfluxWithRetry.mockRejectedValue(new Error('Write failed')); await queueMetrics.postLogEventQueueMetricsToInfluxdbV3(); diff --git a/src/lib/influxdb/__tests__/v3-sessions.test.js b/src/lib/influxdb/__tests__/v3-sessions.test.js index 50b1e07..5af9159 100644 --- a/src/lib/influxdb/__tests__/v3-sessions.test.js +++ b/src/lib/influxdb/__tests__/v3-sessions.test.js @@ -29,7 +29,7 @@ jest.unstable_mockModule('../../../globals.js', () => ({ // Mock shared utils const mockUtils = { isInfluxDbEnabled: jest.fn(), - writeToInfluxV3WithRetry: jest.fn(), + writeToInfluxWithRetry: jest.fn(), }; jest.unstable_mockModule('../shared/utils.js', () => mockUtils); @@ -62,7 +62,7 @@ describe('v3/sessions', () => { globals.config.get.mockReturnValue('test-db'); globals.influx.write.mockResolvedValue(); utils.isInfluxDbEnabled.mockReturnValue(true); - utils.writeToInfluxV3WithRetry.mockImplementation(async (fn) => await fn()); + utils.writeToInfluxWithRetry.mockImplementation(async (fn) => await fn()); }); describe('postProxySessionsToInfluxdbV3', () => { diff --git a/src/lib/influxdb/__tests__/v3-shared-utils.test.js b/src/lib/influxdb/__tests__/v3-shared-utils.test.js index 1ee0e6d..65717dd 100644 --- a/src/lib/influxdb/__tests__/v3-shared-utils.test.js +++ b/src/lib/influxdb/__tests__/v3-shared-utils.test.js @@ -95,7 +95,7 @@ describe('InfluxDB v3 Shared Utils', () => { expect(result).toBe(false); }); - test('should return false when feature flag is undefined for v1/v2', () => { + test('should return true for v1 even when feature flag is undefined (v1 always uses refactored code)', () => { globals.config.get.mockImplementation((key) => { if (key === 'Butler-SOS.influxdbConfig.version') return 1; if (key === 'Butler-SOS.influxdbConfig.useRefactoredCode') return undefined; @@ -104,6 +104,18 @@ describe('InfluxDB v3 Shared Utils', () => { const result = utils.useRefactoredInfluxDb(); + expect(result).toBe(true); + }); + + test('should return false when feature flag is undefined for v2', () => { + globals.config.get.mockImplementation((key) => { + if (key === 'Butler-SOS.influxdbConfig.version') return 2; + if (key === 'Butler-SOS.influxdbConfig.useRefactoredCode') return undefined; + return undefined; + }); + + const result = utils.useRefactoredInfluxDb(); + expect(result).toBe(false); }); }); @@ -129,11 +141,11 @@ describe('InfluxDB v3 Shared Utils', () => { }); }); - describe('writeToInfluxV3WithRetry', () => { + describe('writeToInfluxWithRetry', () => { test('should successfully write on first attempt', async () => { const writeFn = jest.fn().mockResolvedValue(); - await utils.writeToInfluxV3WithRetry(writeFn, 'Test context'); + await utils.writeToInfluxWithRetry(writeFn, 'Test context', 'v3', ''); expect(writeFn).toHaveBeenCalledTimes(1); expect(globals.logger.error).not.toHaveBeenCalled(); @@ -145,14 +157,14 @@ describe('InfluxDB v3 Shared Utils', () => { const writeFn = jest.fn().mockRejectedValueOnce(timeoutError).mockResolvedValueOnce(); - await utils.writeToInfluxV3WithRetry(writeFn, 'Test context', { + await utils.writeToInfluxWithRetry(writeFn, 'Test context', 'v3', '', { maxRetries: 3, initialDelayMs: 10, }); expect(writeFn).toHaveBeenCalledTimes(2); expect(globals.logger.warn).toHaveBeenCalledWith( - expect.stringContaining('INFLUXDB V3 RETRY: Test context - Timeout') + expect.stringContaining('INFLUXDB V3 RETRY: Test context - Retryable') ); }); @@ -166,7 +178,7 @@ describe('InfluxDB v3 Shared Utils', () => { .mockRejectedValueOnce(timeoutError) .mockResolvedValueOnce(); - await utils.writeToInfluxV3WithRetry(writeFn, 'Test context', { + await utils.writeToInfluxWithRetry(writeFn, 'Test context', 'v3', '', { maxRetries: 3, initialDelayMs: 10, }); @@ -183,7 +195,7 @@ describe('InfluxDB v3 Shared Utils', () => { globals.errorTracker = { incrementError: jest.fn().mockResolvedValue() }; await expect( - utils.writeToInfluxV3WithRetry(writeFn, 'Test context', { + utils.writeToInfluxWithRetry(writeFn, 'Test context', 'v3', '', { maxRetries: 2, initialDelayMs: 10, }) @@ -199,12 +211,13 @@ describe('InfluxDB v3 Shared Utils', () => { ); }); - test('should throw non-timeout error immediately without retry', async () => { - const nonTimeoutError = new Error('Connection refused'); - const writeFn = jest.fn().mockRejectedValue(nonTimeoutError); + test('should throw non-retryable error immediately without retry', async () => { + const nonRetryableError = new Error('Connection refused'); + const writeFn = jest.fn().mockRejectedValue(nonRetryableError); + globals.errorTracker = { incrementError: jest.fn().mockResolvedValue() }; await expect( - utils.writeToInfluxV3WithRetry(writeFn, 'Test context', { + utils.writeToInfluxWithRetry(writeFn, 'Test context', 'v3', '', { maxRetries: 3, initialDelayMs: 10, }) @@ -212,7 +225,7 @@ describe('InfluxDB v3 Shared Utils', () => { expect(writeFn).toHaveBeenCalledTimes(1); expect(globals.logger.warn).toHaveBeenCalledWith( - expect.stringContaining('INFLUXDB V3 WRITE: Test context - Non-timeout error') + expect.stringContaining('INFLUXDB V3 WRITE: Test context - Non-retryable error') ); }); @@ -221,7 +234,7 @@ describe('InfluxDB v3 Shared Utils', () => { const writeFn = jest.fn().mockRejectedValueOnce(timeoutError).mockResolvedValueOnce(); - await utils.writeToInfluxV3WithRetry(writeFn, 'Test context', { + await utils.writeToInfluxWithRetry(writeFn, 'Test context', 'v3', '', { maxRetries: 3, initialDelayMs: 10, }); @@ -237,7 +250,7 @@ describe('InfluxDB v3 Shared Utils', () => { const writeFn = jest.fn().mockRejectedValueOnce(timeoutError).mockResolvedValueOnce(); - await utils.writeToInfluxV3WithRetry(writeFn, 'Test context', { + await utils.writeToInfluxWithRetry(writeFn, 'Test context', 'v3', '', { maxRetries: 3, initialDelayMs: 10, }); diff --git a/src/lib/influxdb/__tests__/v3-user-events.test.js b/src/lib/influxdb/__tests__/v3-user-events.test.js index a359f02..b3d10bc 100644 --- a/src/lib/influxdb/__tests__/v3-user-events.test.js +++ b/src/lib/influxdb/__tests__/v3-user-events.test.js @@ -30,7 +30,7 @@ jest.unstable_mockModule('../../../globals.js', () => ({ // Mock shared utils const mockUtils = { isInfluxDbEnabled: jest.fn(), - writeToInfluxV3WithRetry: jest.fn(), + writeToInfluxWithRetry: jest.fn(), }; jest.unstable_mockModule('../shared/utils.js', () => mockUtils); @@ -64,7 +64,7 @@ describe('v3/user-events', () => { // Setup default mocks globals.config.get.mockReturnValue('test-db'); utils.isInfluxDbEnabled.mockReturnValue(true); - utils.writeToInfluxV3WithRetry.mockResolvedValue(); + utils.writeToInfluxWithRetry.mockResolvedValue(); }); describe('postUserEventToInfluxdbV3', () => { @@ -81,7 +81,7 @@ describe('v3/user-events', () => { await postUserEventToInfluxdbV3(msg); - expect(utils.writeToInfluxV3WithRetry).not.toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).not.toHaveBeenCalled(); }); test('should warn and return early when required fields are missing', async () => { @@ -96,7 +96,7 @@ describe('v3/user-events', () => { expect(globals.logger.warn).toHaveBeenCalledWith( expect.stringContaining('Missing required fields') ); - expect(utils.writeToInfluxV3WithRetry).not.toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).not.toHaveBeenCalled(); }); test('should successfully write user event with all fields', async () => { @@ -117,7 +117,7 @@ describe('v3/user-events', () => { await postUserEventToInfluxdbV3(msg); - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).toHaveBeenCalled(); expect(mockPoint.setTag).toHaveBeenCalledWith('host', 'server1'); expect(mockPoint.setTag).toHaveBeenCalledWith('event_action', 'OpenApp'); expect(mockPoint.setTag).toHaveBeenCalledWith('userDirectory', 'DOMAIN'); @@ -136,7 +136,7 @@ describe('v3/user-events', () => { await postUserEventToInfluxdbV3(msg); - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).toHaveBeenCalled(); expect(mockPoint.setTag).toHaveBeenCalledWith('host', 'server1'); expect(mockPoint.setTag).toHaveBeenCalledWith('event_action', 'CreateApp'); }); @@ -152,7 +152,7 @@ describe('v3/user-events', () => { await postUserEventToInfluxdbV3(msg); - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).toHaveBeenCalled(); }); test('should handle write errors', async () => { @@ -165,7 +165,7 @@ describe('v3/user-events', () => { }; const writeError = new Error('Write failed'); - utils.writeToInfluxV3WithRetry.mockRejectedValue(writeError); + utils.writeToInfluxWithRetry.mockRejectedValue(writeError); await postUserEventToInfluxdbV3(msg); @@ -199,7 +199,7 @@ describe('v3/user-events', () => { await postUserEventToInfluxdbV3(msg); - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).toHaveBeenCalled(); expect(mockPoint.setTag).toHaveBeenCalledWith('uaBrowserName', 'Chrome'); expect(mockPoint.setTag).toHaveBeenCalledWith('uaBrowserMajorVersion', '96'); expect(mockPoint.setTag).toHaveBeenCalledWith('uaOsName', 'Windows'); @@ -219,7 +219,7 @@ describe('v3/user-events', () => { await postUserEventToInfluxdbV3(msg); - expect(utils.writeToInfluxV3WithRetry).toHaveBeenCalled(); + expect(utils.writeToInfluxWithRetry).toHaveBeenCalled(); expect(mockPoint.setTag).toHaveBeenCalledWith('appId', 'abc-123-def'); expect(mockPoint.setStringField).toHaveBeenCalledWith('appId_field', 'abc-123-def'); expect(mockPoint.setTag).toHaveBeenCalledWith('appName', 'Sales Dashboard'); diff --git a/src/lib/influxdb/v3/__tests__/health-metrics.test.js b/src/lib/influxdb/v3/__tests__/health-metrics.test.js deleted file mode 100644 index e6b0471..0000000 --- a/src/lib/influxdb/v3/__tests__/health-metrics.test.js +++ /dev/null @@ -1,3 +0,0 @@ -// This test file has been removed as it only contained skipped placeholder tests. -// Note: Complex ES module mocking requirements make these tests difficult. -// The v3 health metrics code is functionally tested through integration tests. diff --git a/src/lib/influxdb/v3/butler-memory.js b/src/lib/influxdb/v3/butler-memory.js index ce29e00..52f246a 100644 --- a/src/lib/influxdb/v3/butler-memory.js +++ b/src/lib/influxdb/v3/butler-memory.js @@ -1,6 +1,6 @@ import { Point as Point3 } from '@influxdata/influxdb3-client'; import globals from '../../../globals.js'; -import { isInfluxDbEnabled, writeToInfluxV3WithRetry } from '../shared/utils.js'; +import { isInfluxDbEnabled, writeToInfluxWithRetry } from '../shared/utils.js'; /** * Posts Butler SOS memory usage metrics to InfluxDB v3. @@ -40,9 +40,11 @@ export async function postButlerSOSMemoryUsageToInfluxdbV3(memory) { try { // Convert point to line protocol and write directly with retry logic - await writeToInfluxV3WithRetry( + await writeToInfluxWithRetry( async () => await globals.influx.write(point.toLineProtocol(), database), - 'Memory usage metrics' + 'Memory usage metrics', + 'v3', + '' // No specific server context for Butler memory ); globals.logger.debug(`MEMORY USAGE V3: Wrote data to InfluxDB v3`); } catch (err) { diff --git a/src/lib/influxdb/v3/event-counts.js b/src/lib/influxdb/v3/event-counts.js index 68552d1..bb03bea 100644 --- a/src/lib/influxdb/v3/event-counts.js +++ b/src/lib/influxdb/v3/event-counts.js @@ -1,6 +1,6 @@ import { Point as Point3 } from '@influxdata/influxdb3-client'; import globals from '../../../globals.js'; -import { isInfluxDbEnabled, writeToInfluxV3WithRetry } from '../shared/utils.js'; +import { isInfluxDbEnabled, writeToInfluxWithRetry } from '../shared/utils.js'; /** * Store event count in InfluxDB v3 @@ -80,9 +80,11 @@ export async function storeEventCountInfluxDBV3() { point.setTag(key, tags[key]); }); - await writeToInfluxV3WithRetry( + await writeToInfluxWithRetry( async () => await globals.influx.write(point.toLineProtocol(), database), - 'Log event counts' + 'Log event counts', + 'v3', + 'log-events' ); globals.logger.debug(`EVENT COUNT INFLUXDB V3: Wrote log event data to InfluxDB v3`); } @@ -127,9 +129,11 @@ export async function storeEventCountInfluxDBV3() { point.setTag(key, tags[key]); }); - await writeToInfluxV3WithRetry( + await writeToInfluxWithRetry( async () => await globals.influx.write(point.toLineProtocol(), database), - 'User event counts' + 'User event counts', + 'v3', + 'user-events' ); globals.logger.debug(`EVENT COUNT INFLUXDB V3: Wrote user event data to InfluxDB v3`); } @@ -240,9 +244,11 @@ export async function storeRejectedEventCountInfluxDBV3() { // Write to InfluxDB for (const point of points) { - await writeToInfluxV3WithRetry( + await writeToInfluxWithRetry( async () => await globals.influx.write(point.toLineProtocol(), database), - 'Rejected event counts' + 'Rejected event counts', + 'v3', + 'rejected-events' ); } globals.logger.debug(`REJECT LOG EVENT INFLUXDB V3: Wrote data to InfluxDB v3`); diff --git a/src/lib/influxdb/v3/health-metrics.js b/src/lib/influxdb/v3/health-metrics.js index 58e0988..894981e 100644 --- a/src/lib/influxdb/v3/health-metrics.js +++ b/src/lib/influxdb/v3/health-metrics.js @@ -5,7 +5,7 @@ import { processAppDocuments, isInfluxDbEnabled, applyTagsToPoint3, - writeToInfluxV3WithRetry, + writeToInfluxWithRetry, } from '../shared/utils.js'; /** @@ -194,9 +194,11 @@ export async function postHealthMetricsToInfluxdbV3(serverName, host, body, serv for (const point of points) { // Apply server tags to each point applyTagsToPoint3(point, serverTags); - await writeToInfluxV3WithRetry( + await writeToInfluxWithRetry( async () => await globals.influx.write(point.toLineProtocol(), database), - `Health metrics for ${host}` + `Health metrics for ${host}`, + 'v3', + serverName ); } globals.logger.debug(`HEALTH METRICS V3: Wrote data to InfluxDB v3`); diff --git a/src/lib/influxdb/v3/log-events.js b/src/lib/influxdb/v3/log-events.js index 1f6220d..d9ead3a 100644 --- a/src/lib/influxdb/v3/log-events.js +++ b/src/lib/influxdb/v3/log-events.js @@ -1,6 +1,6 @@ import { Point as Point3 } from '@influxdata/influxdb3-client'; import globals from '../../../globals.js'; -import { isInfluxDbEnabled, writeToInfluxV3WithRetry } from '../shared/utils.js'; +import { isInfluxDbEnabled, writeToInfluxWithRetry } from '../shared/utils.js'; /** * Post log event to InfluxDB v3 @@ -195,9 +195,11 @@ export async function postLogEventToInfluxdbV3(msg) { } } - await writeToInfluxV3WithRetry( + await writeToInfluxWithRetry( async () => await globals.influx.write(point.toLineProtocol(), database), - `Log event for ${msg.host}` + `Log event for ${msg.host}`, + 'v3', + msg.host ); globals.logger.debug(`LOG EVENT INFLUXDB V3: Wrote data to InfluxDB v3`); diff --git a/src/lib/influxdb/v3/queue-metrics.js b/src/lib/influxdb/v3/queue-metrics.js index 7a05c4c..99ed1bd 100644 --- a/src/lib/influxdb/v3/queue-metrics.js +++ b/src/lib/influxdb/v3/queue-metrics.js @@ -1,6 +1,6 @@ import { Point as Point3 } from '@influxdata/influxdb3-client'; import globals from '../../../globals.js'; -import { isInfluxDbEnabled, writeToInfluxV3WithRetry } from '../shared/utils.js'; +import { isInfluxDbEnabled, writeToInfluxWithRetry } from '../shared/utils.js'; /** * Store user event queue metrics to InfluxDB v3 @@ -77,9 +77,11 @@ export async function postUserEventQueueMetricsToInfluxdbV3() { } } - await writeToInfluxV3WithRetry( + await writeToInfluxWithRetry( async () => await globals.influx.write(point.toLineProtocol(), database), - 'User event queue metrics' + 'User event queue metrics', + 'v3', + 'user-events-queue' ); globals.logger.verbose( @@ -168,9 +170,11 @@ export async function postLogEventQueueMetricsToInfluxdbV3() { } } - await writeToInfluxV3WithRetry( + await writeToInfluxWithRetry( async () => await globals.influx.write(point.toLineProtocol(), database), - 'Log event queue metrics' + 'Log event queue metrics', + 'v3', + 'log-events-queue' ); globals.logger.verbose( diff --git a/src/lib/influxdb/v3/sessions.js b/src/lib/influxdb/v3/sessions.js index a92fe49..a426c19 100644 --- a/src/lib/influxdb/v3/sessions.js +++ b/src/lib/influxdb/v3/sessions.js @@ -1,6 +1,6 @@ import { Point as Point3 } from '@influxdata/influxdb3-client'; import globals from '../../../globals.js'; -import { isInfluxDbEnabled, writeToInfluxV3WithRetry } from '../shared/utils.js'; +import { isInfluxDbEnabled, writeToInfluxWithRetry } from '../shared/utils.js'; /** * Posts proxy sessions data to InfluxDB v3. @@ -40,9 +40,11 @@ export async function postProxySessionsToInfluxdbV3(userSessions) { try { if (userSessions.datapointInfluxdb && userSessions.datapointInfluxdb.length > 0) { for (const point of userSessions.datapointInfluxdb) { - await writeToInfluxV3WithRetry( + await writeToInfluxWithRetry( async () => await globals.influx.write(point.toLineProtocol(), database), - `Proxy sessions for ${userSessions.host}/${userSessions.virtualProxy}` + `Proxy sessions for ${userSessions.host}/${userSessions.virtualProxy}`, + 'v3', + userSessions.host ); } globals.logger.debug( diff --git a/src/lib/influxdb/v3/user-events.js b/src/lib/influxdb/v3/user-events.js index 260c813..8187124 100644 --- a/src/lib/influxdb/v3/user-events.js +++ b/src/lib/influxdb/v3/user-events.js @@ -1,6 +1,6 @@ import { Point as Point3 } from '@influxdata/influxdb3-client'; import globals from '../../../globals.js'; -import { isInfluxDbEnabled, writeToInfluxV3WithRetry } from '../shared/utils.js'; +import { isInfluxDbEnabled, writeToInfluxWithRetry } from '../shared/utils.js'; /** * Sanitize tag values for InfluxDB line protocol. @@ -100,9 +100,11 @@ export async function postUserEventToInfluxdbV3(msg) { // Write to InfluxDB try { // Convert point to line protocol and write directly with retry logic - await writeToInfluxV3WithRetry( + await writeToInfluxWithRetry( async () => await globals.influx.write(point.toLineProtocol(), database), - `User event for ${msg.host}` + `User event for ${msg.host}`, + 'v3', + msg.host ); globals.logger.debug(`USER EVENT INFLUXDB V3: Wrote data to InfluxDB v3`); } catch (err) {