Skip to content

Commit 25ea9d5

Browse files
committed
Add unit tests for scan monitoring metrics
Issue: BB-740
1 parent 3b78c2d commit 25ea9d5

File tree

2 files changed

+156
-0
lines changed

2 files changed

+156
-0
lines changed

tests/unit/lifecycle/LifecycleConductor.spec.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const { splitter } = require('arsenal').constants;
77

88
const LifecycleConductor = require(
99
'../../../extensions/lifecycle/conductor/LifecycleConductor');
10+
const { LifecycleMetrics } = require('../../../extensions/lifecycle/LifecycleMetrics');
1011
const {
1112
lifecycleTaskVersions,
1213
indexesForFeature
@@ -190,6 +191,64 @@ describe('Lifecycle Conductor', () => {
190191
conductor.processBuckets(done);
191192
});
192193

194+
it('should publish full scan metrics at end of scan', done => {
195+
conductor._mongodbClient = {
196+
getIndexingJobs: (_, cb) => cb(null, ['job1']),
197+
getCollection: () => ({
198+
find: () => ({
199+
project: () => ({
200+
hasNext: () => Promise.resolve(false),
201+
}),
202+
}),
203+
}),
204+
};
205+
conductor._zkClient = {
206+
getData: (_, cb) => cb(null, null, null),
207+
setData: (path, data, version, cb) => cb(null, { version: 1 }),
208+
};
209+
210+
sinon.stub(conductor, '_controlBacklog').callsFake(cb => cb(null));
211+
const metricStub = sinon.stub(LifecycleMetrics, 'onConductorFullScan');
212+
213+
conductor.processBuckets(err => {
214+
assert.ifError(err);
215+
assert(metricStub.calledOnce);
216+
const [, , bucketCount, workflowCount, lifecycleBucketCount] =
217+
metricStub.firstCall.args;
218+
assert.strictEqual(bucketCount, 0);
219+
assert.strictEqual(workflowCount, 0);
220+
assert.strictEqual(lifecycleBucketCount, 0);
221+
done();
222+
});
223+
});
224+
225+
it('should generate a conductorScanId', done => {
226+
conductor._mongodbClient = {
227+
getIndexingJobs: (_, cb) => cb(null, []),
228+
getCollection: () => ({
229+
find: () => ({
230+
project: () => ({
231+
hasNext: () => Promise.resolve(false),
232+
}),
233+
}),
234+
}),
235+
};
236+
conductor._zkClient = {
237+
getData: (_, cb) => cb(null, null, null),
238+
setData: (path, data, version, cb) => cb(null, { version: 1 }),
239+
};
240+
241+
sinon.stub(conductor, '_controlBacklog').callsFake(cb => cb(null));
242+
243+
conductor.processBuckets(err => {
244+
assert.ifError(err);
245+
assert(conductor._scanId);
246+
assert(typeof conductor._scanId === 'string');
247+
assert(conductor._scanId.length > 0);
248+
done();
249+
});
250+
});
251+
193252
// tests that `activeIndexingJobRetrieved` is not reset until the e
194253
it('should not reset `activeIndexingJobsRetrieved` while async operations are in progress', done => {
195254
const order = [];
@@ -244,6 +303,14 @@ describe('Lifecycle Conductor', () => {
244303
});
245304

246305
describe('_indexesGetOrCreate', () => {
306+
it('should include conductor scan id in task context', () => {
307+
conductor._scanId = 'scan-id-test';
308+
309+
const taskMessage = conductor._taskToMessage(getTask(true), lifecycleTaskVersions.v2, log);
310+
const parsed = JSON.parse(taskMessage.message);
311+
assert.strictEqual(parsed.contextInfo.conductorScanId, 'scan-id-test');
312+
});
313+
247314
it('should return v2 for bucketd bucket sources', done => {
248315
conductor._bucketSource = 'bucketd';
249316
conductor._indexesGetOrCreate(getTask(undefined), log, (err, taskVersion) => {

tests/unit/lifecycle/LifecycleMetrics.spec.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,72 @@ describe('LifecycleMetrics', () => {
7979
}));
8080
});
8181

82+
it('should catch errors in onConductorFullScan', () => {
83+
const metric = ZenkoMetrics.getMetric('s3_lifecycle_conductor_full_scan_elapsed_seconds');
84+
sinon.stub(metric, 'set').throws(new Error('Metric error'));
85+
86+
LifecycleMetrics.onConductorFullScan(log, 5000, 10, 8, 5);
87+
88+
assert(log.error.calledOnce);
89+
assert(log.error.calledWithMatch('failed to update prometheus metrics', {
90+
method: 'LifecycleMetrics.onConductorFullScan',
91+
elapsedMs: 5000,
92+
bucketCount: 10,
93+
workflowCount: 8,
94+
lifecycleBucketCount: 5,
95+
}));
96+
});
97+
98+
it('should set and reset bucket processor scan gauges', () => {
99+
const scanStartMetric = ZenkoMetrics.getMetric(
100+
's3_lifecycle_bucket_processor_scan_start_time');
101+
const bucketsMetric = ZenkoMetrics.getMetric(
102+
's3_lifecycle_bucket_processor_buckets_count');
103+
const startSet = sinon.stub(scanStartMetric, 'set');
104+
const bucketsSet = sinon.stub(bucketsMetric, 'set');
105+
const bucketsInc = sinon.stub(bucketsMetric, 'inc');
106+
107+
// start a scan
108+
LifecycleMetrics.onBucketProcessorScanStart(
109+
log, 1706000000000);
110+
assert(startSet.calledOnce);
111+
assert(startSet.calledWithMatch(
112+
{ origin: 'bucket_processor' }, 1706000000000));
113+
assert(bucketsSet.calledOnce);
114+
assert(bucketsSet.calledWithMatch(
115+
{ origin: 'bucket_processor' }, 0));
116+
117+
// process a bucket
118+
LifecycleMetrics.onBucketProcessorBucketDone(log);
119+
assert(bucketsInc.calledOnce);
120+
121+
// end the scan
122+
startSet.resetHistory();
123+
LifecycleMetrics.onBucketProcessorScanEnd(log);
124+
assert(startSet.calledOnce);
125+
assert(startSet.calledWithMatch(
126+
{ origin: 'bucket_processor' }, 0));
127+
128+
assert(log.error.notCalled);
129+
});
130+
131+
it('should catch errors in onBucketProcessorScanStart', () => {
132+
const scanStartMetric = ZenkoMetrics.getMetric(
133+
's3_lifecycle_bucket_processor_scan_start_time');
134+
sinon.stub(scanStartMetric, 'set')
135+
.throws(new Error('Metric error'));
136+
137+
LifecycleMetrics.onBucketProcessorScanStart(
138+
log, 1706000000000);
139+
140+
assert(log.error.calledOnce);
141+
assert(log.error.calledWithMatch(
142+
'failed to update prometheus metrics', {
143+
method:
144+
'LifecycleMetrics.onBucketProcessorScanStart',
145+
}));
146+
});
147+
82148
it('should catch errors in onLifecycleTriggered', () => {
83149
LifecycleMetrics.onLifecycleTriggered(log, 'conductor', 'expiration', 'us-east-1', NaN);
84150

@@ -169,5 +235,28 @@ describe('LifecycleMetrics', () => {
169235
process: 'conductor',
170236
}));
171237
});
238+
239+
it('should set full scan metrics including lifecycle bucket count', () => {
240+
const elapsedMetric = ZenkoMetrics.getMetric(
241+
's3_lifecycle_conductor_full_scan_elapsed_seconds');
242+
const countMetric = ZenkoMetrics.getMetric(
243+
's3_lifecycle_conductor_scan_count');
244+
const elapsedSet = sinon.stub(elapsedMetric, 'set');
245+
const countSet = sinon.stub(countMetric, 'set');
246+
247+
LifecycleMetrics.onConductorFullScan(log, 3000, 7, 6, 4);
248+
249+
assert(elapsedSet.calledOnce);
250+
assert(elapsedSet.calledWithMatch(
251+
{ origin: 'conductor' }, 3));
252+
assert.strictEqual(countSet.callCount, 3);
253+
assert(countSet.getCall(0).calledWithMatch(
254+
{ origin: 'conductor', type: 'bucket' }, 7));
255+
assert(countSet.getCall(1).calledWithMatch(
256+
{ origin: 'conductor', type: 'lifecycle_bucket' }, 4));
257+
assert(countSet.getCall(2).calledWithMatch(
258+
{ origin: 'conductor', type: 'workflow' }, 6));
259+
assert(log.error.notCalled);
260+
});
172261
});
173262
});

0 commit comments

Comments
 (0)