refactor: use WeakMap for client singleton, ignore gql files in biome

- replace string hash cache with WeakMap keyed on rate limiter
  - auto-GC client when env released
  - exclude cloudflare/gql files from biome checks
This commit is contained in:
dmmulroy
2025-12-10 17:50:27 -05:00
parent 9f919a7138
commit 6c419cc2c6
2 changed files with 12 additions and 21 deletions

View File

@@ -11,7 +11,9 @@
"**", "**",
"!worker-configuration.d.ts", "!worker-configuration.d.ts",
"!src/gql/graphql-env.d.ts", "!src/gql/graphql-env.d.ts",
"!src/gql/schema.gql" "!src/gql/schema.gql",
"!src/cloudflare/gql/graphql-env.d.ts",
"!src/cloudflare/gql/schema.gql"
] ]
}, },
"formatter": { "formatter": {

View File

@@ -2379,23 +2379,12 @@ export class CloudflareMetricsClient {
// Singleton factory // Singleton factory
// ============================================================================= // =============================================================================
let cachedClient: CloudflareMetricsClient | null = null; const clientCache = new WeakMap<RateLimiter, CloudflareMetricsClient>();
let cachedEnvHash: string | null = null;
type RateLimiter = { type RateLimiter = {
limit: (opts: { key: string }) => Promise<{ success: boolean }>; limit: (opts: { key: string }) => Promise<{ success: boolean }>;
}; };
/**
* Generates hash from environment for client caching.
*
* @param env Environment variables.
* @returns Hash string (API token).
*/
function envHash(env: Env): string {
return env.CLOUDFLARE_API_TOKEN;
}
const MAX_RETRIES = 3; const MAX_RETRIES = 3;
const BASE_DELAY_MS = 250; const BASE_DELAY_MS = 250;
@@ -2433,16 +2422,16 @@ function createRateLimitedFetch(
} }
/** /**
* Gets or creates singleton CloudflareMetricsClient with rate limiting and env-based caching. * Gets or creates singleton CloudflareMetricsClient with rate limiting.
* Uses WeakMap keyed on rate limiter for automatic GC when env is released.
* *
* @param env Environment variables. * @param env Environment variables.
* @returns CloudflareMetricsClient singleton instance. * @returns CloudflareMetricsClient singleton instance.
*/ */
export function getCloudflareMetricsClient(env: Env): CloudflareMetricsClient { export function getCloudflareMetricsClient(env: Env): CloudflareMetricsClient {
const currentHash = envHash(env); const existing = clientCache.get(env.CF_API_RATE_LIMITER);
if (existing) {
if (cachedClient && cachedEnvHash === currentHash) { return existing;
return cachedClient;
} }
const loggerConfig = configFromEnv(env); const loggerConfig = configFromEnv(env);
@@ -2459,7 +2448,7 @@ export function getCloudflareMetricsClient(env: Env): CloudflareMetricsClient {
logger, logger,
); );
cachedClient = new CloudflareMetricsClient({ const client = new CloudflareMetricsClient({
apiToken: env.CLOUDFLARE_API_TOKEN, apiToken: env.CLOUDFLARE_API_TOKEN,
scrapeDelaySeconds: env.SCRAPE_DELAY_SECONDS, scrapeDelaySeconds: env.SCRAPE_DELAY_SECONDS,
timeWindowSeconds: env.TIME_WINDOW_SECONDS, timeWindowSeconds: env.TIME_WINDOW_SECONDS,
@@ -2468,6 +2457,6 @@ export function getCloudflareMetricsClient(env: Env): CloudflareMetricsClient {
fetch: rateLimitedFetch, fetch: rateLimitedFetch,
}); });
cachedEnvHash = currentHash; clientCache.set(env.CF_API_RATE_LIMITER, client);
return cachedClient; return client;
} }