chore: health check improvements to avoid performance issues

- only once per minute
- concurrency 1
- use default timeout
This commit is contained in:
WebStreamr 2025-08-05 09:56:11 +00:00
parent 115e2278d8
commit 086c32f980
No known key found for this signature in database
3 changed files with 16 additions and 15 deletions

View file

@ -7,7 +7,7 @@ import { ConfigureController, ManifestController, StreamController } from './con
import { BlockedError, logErrorAndReturnNiceString } from './error';
import { createExtractors, ExtractorRegistry } from './extractor';
import { createSources } from './source';
import { contextFromRequestAndResponse, envGet, envIsProd, Fetcher, isHaydukInstance, StreamResolver } from './utils';
import { contextFromRequestAndResponse, envGet, envIsProd, Fetcher, StreamResolver } from './utils';
console.log = console.warn = console.error = console.info = console.debug = () => { /* disable in favor of logger */ };
@ -62,6 +62,7 @@ addon.get('/', (_req: Request, res: Response) => {
res.redirect('/configure');
});
let lastHealthCheckRequestsTimestamp = 0;
addon.get('/health', async (req: Request, res: Response) => {
const ctx = contextFromRequestAndResponse(req, res);
@ -74,9 +75,9 @@ addon.get('/health', async (req: Request, res: Response) => {
let blockedCount = 0;
let errorCount = 0;
const fetchPromises = urls.map(async (url) => {
const fetchFactories = urls.map(url => async () => {
try {
await fetcher.head(ctx, url, { noCache: true, timeout: 5000 });
await fetcher.head(ctx, url, { queueLimit: 1, noCache: true });
} catch (error) {
if (error instanceof BlockedError) {
blockedCount++;
@ -87,11 +88,18 @@ addon.get('/health', async (req: Request, res: Response) => {
logErrorAndReturnNiceString(ctx, logger, url.href, error);
}
});
await Promise.all(fetchPromises);
if (isHaydukInstance(req) && blockedCount > 0) {
res.status(503).json({ status: 'blocked' });
} else if (errorCount === urls.length) {
if (Date.now() - lastHealthCheckRequestsTimestamp > 60000) { // every minute
await Promise.all(fetchFactories.map(fn => fn()));
lastHealthCheckRequestsTimestamp = Date.now();
}
if (blockedCount > 0) {
// TODO: fail health check and try to get a clean IP if infra is ready
logger.warn('IP might be not clean and leading to blocking.', ctx);
}
if (errorCount === urls.length) {
res.status(503).json({ status: 'error' });
} else {
res.json({ status: 'ok' });

View file

@ -1,5 +1,5 @@
import { Request } from 'express';
import { envGet, envGetAppId, envGetAppName, envIsProd, isElfHostedInstance, isHaydukInstance } from './env';
import { envGet, envGetAppId, envGetAppName, envIsProd, isElfHostedInstance } from './env';
describe('env', () => {
test('envGet', () => {
@ -35,9 +35,4 @@ describe('env', () => {
expect(isElfHostedInstance({ host: 'someuser.elfhosted.com' } as Request)).toBeTruthy();
expect(isElfHostedInstance({ host: 'webstreamr.hayd.uk' } as Request)).toBeFalsy();
});
test('isHaydukInstance', () => {
expect(isHaydukInstance({ host: 'webstreamr.hayd.uk' } as Request)).toBeTruthy();
expect(isHaydukInstance({ host: 'someuser.elfhosted.com' } as Request)).toBeFalsy();
});
});

View file

@ -9,5 +9,3 @@ export const envGetAppName = (): string => process.env['MANIFEST_NAME'] || 'WebS
export const envIsProd = (): boolean => process.env['NODE_ENV'] === 'production';
export const isElfHostedInstance = (req: Request): boolean => req.host.endsWith('elfhosted.com');
export const isHaydukInstance = (req: Request): boolean => req.host.endsWith('hayd.uk');