@@ -13,7 +13,8 @@ const AUDIT_LOG_KEY = "audit_log";
1313const AUDIT_LOG_LIMIT = 50 ;
1414const CURRENT_STRATEGIES_TIMEOUT_MS = 25000 ;
1515const CURRENT_STRATEGIES_CACHE_KEY = "current_strategies_cache" ;
16- const CURRENT_STRATEGIES_CACHE_TTL_MS = 60_000 ;
16+ const CURRENT_STRATEGIES_CACHE_TTL_MS = 120_000 ; // 2 min — return cached data without refresh
17+ const CURRENT_STRATEGIES_STALE_TTL_MS = 600_000 ; // 10 min — return stale + background refresh
1718const GITHUB_API_TIMEOUT_MS = 8000 ;
1819
1920const SUPPORTED_PLATFORMS = [ "longbridge" , "ibkr" , "schwab" , "firstrade" , "qmt" , "binance" ] ;
@@ -576,10 +577,9 @@ async function loadPlatformMeta() {
576577}
577578
578579// 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.
581580let _memCurrentStrategies = null ;
582581let _memCurrentStrategiesTs = 0 ;
582+ let _memRefreshing = false ; // prevent concurrent background refreshes
583583
584584async function configPayload ( request , env , ctx ) {
585585 const session = await readSession ( request , env ) ;
@@ -589,23 +589,57 @@ async function configPayload(request, env, ctx) {
589589 const strategyProfiles = await loadStrategyProfilesConfig ( env ) ;
590590
591591 let currentStrategies = null ;
592-
593- // 1) In-memory cache (fastest — same Worker isolate)
594- if ( _memCurrentStrategies && ( Date . now ( ) - _memCurrentStrategiesTs ) < CURRENT_STRATEGIES_CACHE_TTL_MS ) {
595- currentStrategies = _memCurrentStrategies ;
592+ let cacheFresh = false ;
593+
594+ // 1) In-memory cache
595+ if ( _memCurrentStrategies ) {
596+ const age = Date . now ( ) - _memCurrentStrategiesTs ;
597+ if ( age < CURRENT_STRATEGIES_CACHE_TTL_MS ) {
598+ currentStrategies = _memCurrentStrategies ;
599+ cacheFresh = true ;
600+ } else if ( age < CURRENT_STRATEGIES_STALE_TTL_MS ) {
601+ currentStrategies = _memCurrentStrategies ;
602+ // stale — trigger background refresh below
603+ }
596604 }
597605
598- // 2) KV cache (survives cold starts)
606+ // 2) KV cache
599607 if ( ! currentStrategies && hasConfigStore ( env ) ) {
600608 const cached = await readConfigJson ( env , CURRENT_STRATEGIES_CACHE_KEY ) ;
601- if ( cached ?. ts && ( Date . now ( ) - cached . ts ) < CURRENT_STRATEGIES_CACHE_TTL_MS && cached . data ) {
602- currentStrategies = cached . data ;
603- _memCurrentStrategies = cached . data ;
604- _memCurrentStrategiesTs = cached . ts ;
609+ if ( cached ?. ts && cached . data ) {
610+ const age = Date . now ( ) - cached . ts ;
611+ if ( age < CURRENT_STRATEGIES_CACHE_TTL_MS ) {
612+ currentStrategies = cached . data ;
613+ cacheFresh = true ;
614+ } else if ( age < CURRENT_STRATEGIES_STALE_TTL_MS ) {
615+ currentStrategies = cached . data ;
616+ // stale — trigger background refresh below
617+ }
618+ if ( currentStrategies && ! _memCurrentStrategies ) {
619+ _memCurrentStrategies = currentStrategies ;
620+ _memCurrentStrategiesTs = cached . ts ;
621+ }
605622 }
606623 }
607624
608- // 3) Cache miss — fetch from GitHub
625+ // 3) Background refresh when stale (return old data immediately)
626+ if ( currentStrategies && ! cacheFresh && ! _memRefreshing && hasConfigStore ( env ) && ctx ) {
627+ _memRefreshing = true ;
628+ ctx . waitUntil ( ( async ( ) => {
629+ try {
630+ const fresh = await loadCurrentStrategiesSafely ( accountConfig . options , env ) ;
631+ _memCurrentStrategies = fresh ;
632+ _memCurrentStrategiesTs = Date . now ( ) ;
633+ await writeConfigJson ( env , CURRENT_STRATEGIES_CACHE_KEY , {
634+ ts : _memCurrentStrategiesTs ,
635+ data : fresh ,
636+ } ) ;
637+ } catch { /* keep stale data */ }
638+ finally { _memRefreshing = false ; }
639+ } ) ( ) ) ;
640+ }
641+
642+ // 4) Complete miss — must wait for GitHub
609643 if ( ! currentStrategies ) {
610644 currentStrategies = await loadCurrentStrategiesSafely ( accountConfig . options , env ) ;
611645 _memCurrentStrategies = currentStrategies ;
0 commit comments