0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-17 23:44:39 -05:00

Removed connection pool instrumentation (#22091)

no issue

- This instrumentation is not currently being used and we have a safer
method to get this data from the connection pool using prometheus.
This commit is contained in:
Chris Raible 2025-02-05 18:47:36 -08:00 committed by GitHub
parent 049dba469f
commit 45432fa186
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 0 additions and 376 deletions

View file

@ -1,142 +0,0 @@
class ConnectionPoolInstrumentation {
constructor({knex, logging, metrics, config}) {
this.knex = knex;
this.pool = this.knex.client.pool;
this.logging = logging;
this.metrics = metrics;
this.enabled = config.get('telemetry:connectionPool');
this.requestStartTimes = {};
this.createStartTimes = {};
}
// Get the number of pending creates and acquires plus free and used connections
getPoolMetrics() {
return {
numPendingCreates: this.pool.numPendingCreates(),
numPendingAcquires: this.pool.numPendingAcquires(),
numFree: this.pool.numFree(),
numUsed: this.pool.numUsed()
};
}
handleCreateRequest(eventId) {
// Track when the request was submitted for calculating wait time
this.createStartTimes[eventId] = Date.now();
const poolMetrics = this.getPoolMetrics();
this.logging.debug(`[ConnectionPool] Creating a connection. EventID: ${eventId} Pending Creates: ${poolMetrics.numPendingCreates} Free: ${poolMetrics.numFree} Used: ${poolMetrics.numUsed}`);
this.metrics.metric('connection-pool', {
event: 'create-request',
eventId,
...poolMetrics
});
}
handleCreateSuccess(eventId, resource) {
// Calculate the time it took to create the connection
const timeToCreate = Date.now() - this.createStartTimes[eventId];
// Delete the start time so we don't leak memory
delete this.createStartTimes[eventId];
this.logging.debug(`[ConnectionPool] Created a connection. EventID: ${eventId} Connection ID: ${resource.connectionId} Knex ID: ${resource.__knexUid} Time to Create: ${timeToCreate}ms`);
this.metrics.metric('connection-pool', {
event: 'create-success',
eventId,
connectionId: resource.connectionId,
knexUid: resource.__knexUid,
timeToCreate
});
}
handleCreateFail(eventId, err) {
// Calculate the time it took to create the connection
const timeToFail = Date.now() - this.createStartTimes[eventId];
// Delete the start time so we don't leak memory
delete this.createStartTimes[eventId];
const poolMetrics = this.getPoolMetrics();
this.logging.error(`[ConnectionPool] Failed to create a connection. EventID: ${eventId} Time to Create: ${timeToFail}ms`, err);
this.metrics.metric('connection-pool', {
event: 'create-fail',
eventId,
timeToFail,
...poolMetrics
});
}
handleAcquireRequest(eventId) {
// Track when the request was submitted for calculating wait time
this.requestStartTimes[eventId] = Date.now();
const poolMetrics = this.getPoolMetrics();
this.logging.debug(`[ConnectionPool] Acquiring a connection. EventID: ${eventId} Pending Acquires: ${poolMetrics.numPendingAcquires} Free: ${poolMetrics.numFree} Used: ${poolMetrics.numUsed}`);
this.metrics.metric('connection-pool', {
event: 'acquire-request',
eventId,
...poolMetrics
});
}
handleAcquireSuccess(eventId, resource) {
// Calculate the time it took to acquire the connection
const timeToAcquire = Date.now() - this.requestStartTimes[eventId];
// Delete the start time so we don't leak memory
delete this.requestStartTimes[eventId];
// Track when the connection was acquired for calculating lifetime upon release
resource.connectionAcquired = Date.now();
this.logging.debug(`[ConnectionPool] Acquired a connection. EventID: ${eventId} Connection ID: ${resource.connectionId} Knex Id: ${resource.__knexUid} Time to Acquire: ${timeToAcquire}ms`);
this.metrics.metric('connection-pool', {
event: 'acquire-success',
eventId,
connectionId: resource.connectionId,
knexUid: resource.__knexUid,
timeToAcquire
});
}
handleAcquireFail(eventId, err) {
// Calculate the time it took to acquire the connection
const timeToFail = Date.now() - this.requestStartTimes[eventId];
// Delete the start time so we don't leak memory
delete this.requestStartTimes[eventId];
const poolMetrics = this.getPoolMetrics();
this.logging.error(`[ConnectionPool] Failed to acquire a connection. EventID: ${eventId} Time to Acquire: ${timeToFail}ms`, err);
this.metrics.metric('connection-pool', {
event: 'acquire-fail',
eventId,
timeToFail,
...poolMetrics
});
}
handleRelease(resource) {
const lifetime = Date.now() - resource.connectionAcquired;
this.logging.debug(`[ConnectionPool] Released a connection. Connection ID: ${resource.connectionId} Lifetime: ${lifetime}ms`);
this.metrics.metric('connection-pool', {
event: 'release',
connectionId: resource.connectionId,
knexUid: resource.__knexUid,
lifetime
});
}
instrument() {
if (this.enabled) {
// Check to make sure these event listeners haven't already been added
if (this.pool.emitter.eventNames().length === 0) {
// Fires when a connection is requested to be created
this.pool.on('createRequest', eventId => this.handleCreateRequest(eventId));
// Fires when a connection is successfully created
this.pool.on('createSuccess', (eventId, resource) => this.handleCreateSuccess(eventId, resource));
// Fires when a connection fails to be created
this.pool.on('createFail', (eventId, err) => this.handleCreateFail(eventId, err));
// Fires when a connection is requested from the pool
this.pool.on('acquireRequest', eventId => this.handleAcquireRequest(eventId));
// Fires when a connection is allocated from the pool
this.pool.on('acquireSuccess', (eventId, resource) => this.handleAcquireSuccess(eventId, resource));
// Fires when a connection fails to be allocated from the pool
this.pool.on('acquireFail', (eventId, err) => this.handleAcquireFail(eventId, err));
// Fires when a connection is released back into the pool
this.pool.on('release', resource => this.handleRelease(resource));
}
}
}
}
module.exports = ConnectionPoolInstrumentation;

View file

@ -4,10 +4,8 @@ const os = require('os');
const fs = require('fs');
const logging = require('@tryghost/logging');
const metrics = require('@tryghost/metrics');
const config = require('../../../shared/config');
const errors = require('@tryghost/errors');
const ConnectionPoolInstrumentation = require('./ConnectionPoolInstrumentation');
let knexInstance;
// @TODO:
@ -64,10 +62,6 @@ function configure(dbConfig) {
if (!knexInstance && config.get('database') && config.get('database').client) {
knexInstance = knex(configure(config.get('database')));
if (config.get('telemetry:connectionPool')) {
const instrumentation = new ConnectionPoolInstrumentation({knex: knexInstance, logging, metrics, config});
instrumentation.instrument();
}
}
module.exports = knexInstance;

View file

@ -1,228 +0,0 @@
const sinon = require('sinon');
const ConnectionPoolInstrumentation = require('../../../../../core/server/data/db/ConnectionPoolInstrumentation');
const should = require('should');
var EventEmitter = require('events').EventEmitter;
describe('ConnectionPoolInstrumentation', function () {
afterEach(function () {
sinon.restore();
});
it('getPoolMetrics', async function () {
const knex = {
client: {
pool: {
numPendingCreates: sinon.stub().returns(1),
numPendingAcquires: sinon.stub().returns(2),
numFree: sinon.stub().returns(3),
numUsed: sinon.stub().returns(4)
}
}
};
const logging = {};
const metrics = {};
const config = {
get: sinon.stub().withArgs('telemetry:connectionPool').returns(true)
};
const instrumentation = new ConnectionPoolInstrumentation({knex, logging, metrics, config});
const result = instrumentation.getPoolMetrics();
result.should.eql({
numPendingCreates: 1,
numPendingAcquires: 2,
numFree: 3,
numUsed: 4
});
});
it('should handle creating a request successfully', async function () {
const knex = {
client: {
pool: {
numPendingCreates: sinon.stub().returns(1),
numPendingAcquires: sinon.stub().returns(2),
numFree: sinon.stub().returns(3),
numUsed: sinon.stub().returns(4)
}
}
};
const logging = {
debug: sinon.stub()
};
const metrics = {
metric: sinon.stub()
};
const config = {
get: sinon.stub().withArgs('telemetry:connectionPool').returns(true)
};
const instrumentation = new ConnectionPoolInstrumentation({knex, logging, metrics, config});
instrumentation.handleCreateRequest(1);
instrumentation.handleCreateSuccess(1, {connectionId: 2, __knexUid: 3});
logging.debug.calledTwice.should.be.true();
metrics.metric.calledTwice.should.be.true();
});
it('should handle creating a request unsuccessfully', async function () {
const knex = {
client: {
pool: {
numPendingCreates: sinon.stub().returns(1),
numPendingAcquires: sinon.stub().returns(2),
numFree: sinon.stub().returns(3),
numUsed: sinon.stub().returns(4)
}
}
};
const logging = {
debug: sinon.stub(),
error: sinon.stub()
};
const metrics = {
metric: sinon.stub()
};
const config = {
get: sinon.stub().withArgs('telemetry:connectionPool').returns(true)
};
const instrumentation = new ConnectionPoolInstrumentation({knex, logging, metrics, config});
instrumentation.handleCreateRequest(1);
instrumentation.handleCreateFail(1, new Error('test'));
logging.error.calledOnce.should.be.true();
metrics.metric.calledTwice.should.be.true();
});
it('should handle acquiring a connection successfully', async function () {
const knex = {
client: {
pool: {
numPendingCreates: sinon.stub().returns(1),
numPendingAcquires: sinon.stub().returns(2),
numFree: sinon.stub().returns(3),
numUsed: sinon.stub().returns(4)
}
}
};
const logging = {
debug: sinon.stub()
};
const metrics = {
metric: sinon.stub()
};
const config = {
get: sinon.stub().withArgs('telemetry:connectionPool').returns(true)
};
const instrumentation = new ConnectionPoolInstrumentation({knex, logging, metrics, config});
instrumentation.handleAcquireRequest(1);
instrumentation.handleAcquireSuccess(1, {connectionId: 2, __knexUid: 3});
logging.debug.calledTwice.should.be.true();
metrics.metric.calledTwice.should.be.true();
});
it('should handle acquiring a connection unsuccessfully', async function () {
const knex = {
client: {
pool: {
numPendingCreates: sinon.stub().returns(1),
numPendingAcquires: sinon.stub().returns(2),
numFree: sinon.stub().returns(3),
numUsed: sinon.stub().returns(4)
}
}
};
const logging = {
debug: sinon.stub(),
error: sinon.stub()
};
const metrics = {
metric: sinon.stub()
};
const config = {
get: sinon.stub().withArgs('telemetry:connectionPool').returns(true)
};
const instrumentation = new ConnectionPoolInstrumentation({knex, logging, metrics, config});
instrumentation.handleAcquireRequest(1);
instrumentation.handleAcquireFail(1, new Error('test'));
logging.error.calledOnce.should.be.true();
metrics.metric.calledTwice.should.be.true();
});
it('should handle releasing a connection successfully', async function () {
const knex = {
client: {
pool: {
numPendingCreates: sinon.stub().returns(1),
numPendingAcquires: sinon.stub().returns(2),
numFree: sinon.stub().returns(3),
numUsed: sinon.stub().returns(4)
}
}
};
const logging = {
debug: sinon.stub()
};
const metrics = {
metric: sinon.stub()
};
const config = {
get: sinon.stub().withArgs('telemetry:connectionPool').returns(true)
};
const instrumentation = new ConnectionPoolInstrumentation({knex, logging, metrics, config});
instrumentation.handleRelease(1);
logging.debug.calledOnce.should.be.true();
metrics.metric.calledOnce.should.be.true();
});
it('should instrument the connection pool if enabled', async function () {
const knex = {
client: {
pool: {
numPendingCreates: sinon.stub().returns(1),
numPendingAcquires: sinon.stub().returns(2),
numFree: sinon.stub().returns(3),
numUsed: sinon.stub().returns(4),
on: sinon.stub(),
emitter: new EventEmitter()
}
}
};
const logging = {
debug: sinon.stub(),
error: sinon.stub()
};
const metrics = {
metric: sinon.stub()
};
const config = {
get: sinon.stub().withArgs('telemetry:connectionPool').returns(true)
};
const instrumentation = new ConnectionPoolInstrumentation({knex, logging, metrics, config});
instrumentation.instrument();
knex.client.pool.on.callCount.should.eql(7);
});
it('should not instrument the connection pool if disabled', async function () {
const knex = {
client: {
pool: {
numPendingCreates: sinon.stub().returns(1),
numPendingAcquires: sinon.stub().returns(2),
numFree: sinon.stub().returns(3),
numUsed: sinon.stub().returns(4),
on: sinon.stub(),
emitter: new EventEmitter()
}
}
};
const logging = {
debug: sinon.stub(),
error: sinon.stub()
};
const metrics = {
metric: sinon.stub()
};
const config = {
get: sinon.stub().withArgs('telemetry:connectionPool').returns(false)
};
const instrumentation = new ConnectionPoolInstrumentation({knex, logging, metrics, config});
instrumentation.instrument();
knex.client.pool.on.callCount.should.eql(0);
});
});