terminus
terminus copied to clipboard
Implementation of bullHealthcheck
hi,
I have a working implementation of health check for @nest/bull
Should I PR here or release it in another place ?
To check liveliness, I use ´queue.client.ping()´ from redis io, is it a correct approach ?
Hi @ajubin
Maybe you have a health check for nest-redis as well?
It's basically a check on whether the redis client answers to the ping. But the implementation is based on bull dependency injection to get client configuration
I can try to port it over to nest-redis
Why not :) it shouldn't be too complicated (by injecting redis connection directly). I will keep you updated when the PR arrives :)
I have a lib for redis with nestjs, support inject or get client via namespace, and health check.
https://github.com/liaoliaots/nestjs-redis
@liaoliaots This module is so good, thank you so much
Sorry for the delay. I'd be happy to accept this as a PR! Though if possible, I'd recommend you to post the public API (how the user will use this healthcheck) here upfront, so I can already evaluate it and save you some time!
Hello! @ajubin any updates? :)
I was looking for a redis/bull implementation and I saw this issue
Hi guys, any updates?
🥲 up
Just for anyone else looking for a solution, I'm currently using the MicroserviceHealthIndicator
from @nestjs/microservices
likes this:
() =>
this.microserviceHealthIndicator.pingCheck<RedisOptions>('redis', {
transport: Transport.REDIS,
options: {
// Set your options just like you configure your BullModule
host: this.configService.get('REDIS_HOST'),
port: this.configService.get('REDIS_PORT'),
},
}),
I encounter a problem using url
option. It looks like this works :
options: {
host: this.configService.get('REDIS_HOST'),
port: this.configService.get('REDIS_PORT'),
},
but not :
options: {
url: 'redis://'+this.configService.get('REDIS_HOST')+':'+this.configService.get('REDIS_PORT'),
},
and I did not get why.
@stouch I guess you’re using Nest v9? If so, there is a breaking change & urls are not supported with Redis anymore
https://docs.nestjs.com/migration-guide#redis-strategy-microservices
Hey guys I know I am late to this but you can easily create a health check using the following service
import { Injectable } from '@nestjs/common';
import {
HealthCheckError,
HealthIndicator,
HealthIndicatorResult,
} from '@nestjs/terminus';
import { QueueNames } from '../queue-names';
import { InjectQueue } from '@nestjs/bull';
import { Queue } from 'bull';
@Injectable()
export class QueueHealthService extends HealthIndicator {
constructor(
@InjectQueue(QueueNames.General) protected generalQueue: Queue,
@InjectQueue(QueueNames.Emails) protected emailQueue: Queue,
@InjectQueue(QueueNames.PushNotifications)
protected pushNotificationQueue: Queue,
) {
super();
}
public queueMap(queueName: QueueNames): Queue {
switch (queueName) {
case QueueNames.Emails:
return this.emailQueue;
case QueueNames.General:
return this.generalQueue;
case QueueNames.PushNotifications:
return this.pushNotificationQueue;
}
}
public async isQueueHealthy(
queueName: QueueNames,
): Promise<HealthIndicatorResult> {
const queue: Queue = this.queueMap(queueName);
const isHealthy = !!(await queue.isReady());
const data = {
currentStatus: queue.client.status,
totalJobs: await queue.count().catch(() => null),
};
const result = this.getStatus(
this.healthKeyName(queueName),
!!(await queue.isReady()),
data,
);
if (!isHealthy) {
throw new HealthCheckError(`Queue ${queueName} is not connected`, result);
}
return result;
}
/**
* Generates the queue name
* @param queue
* @protected
*/
protected healthKeyName(queue: QueueNames): `redis-queue:${QueueNames}` {
return `redis-queue:${queue}`;
}
}
and then in the Helthc controller
@Get()
@HealthCheck()
public check() {
const checks: HealthIndicatorFunction[] = [];
checks.push(
() => this.queueHealth.isQueueHealthy(QueueNames.General),
() => this.queueHealth.isQueueHealthy(QueueNames.PushNotifications),
() => this.queueHealth.isQueueHealthy(QueueNames.Emails),
);
return this.health.check(checks);
}
Hi all,
I'm also late to this, but I came across this thread over the weekend while looking into implementing a RedisHealthIndicator
for an application. This application uses @nestjs/bullmq and @nestjs/cache-manager. The issue with the existing MicroserviceHealthIndicator is that it doesn't support Redis Clusters (as far as I could tell).
I imagine it would look similar to this where the REDIS_OPTIONS_TYPE
is specified in via a dynamic module. For simplicity, ioredis is used since Bullmq is now using it and Bull is in maintenance mode.
@Injectable()
export class RedisHealthIndicator
extends HealthIndicator
implements OnApplicationShutdown
{
private connection: Redis | Cluster;
constructor(@Inject(REDIS_OPTIONS_TOKEN) options: typeof REDIS_OPTIONS_TYPE) {
super();
this._connect(options);
}
/**
* Checks if Redis responds in the amount of time specified in module options.
* @param {string} key
* @returns {Promise<HealthIndicatorResult>}
*/
async pingCheck(key: string): Promise<HealthIndicatorResult> {
let pingError: Error;
try {
if (!this.connection) {
throw new Error('A Redis connection does not exist');
}
await this.connection.ping();
} catch (error) {
pingError = error;
}
const result = this.getStatus(key, !pingError, {
error: pingError?.message,
});
if (!pingError) {
return result;
}
throw new HealthCheckError('Redis Error', result);
}
/**
* Establishes a connection to a Redis Server or Cluster.
* @param {typeof REDIS_OPTIONS_TYPE} options
*/
private _connect(options: typeof REDIS_OPTIONS_TYPE): void {
if (options instanceof Cluster) {
this.connection = options;
} else {
this.connection = new Redis(options.port, options.host, options.options);
}
}
/**
* Lifecycle hook method that disconnects from Redis during application shutdown.
* @param {string} signal
*/
async onApplicationShutdown(signal?: string): Promise<void> {
await this.connection?.quit();
}
}
Would there be interest in the library having this functionality instead of requiring everyone to implement it separately?