refactor(influxdb): Modernized shared InfluxDB code, better sharing of code across InfluxDB versions

This commit is contained in:
Göran Sander
2025-12-16 07:28:31 +01:00
parent 87b98d5e3c
commit d05c0bb653
18 changed files with 139 additions and 104 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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();

View File

@@ -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, {});

View File

@@ -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();
});
});
});

View File

@@ -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();

View File

@@ -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', () => {

View File

@@ -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,
});

View File

@@ -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');

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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`);

View File

@@ -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`);

View File

@@ -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`);

View File

@@ -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(

View File

@@ -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(

View File

@@ -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) {