fix: prevent NaN in health indicator calculations (#19378)
## Summary Fixes #19377 - **Redis health**: The hit rate calculation divides by zero when both `keyspace_hits` and `keyspace_misses` are `"0"` (common on fresh instances). The string `"0"` is truthy so the guard `statsData.keyspace_hits ? ...` doesn't catch this case, resulting in `0/0 = NaN`. Fixed by computing the total first and checking it's a valid non-zero number. - **Database health**: The cache hit ratio query returns `null` when `pg_statio_user_tables` is empty (no user tables). `parseFloat(null)` → `NaN`. Fixed by adding a null check. ## Test plan - [ ] Verify health indicators display correctly on a fresh instance with no Redis keyspace activity - [ ] Verify health indicators display correctly on a database with no user tables - [ ] Existing tests in `redis.health.spec.ts` still pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: easonysliu <easonysliu@tencent.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
+4
-1
@@ -6,6 +6,7 @@ import {
|
||||
import { InjectDataSource } from '@nestjs/typeorm';
|
||||
|
||||
import { DataSource } from 'typeorm';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { HEALTH_ERROR_MESSAGES } from 'src/engine/core-modules/admin-panel/constants/health-error-messages.constants';
|
||||
import { withHealthCheckTimeout } from 'src/engine/core-modules/admin-panel/utils/health-check-timeout.util';
|
||||
@@ -89,7 +90,9 @@ export class DatabaseHealthIndicator {
|
||||
},
|
||||
databaseSize: databaseSize.size,
|
||||
performance: {
|
||||
cacheHitRatio: Math.round(parseFloat(cacheHitRatio.ratio)) + '%',
|
||||
cacheHitRatio: isDefined(cacheHitRatio.ratio)
|
||||
? Math.round(parseFloat(cacheHitRatio.ratio)) + '%'
|
||||
: 'N/A',
|
||||
deadlocks: parseInt(deadlocks.deadlocks),
|
||||
slowQueries: parseInt(slowQueries.count),
|
||||
},
|
||||
|
||||
+15
-8
@@ -4,6 +4,8 @@ import {
|
||||
HealthIndicatorService,
|
||||
} from '@nestjs/terminus';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { HEALTH_ERROR_MESSAGES } from 'src/engine/core-modules/admin-panel/constants/health-error-messages.constants';
|
||||
import { withHealthCheckTimeout } from 'src/engine/core-modules/admin-panel/utils/health-check-timeout.util';
|
||||
import { HealthStateManager } from 'src/engine/core-modules/admin-panel/utils/health-state-manager.util';
|
||||
@@ -70,14 +72,19 @@ export class RedisHealthIndicator {
|
||||
},
|
||||
performance: {
|
||||
opsPerSecond: parseInt(statsData.instantaneous_ops_per_sec),
|
||||
hitRate: statsData.keyspace_hits
|
||||
? Math.round(
|
||||
(parseInt(statsData.keyspace_hits) /
|
||||
(parseInt(statsData.keyspace_hits) +
|
||||
parseInt(statsData.keyspace_misses))) *
|
||||
100,
|
||||
) + '%'
|
||||
: '0%',
|
||||
hitRate:
|
||||
isDefined(statsData.keyspace_hits) &&
|
||||
isDefined(statsData.keyspace_misses)
|
||||
? (() => {
|
||||
const hits = parseInt(statsData.keyspace_hits);
|
||||
const misses = parseInt(statsData.keyspace_misses);
|
||||
const total = hits + misses;
|
||||
|
||||
return total > 0
|
||||
? Math.round((hits / total) * 100) + '%'
|
||||
: '0%';
|
||||
})()
|
||||
: '0%',
|
||||
evictedKeys: parseInt(statsData.evicted_keys),
|
||||
expiredKeys: parseInt(statsData.expired_keys),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user