Skip to content

Commit 4ac8887

Browse files
Disconnect the redis client the metrics consumer opens
MetricsConsumer built its RedisClient as a local and handed it straight to the StatsModel, so nothing could reach it afterwards: close() only tore down the kafka consumer, and the socket stayed open for the life of the process. Keep the client as a member and disconnect it alongside the consumer. Both populators already close their metrics consumer, so there is nothing to wire up on their side. close() also dereferenced the consumer unconditionally, while start() only assigns it on the ready event: closing during startup threw a TypeError and left the caller's series waiting on a callback that never came. Guard it, and still call back. Issue: BB-882
1 parent 322f768 commit 4ac8887

2 files changed

Lines changed: 96 additions & 3 deletions

File tree

‎lib/MetricsConsumer.js‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ class MetricsConsumer {
4343
this._consumer = null;
4444

4545
this.logger = new Logger('Backbeat:MetricsConsumer');
46-
const redisClient = new RedisClient(rConfig, this.logger);
47-
this._statsClient = new StatsModel(redisClient, INTERVAL, EXPIRY);
46+
this._redisClient = new RedisClient(rConfig, this.logger);
47+
this._statsClient = new StatsModel(this._redisClient, INTERVAL, EXPIRY);
4848
}
4949

5050
/**
@@ -230,7 +230,18 @@ class MetricsConsumer {
230230
}
231231

232232
close(cb) {
233-
this._consumer.close(cb);
233+
return (next => {
234+
// start() only sets the consumer once it reports ready, so a
235+
// close racing the startup has nothing to tear down on the
236+
// kafka side
237+
if (!this._consumer) {
238+
return process.nextTick(next);
239+
}
240+
return this._consumer.close(next);
241+
})(err => {
242+
this._redisClient.disconnect();
243+
cb(err);
244+
});
234245
}
235246
}
236247

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
const assert = require('assert');
2+
const sinon = require('sinon');
3+
4+
const MetricsConsumer = require('../../../lib/MetricsConsumer');
5+
6+
// lazyConnect keeps ioredis from opening a socket for the tests that only
7+
// check the wiring
8+
const redisConfig = { host: 'localhost', port: 6379, lazyConnect: true };
9+
const mConfig = { topic: 'backbeat-metrics', groupIdPrefix: 'backbeat-metrics-group' };
10+
const kafkaConfig = { hosts: 'localhost:9092' };
11+
12+
describe('MetricsConsumer', () => {
13+
let mConsumer;
14+
15+
beforeEach(() => {
16+
mConsumer = new MetricsConsumer(redisConfig, mConfig, kafkaConfig, 'crr');
17+
});
18+
19+
afterEach(() => {
20+
sinon.restore();
21+
});
22+
23+
it('should keep the redis client it hands to the stats model', () => {
24+
assert(mConsumer._redisClient);
25+
assert.strictEqual(mConsumer._statsClient._redis, mConsumer._redisClient);
26+
});
27+
28+
describe('close', () => {
29+
let disconnect;
30+
31+
beforeEach(() => {
32+
disconnect = sinon.stub(mConsumer._redisClient, 'disconnect');
33+
});
34+
35+
it('should disconnect redis along with the kafka consumer', done => {
36+
mConsumer._consumer = { close: sinon.stub().yields() };
37+
38+
mConsumer.close(err => {
39+
assert.ifError(err);
40+
sinon.assert.calledOnce(mConsumer._consumer.close);
41+
sinon.assert.calledOnce(disconnect);
42+
return done();
43+
});
44+
});
45+
46+
it('should disconnect redis and forward a kafka consumer error', done => {
47+
const closeError = new Error('close failed');
48+
mConsumer._consumer = { close: sinon.stub().yields(closeError) };
49+
50+
mConsumer.close(err => {
51+
assert.strictEqual(err, closeError);
52+
sinon.assert.calledOnce(disconnect);
53+
return done();
54+
});
55+
});
56+
57+
it('should still call back when the consumer is not ready yet', done => {
58+
assert.strictEqual(mConsumer._consumer, null);
59+
60+
assert.doesNotThrow(() => mConsumer.close(err => {
61+
assert.ifError(err);
62+
sinon.assert.calledOnce(disconnect);
63+
return done();
64+
}));
65+
});
66+
});
67+
68+
describe('close, on a live redis client', () => {
69+
it('should leave the connection unusable', done => {
70+
const consumer = new MetricsConsumer(
71+
{ host: 'localhost', port: 6379 }, mConfig, kafkaConfig, 'crr');
72+
73+
consumer.close(err => {
74+
assert.ifError(err);
75+
return consumer._redisClient.incrby('test-key', 1, err => {
76+
assert(err, 'expected the redis connection to be closed');
77+
return done();
78+
});
79+
});
80+
});
81+
});
82+
});

0 commit comments

Comments
 (0)