Skip to content

feat: Phase 2 singleton state management (Issue #598)#636

Draft
jlin53882 wants to merge 1 commit intoCortexReach:masterfrom
jlin53882:feat/phase2-singleton-state
Draft

feat: Phase 2 singleton state management (Issue #598)#636
jlin53882 wants to merge 1 commit intoCortexReach:masterfrom
jlin53882:feat/phase2-singleton-state

Conversation

@jlin53882
Copy link
Copy Markdown
Contributor

Phase 2: Singleton State Management — Continuation of PR #430

Issue

Issue #598

Problem

memory-lancedb-pro calls register() multiple times per process lifecycle:

  • 5× at startup (one per scope init)
  • 4× per inbound message that triggers a scope cache-miss

Without guarding, all heavy plugin resources and session Maps are recreated on each register() call, causing:

  1. Memory heap growth — accumulated Maps never released (~9× resource waste)
  2. Duplicate handlers accumulating unboundedly (200+ observed)
  3. Session Maps being lost when clearInternalHooks() resets the plugin between registrations

Solution: Phase 2 Singleton State

This PR implements Phase 2 of the multi-phase fix for Issue #430 / Issue #598.

Building on Phase 1 (hook event deduplication via _hookEventDedup Set, merged as PR #617), Phase 2 addresses resource deduplication:

  1. PluginSingletonState interface — defines all 18 properties returned by the factory (8 heavy resources + 8 session Maps)
  2. _singletonState module-level variablenull until first register() call, then persists for process lifetime
  3. _initPluginState(api) factory function — creates all resources and Maps exactly once
  4. if (!_singletonState) guard in register() — subsequent calls reuse the singleton via destructuring
interface PluginSingletonState {
  config, resolvedDbPath, store, embedder, decayEngine, tierManager,
  retriever, scopeManager, migrator, smartExtractor, extractionRateLimiter,
  // Session Maps — persist across scope refreshes
  reflectionErrorStateBySession, reflectionDerivedBySession, reflectionByAgentCache,
  recallHistory, turnCounter, autoCaptureSeenTextCount,
  autoCapturePendingIngressTexts, autoCaptureRecentTexts
}

let _singletonState: PluginSingletonState | null = null;

function _initPluginState(api: OpenClawPluginApi): PluginSingletonState {
  // ... create all resources and Maps exactly once
}

register(api: OpenClawPluginApi) {
  if (!_singletonState) { _singletonState = _initPluginState(api); }
  const { config, store, embedder, /* ... 16 more */ } = _singletonState;
  // register handlers using destructured resources
}

What Phase 3 Is NOT

Phase 3 (backup timer optimization) is NOT included in this PR.

Files Changed

  • index.ts — singleton infrastructure (PluginSingletonState, _singletonState, _initPluginState, register() refactor)

Testing

  • ✅ CLI smoke test passed (npm run test:cli-smoke)
  • ✅ Functional e2e test passed
  • ✅ All Maps appear exactly once in source (no duplicates)
  • ✅ Braces balanced (1013/1013)
  • ⚠️ test:core-regression has a pre-existing migrator is not defined error in upstream (16ee3e4) — not caused by this PR

Commits in This PR Branch

a147335 feat: Phase 2 singleton state management (Issue #598)
         (on top of upstream master 16ee3e4)

- 新增 PluginSingletonState interface:定義所有 heavy plugin 資源的型別
- 新增 _singletonState 模組層級變數:null 直到第一次 register() 呼叫
- 新增 _initPluginState(api) factory function:建立所有資源(store、embedder、
  tierManager、retriever、scopeManager、migrator、smartExtractor、
  extractionRateLimiter)及 8 個 Session Maps
- 替換 register() 內的 init block:改為 if (!_singletonState) guard,
  後續呼叫直接 destructuring 從 singleton 取用資源
- 移除 register() 內重複的 Map 宣告(改由 singleton 擁有)
- Session Maps 現在在第一次 register() 時建立並 persist,後續 scope
  refresh 不會重複建立導致 memory heap growth
- Phase 1 dedup 機制(_hookEventDedup / _dedupHookEvent)保持不變
- Phase 3 backup timer optimization 不包含在本次實作

PR CortexReach#430 Phase 2 / Issue CortexReach#598
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant