mirror of
https://github.com/fosrl/pangolin.git
synced 2026-01-28 22:00:51 +00:00
Fix: Prevent cache memory leak by adding maxKeys limit and conditional caching
- Add maxKeys limit (10,000) to NodeCache to prevent unbounded memory growth - Skip caching undefined values when GeoIP/ASN lookups fail (e.g., when MaxMind DB not configured) - Add periodic cache statistics logging every 5 minutes for monitoring - Fixes memory leak where cache would grow indefinitely with high request volumes The maxKeys limit uses LRU eviction, so oldest entries are automatically removed when the limit is reached. With ~10k requests/day and 5min TTL, 10k keys provides ample headroom while preventing OOM issues. Fixes #2120
This commit is contained in:
committed by
Owen Schwartz
parent
9e68c6c004
commit
90c48f20e0
@@ -1,5 +1,20 @@
|
|||||||
import NodeCache from "node-cache";
|
import NodeCache from "node-cache";
|
||||||
|
import logger from "@server/logger";
|
||||||
|
|
||||||
export const cache = new NodeCache({ stdTTL: 3600, checkperiod: 120 });
|
// Create cache with maxKeys limit to prevent memory leaks
|
||||||
|
// With ~10k requests/day and 5min TTL, 10k keys should be more than sufficient
|
||||||
|
export const cache = new NodeCache({
|
||||||
|
stdTTL: 3600,
|
||||||
|
checkperiod: 120,
|
||||||
|
maxKeys: 10000
|
||||||
|
});
|
||||||
|
|
||||||
|
// Log cache statistics periodically for monitoring
|
||||||
|
setInterval(() => {
|
||||||
|
const stats = cache.getStats();
|
||||||
|
logger.debug(
|
||||||
|
`Cache stats - Keys: ${stats.keys}, Hits: ${stats.hits}, Misses: ${stats.misses}, Hit rate: ${stats.hits > 0 ? ((stats.hits / (stats.hits + stats.misses)) * 100).toFixed(2) : 0}%`
|
||||||
|
);
|
||||||
|
}, 300000); // Every 5 minutes
|
||||||
|
|
||||||
export default cache;
|
export default cache;
|
||||||
|
|||||||
@@ -161,8 +161,11 @@ async function getCountryCodeFromIp(ip: string): Promise<string | undefined> {
|
|||||||
|
|
||||||
if (!cachedCountryCode) {
|
if (!cachedCountryCode) {
|
||||||
cachedCountryCode = await getCountryCodeForIp(ip); // do it locally
|
cachedCountryCode = await getCountryCodeForIp(ip); // do it locally
|
||||||
// Cache for longer since IP geolocation doesn't change frequently
|
// Only cache successful lookups to avoid filling cache with undefined values
|
||||||
cache.set(geoIpCacheKey, cachedCountryCode, 300); // 5 minutes
|
if (cachedCountryCode) {
|
||||||
|
// Cache for longer since IP geolocation doesn't change frequently
|
||||||
|
cache.set(geoIpCacheKey, cachedCountryCode, 300); // 5 minutes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cachedCountryCode;
|
return cachedCountryCode;
|
||||||
|
|||||||
@@ -1206,8 +1206,11 @@ async function getCountryCodeFromIp(ip: string): Promise<string | undefined> {
|
|||||||
|
|
||||||
if (!cachedCountryCode) {
|
if (!cachedCountryCode) {
|
||||||
cachedCountryCode = await getCountryCodeForIp(ip); // do it locally
|
cachedCountryCode = await getCountryCodeForIp(ip); // do it locally
|
||||||
// Cache for longer since IP geolocation doesn't change frequently
|
// Only cache successful lookups to avoid filling cache with undefined values
|
||||||
cache.set(geoIpCacheKey, cachedCountryCode, 300); // 5 minutes
|
if (cachedCountryCode) {
|
||||||
|
// Cache for longer since IP geolocation doesn't change frequently
|
||||||
|
cache.set(geoIpCacheKey, cachedCountryCode, 300); // 5 minutes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cachedCountryCode;
|
return cachedCountryCode;
|
||||||
|
|||||||
Reference in New Issue
Block a user