Skip to content

Commit aba2dab

Browse files
committed
fix(metrics): added Redis Latency metric
1 parent 56c6da2 commit aba2dab

File tree

4 files changed

+105
-3
lines changed

4 files changed

+105
-3
lines changed

lib/consts.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ module.exports = {
155155

156156
NONCE_BYTES: 16,
157157

158+
// milliseconds
159+
ALLOWED_REDIS_LATENCY: 15,
160+
158161
generateWebhookTable() {
159162
let entries = [];
160163

lib/routes-ui.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ const {
7272
DEFAULT_DELIVERY_ATTEMPTS,
7373
DEFAULT_MAX_BODY_SIZE,
7474
MAX_FORM_TTL,
75-
NONCE_BYTES
75+
NONCE_BYTES,
76+
ALLOWED_REDIS_LATENCY
7677
} = consts;
7778

7879
config.api = config.api || {
@@ -835,7 +836,15 @@ function applyRoutes(server, call) {
835836
menuDashboard: true,
836837
stats,
837838
counterList,
838-
hasAccounts
839+
hasAccounts,
840+
redisPing: {
841+
key: 'redisPing',
842+
title: 'Redis Latency',
843+
color: typeof stats.redisPing !== 'number' ? 'warning' : stats.redisPing < ALLOWED_REDIS_LATENCY ? 'success' : 'danger',
844+
icon: 'clock',
845+
comment: 'How many milliseconds does it take to run Redis commands',
846+
value: typeof stats.redisPing !== 'number' ? '\u2013' : humanize.numberFormat(stats.redisPing || 0, 0, '.', ' ')
847+
}
839848
},
840849
{
841850
layout: 'app'

server.js

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,11 @@ const metrics = {
378378
help: 'Redis uptime in seconds'
379379
}),
380380

381+
redisPing: new promClient.Gauge({
382+
name: 'redis_latency',
383+
help: 'Redis latency in milliseconds'
384+
}),
385+
381386
redisRejectedConnectionsTotal: new promClient.Gauge({
382387
name: 'redis_rejected_connections_total',
383388
help: 'Number of connections rejected by Redis'
@@ -1212,6 +1217,64 @@ function checkUpgrade() {
12121217
});
12131218
}
12141219

1220+
// measure Redis ping once in every 10 seconds
1221+
1222+
let redisPingCounter = [];
1223+
1224+
function getRedisPing() {
1225+
if (redisPingCounter.length < 14) {
1226+
return null;
1227+
}
1228+
let entries = redisPingCounter
1229+
.slice(-34)
1230+
.map(entry => entry[1])
1231+
.sort((a, b) => a - b);
1232+
1233+
// remove 2 highest and lowest
1234+
entries.shift();
1235+
entries.shift();
1236+
entries.pop();
1237+
entries.pop();
1238+
1239+
let sum = 0;
1240+
for (let entry of entries) {
1241+
sum += entry;
1242+
}
1243+
1244+
return Math.round(sum / entries.length);
1245+
}
1246+
1247+
const REDIS_PING_TIMEOUT = 10 * 1000;
1248+
let redisPingTimer = false;
1249+
const processRedisPing = async () => {
1250+
try {
1251+
let startTime = Date.now();
1252+
await redis.ping();
1253+
let endTime = Date.now();
1254+
let duration = endTime - startTime;
1255+
redisPingCounter.push([endTime, duration]);
1256+
if (redisPingCounter.length > 300) {
1257+
redisPingCounter = redisPingCounter.slice(0, 150);
1258+
}
1259+
return duration;
1260+
} catch (err) {
1261+
logger.error({ msg: 'Failed to run Redis ping', err });
1262+
}
1263+
};
1264+
1265+
const redisPingHandler = async () => {
1266+
await processRedisPing();
1267+
redisPingTimer = setTimeout(checkRedisPing, REDIS_PING_TIMEOUT);
1268+
redisPingTimer.unref();
1269+
};
1270+
1271+
function checkRedisPing() {
1272+
clearTimeout(redisPingTimer);
1273+
redisPingHandler().catch(err => {
1274+
logger.error('Failed to process Redis Ping', err);
1275+
});
1276+
}
1277+
12151278
async function updateQueueCounters() {
12161279
metrics.emailengineConfig.set({ version: 'v' + packageData.version }, 1);
12171280
metrics.emailengineConfig.set({ config: 'uvThreadpoolSize' }, Number(process.env.UV_THREADPOOL_SIZE));
@@ -1245,6 +1308,9 @@ async function updateQueueCounters() {
12451308
metrics.redisVersion.set({ version: 'v' + redisInfo.redis_version }, 1);
12461309

12471310
metrics.redisUptimeInSeconds.set(Number(redisInfo.uptime_in_seconds) || 0);
1311+
1312+
metrics.redisPing.set((await processRedisPing()) || 0);
1313+
12481314
metrics.redisRejectedConnectionsTotal.set(Number(redisInfo.rejected_connections) || 0);
12491315
metrics.redisConfigMaxclients.set(Number(redisInfo.maxclients) || 0);
12501316
metrics.redisConnectedClients.set(Number(redisInfo.connected_clients) || 0);
@@ -1306,7 +1372,7 @@ async function onCommand(worker, message) {
13061372
}
13071373
}
13081374

1309-
return { connections };
1375+
return { connections, redisPing: await getRedisPing() };
13101376
}
13111377

13121378
case 'imapWorkerCount': {
@@ -2176,6 +2242,9 @@ startApplication()
21762242
upgradeCheckTimer = setTimeout(checkUpgrade, UPGRADE_CHECK_TIMEOUT);
21772243
upgradeCheckTimer.unref();
21782244

2245+
redisPingTimer = setTimeout(checkRedisPing, REDIS_PING_TIMEOUT);
2246+
redisPingTimer.unref();
2247+
21792248
queueEvents.notify = new QueueEvents('notify', Object.assign({}, queueConf));
21802249
queueEvents.submit = new QueueEvents('submit', Object.assign({}, queueConf));
21812250
queueEvents.documents = new QueueEvents('documents', Object.assign({}, queueConf));

views/dashboard.hbs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,27 @@
211211
</div>
212212
{{/if}}
213213

214+
<div class="col-xl-3 col-md-6 mb-4">
215+
<div class="card border-left-{{redisPing.color}} shadow">
216+
<div class="card-body">
217+
<div class="row no-gutters align-items-center">
218+
<div class="col mr-2">
219+
<div class="text-xs font-weight-bold text-gray-800 text-uppercase mb-1" {{#if
220+
redisPing.comment}}title="{{redisPing.comment}}" data-toggle="tooltip"
221+
data-placement="top" {{/if}} style="cursor: default;">
222+
{{redisPing.title}}</div>
223+
<div class="h5 mb-0 font-weight-bold text-gray-800">
224+
{{redisPing.value}}
225+
</div>
226+
</div>
227+
<div class="col-auto">
228+
<i class="fas fa-{{redisPing.icon}} fa-2x text-gray-300"></i>
229+
</div>
230+
</div>
231+
</div>
232+
</div>
233+
</div>
234+
214235
</div>
215236

216237

0 commit comments

Comments
 (0)