Skip to content

Commit 012e707

Browse files
Pigbibiclaude
andcommitted
fix: use ctx.waitUntil for KV cache write, add in-memory cache layer
- Add ctx parameter to fetch handler and configPayload - Use ctx.waitUntil() so KV cache write survives past response - Add in-memory cache (_memCurrentStrategies) as first-line cache - Three-tier cache: memory → KV → GitHub Previous fire-and-forget write was cancelled when Worker returned response, so KV cache was never populated. This fixes it. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 2bcc466 commit 012e707

1 file changed

Lines changed: 28 additions & 9 deletions

File tree

web/strategy-switch-console/worker.js

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -127,15 +127,15 @@ const SECURITY_HEADERS = {
127127
};
128128

129129
export default {
130-
async fetch(request, env) {
130+
async fetch(request, env, ctx) {
131131
const url = new URL(request.url);
132132
try {
133133
if (url.pathname === "/login") return await startLogin(request, env);
134134
if (url.pathname === "/callback") return await finishLogin(request, env);
135135
if (url.pathname === "/admin") return await adminPage(request, env);
136136
if (url.pathname === "/api/session") return json(await sessionPayload(request, env));
137137
if (url.pathname === "/api/strategy-profiles") return json(await strategyProfilesPayload(env));
138-
if (url.pathname === "/api/config") return json(await configPayload(request, env));
138+
if (url.pathname === "/api/config") return json(await configPayload(request, env, ctx));
139139
if (url.pathname === "/api/admin/config" && request.method === "GET") {
140140
return await adminConfigResponse(request, env);
141141
}
@@ -575,30 +575,49 @@ async function loadPlatformMeta() {
575575
return merged;
576576
}
577577

578-
async function configPayload(request, env) {
578+
// In-memory cache for the lifetime of this Worker isolate.
579+
// Falls back to KV on cold starts; KV write uses ctx.waitUntil
580+
// so it survives past the response.
581+
let _memCurrentStrategies = null;
582+
let _memCurrentStrategiesTs = 0;
583+
584+
async function configPayload(request, env, ctx) {
579585
const session = await readSession(request, env);
580586
const meta = await loadPlatformMeta();
581587
if (!session?.allowed) return { accountOptions: null, platformMeta: meta };
582588
const accountConfig = await loadAccountOptionsConfig(env);
583589
const strategyProfiles = await loadStrategyProfilesConfig(env);
584590

585591
let currentStrategies = null;
586-
if (hasConfigStore(env)) {
592+
593+
// 1) In-memory cache (fastest — same Worker isolate)
594+
if (_memCurrentStrategies && (Date.now() - _memCurrentStrategiesTs) < CURRENT_STRATEGIES_CACHE_TTL_MS) {
595+
currentStrategies = _memCurrentStrategies;
596+
}
597+
598+
// 2) KV cache (survives cold starts)
599+
if (!currentStrategies && hasConfigStore(env)) {
587600
const cached = await readConfigJson(env, CURRENT_STRATEGIES_CACHE_KEY);
588601
if (cached?.ts && (Date.now() - cached.ts) < CURRENT_STRATEGIES_CACHE_TTL_MS && cached.data) {
589602
currentStrategies = cached.data;
603+
_memCurrentStrategies = cached.data;
604+
_memCurrentStrategiesTs = cached.ts;
590605
}
591606
}
607+
608+
// 3) Cache miss — fetch from GitHub
592609
if (!currentStrategies) {
593610
currentStrategies = await loadCurrentStrategiesSafely(accountConfig.options, env);
594-
if (hasConfigStore(env)) {
595-
// fire-and-forget: don't block the response on cache write
596-
writeConfigJson(env, CURRENT_STRATEGIES_CACHE_KEY, {
597-
ts: Date.now(),
611+
_memCurrentStrategies = currentStrategies;
612+
_memCurrentStrategiesTs = Date.now();
613+
if (hasConfigStore(env) && ctx) {
614+
ctx.waitUntil(writeConfigJson(env, CURRENT_STRATEGIES_CACHE_KEY, {
615+
ts: _memCurrentStrategiesTs,
598616
data: currentStrategies,
599-
}).catch(() => {});
617+
}));
600618
}
601619
}
620+
602621
return {
603622
accountOptions: accountConfig.options,
604623
platformRepositories: platformRepositories(env),

0 commit comments

Comments
 (0)