fix(#338): Log ERROR on rate limiter fallback and track degraded mode

- Log at ERROR level when falling back to in-memory storage
- Track and expose degraded mode status for health checks
- Add isUsingFallback() method to check fallback state
- Add getHealthStatus() method for health check endpoints
- Add comprehensive tests for fallback behavior and health status

Refs #338

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-02-05 16:39:55 -06:00
parent 53f2cd7f47
commit 7ae92f3e1c
2 changed files with 302 additions and 2 deletions

View File

@@ -53,8 +53,11 @@ export class ThrottlerValkeyStorageService implements ThrottlerStorage, OnModule
this.logger.log("Valkey connected successfully for rate limiting");
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.warn(`Failed to connect to Valkey for rate limiting: ${errorMessage}`);
this.logger.warn("Falling back to in-memory rate limiting storage");
this.logger.error(`Failed to connect to Valkey for rate limiting: ${errorMessage}`);
this.logger.error(
"DEGRADED MODE: Falling back to in-memory rate limiting storage. " +
"Rate limits will not be shared across API instances."
);
this.useRedis = false;
this.client = undefined;
}
@@ -168,6 +171,46 @@ export class ThrottlerValkeyStorageService implements ThrottlerStorage, OnModule
return `${this.THROTTLER_PREFIX}${key}`;
}
/**
* Check if the service is using fallback in-memory storage
*
* This indicates a degraded state where rate limits are not shared
* across API instances. Use this for health checks.
*
* @returns true if using in-memory fallback, false if using Redis
*/
isUsingFallback(): boolean {
return !this.useRedis;
}
/**
* Get rate limiter health status for health check endpoints
*
* @returns Health status object with storage mode and details
*/
getHealthStatus(): {
healthy: boolean;
mode: "redis" | "memory";
degraded: boolean;
message: string;
} {
if (this.useRedis) {
return {
healthy: true,
mode: "redis",
degraded: false,
message: "Rate limiter using Redis storage (distributed mode)",
};
}
return {
healthy: true, // Service is functional, but degraded
mode: "memory",
degraded: true,
message:
"Rate limiter using in-memory fallback (degraded mode - limits not shared across instances)",
};
}
/**
* Clean up on module destroy
*/