mirror of
https://github.com/fosrl/pangolin.git
synced 2026-09-11 05:26:32 +02:00
switch to lru in memory cache and dont cache failed sessions
This commit is contained in:
+1
-1
@@ -96,6 +96,7 @@
|
||||
"jmespath": "0.16.0",
|
||||
"js-yaml": "5.4.1",
|
||||
"jsonwebtoken": "9.0.3",
|
||||
"lru-cache": "11.5.2",
|
||||
"lucide-react": "1.38.0",
|
||||
"maxmind": "5.0.7",
|
||||
"moment": "2.30.1",
|
||||
@@ -103,7 +104,6 @@
|
||||
"next-intl": "4.14.1",
|
||||
"next-themes": "0.4.6",
|
||||
"nextjs-toploader": "3.9.17",
|
||||
"node-cache": "5.1.2",
|
||||
"nodemailer": "9.1.0",
|
||||
"oslo": "1.2.1",
|
||||
"pg": "8.23.0",
|
||||
|
||||
+2
-8
@@ -1,13 +1,7 @@
|
||||
import NodeCache from "node-cache";
|
||||
import logger from "@server/logger";
|
||||
import { createLocalCache } from "@server/lib/createLocalCache";
|
||||
|
||||
// Create local cache with maxKeys limit to prevent memory leaks
|
||||
// With ~10k requests/day and 5min TTL, 10k keys should be more than sufficient
|
||||
export const localCache = new NodeCache({
|
||||
stdTTL: 3600,
|
||||
checkperiod: 120,
|
||||
maxKeys: 10000
|
||||
});
|
||||
export const localCache = createLocalCache();
|
||||
|
||||
// Log cache statistics periodically for monitoring
|
||||
// setInterval(() => {
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import { LRUCache } from "lru-cache";
|
||||
|
||||
const DEFAULT_MAX_KEYS = 10000;
|
||||
const DEFAULT_TTL_MS = 3600 * 1000;
|
||||
|
||||
export type LocalCache = {
|
||||
get<T>(key: string): T | undefined;
|
||||
set(key: string, value: unknown, ttlSeconds?: number): boolean;
|
||||
del(key: string | string[]): number;
|
||||
has(key: string): boolean;
|
||||
keys(): string[];
|
||||
flushAll(): void;
|
||||
getStats(): { keys: number };
|
||||
getTtl(key: string): number | undefined;
|
||||
};
|
||||
|
||||
export function createLocalCache(max = DEFAULT_MAX_KEYS): LocalCache {
|
||||
const lru = new LRUCache<string, {}>({
|
||||
max,
|
||||
ttl: DEFAULT_TTL_MS,
|
||||
updateAgeOnGet: false
|
||||
});
|
||||
|
||||
return {
|
||||
get<T>(key: string): T | undefined {
|
||||
return lru.get(key) as T | undefined;
|
||||
},
|
||||
|
||||
set(key: string, value: unknown, ttlSeconds?: number): boolean {
|
||||
const stored = value as {};
|
||||
if (ttlSeconds === undefined) {
|
||||
lru.set(key, stored);
|
||||
} else if (ttlSeconds === 0) {
|
||||
lru.set(key, stored, { ttl: 0 });
|
||||
} else {
|
||||
lru.set(key, stored, { ttl: ttlSeconds * 1000 });
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
del(key: string | string[]): number {
|
||||
const keys = Array.isArray(key) ? key : [key];
|
||||
let deleted = 0;
|
||||
for (const k of keys) {
|
||||
if (lru.delete(k)) {
|
||||
deleted++;
|
||||
}
|
||||
}
|
||||
return deleted;
|
||||
},
|
||||
|
||||
has(key: string): boolean {
|
||||
return lru.has(key);
|
||||
},
|
||||
|
||||
keys(): string[] {
|
||||
return [...lru.keys()];
|
||||
},
|
||||
|
||||
flushAll(): void {
|
||||
lru.clear();
|
||||
},
|
||||
|
||||
getStats(): { keys: number } {
|
||||
return { keys: lru.size };
|
||||
},
|
||||
|
||||
getTtl(key: string): number | undefined {
|
||||
if (!lru.has(key)) {
|
||||
return undefined;
|
||||
}
|
||||
const remaining = lru.getRemainingTTL(key);
|
||||
if (!Number.isFinite(remaining)) {
|
||||
return 0;
|
||||
}
|
||||
return Date.now() + remaining;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -11,17 +11,11 @@
|
||||
* This file is not licensed under the AGPLv3.
|
||||
*/
|
||||
|
||||
import NodeCache from "node-cache";
|
||||
import logger from "@server/logger";
|
||||
import { createLocalCache } from "@server/lib/createLocalCache";
|
||||
import { redisManager, regionalRedisManager } from "@server/private/lib/redis";
|
||||
|
||||
// Create local cache with maxKeys limit to prevent memory leaks
|
||||
// With ~10k requests/day and 5min TTL, 10k keys should be more than sufficient
|
||||
export const localCache = new NodeCache({
|
||||
stdTTL: 3600,
|
||||
checkperiod: 120,
|
||||
maxKeys: 10000
|
||||
});
|
||||
export const localCache = createLocalCache();
|
||||
|
||||
// Log cache statistics periodically for monitoring
|
||||
// setInterval(() => {
|
||||
@@ -301,15 +295,11 @@ export default cache;
|
||||
|
||||
/**
|
||||
* Regional adaptive cache backed by the in-cluster Redis instance.
|
||||
* Falls back to a local NodeCache when the regional Redis is unavailable.
|
||||
* Falls back to a local LRU cache when the regional Redis is unavailable.
|
||||
* Use this for data that is regional in nature (e.g. status history) so
|
||||
* reads are served from the same cluster the user is hitting.
|
||||
*/
|
||||
const regionalLocalCache = new NodeCache({
|
||||
stdTTL: 3600,
|
||||
checkperiod: 120,
|
||||
maxKeys: 10000
|
||||
});
|
||||
const regionalLocalCache = createLocalCache();
|
||||
|
||||
class RegionalAdaptiveCache {
|
||||
private useRedis(): boolean {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
import { db, HostMeta, sites, users } from "@server/db";
|
||||
import { hostMeta, licenseKey } from "@server/db";
|
||||
import logger from "@server/logger";
|
||||
import NodeCache from "node-cache";
|
||||
import { createLocalCache } from "@server/lib/createLocalCache";
|
||||
import { validateJWT } from "./licenseJwt";
|
||||
import { count, eq } from "drizzle-orm";
|
||||
import moment from "moment";
|
||||
@@ -65,8 +65,8 @@ export class License {
|
||||
private validationServerUrl = `${this.serverBaseUrl}/api/v1/license/enterprise/validate`;
|
||||
private activationServerUrl = `${this.serverBaseUrl}/api/v1/license/enterprise/activate`;
|
||||
|
||||
private statusCache = new NodeCache();
|
||||
private licenseKeyCache = new NodeCache();
|
||||
private statusCache = createLocalCache();
|
||||
private licenseKeyCache = createLocalCache();
|
||||
|
||||
private statusKey = "status";
|
||||
private serverSecret!: string;
|
||||
@@ -179,7 +179,7 @@ LQIDAQAB
|
||||
status.isHostLicensed = false;
|
||||
// Invalidate all and set new cache (empty)
|
||||
this.licenseKeyCache.flushAll();
|
||||
this.statusCache.set(this.statusKey, status);
|
||||
this.statusCache.set(this.statusKey, status, 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -389,7 +389,7 @@ LQIDAQAB
|
||||
// Invalidate old cache and set new cache
|
||||
this.licenseKeyCache.flushAll();
|
||||
for (const [key, value] of newCache.entries()) {
|
||||
this.licenseKeyCache.set<LicenseKeyCache>(key, value);
|
||||
this.licenseKeyCache.set(key, value, 0);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error("Error checking license status:");
|
||||
@@ -398,7 +398,7 @@ LQIDAQAB
|
||||
this.checkInProgress = false;
|
||||
}
|
||||
|
||||
this.statusCache.set(this.statusKey, status);
|
||||
this.statusCache.set(this.statusKey, status, 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -691,7 +691,9 @@ export async function verifyResourceSession(
|
||||
);
|
||||
|
||||
resourceSession = result?.resourceSession;
|
||||
localCache.set(sessionCacheKey, resourceSession, 5);
|
||||
if (resourceSession) {
|
||||
localCache.set(sessionCacheKey, resourceSession, 5);
|
||||
}
|
||||
}
|
||||
|
||||
if (resourceSession?.isRequestToken) {
|
||||
@@ -1121,7 +1123,9 @@ async function allowAccessToken(
|
||||
resource.resourceId
|
||||
);
|
||||
resourceSession = result?.resourceSession;
|
||||
localCache.set(sessionCacheKey, resourceSession, 5);
|
||||
if (resourceSession) {
|
||||
localCache.set(sessionCacheKey, resourceSession, 5);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
|
||||
Reference in New Issue
Block a user