-
Notifications
You must be signed in to change notification settings - Fork 280
perf(webview): stop task history globalState writes #1664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f1a0dd5
40450b1
0b1702b
e99bc5c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -242,8 +242,6 @@ | |
| private recentTasksCache?: string[] | ||
| public readonly taskHistoryStore: TaskHistoryStore | ||
| private taskHistoryStoreInitialized = false | ||
| private globalStateWriteThroughTimer: ReturnType<typeof setTimeout> | null = null | ||
| private static readonly GLOBAL_STATE_WRITE_THROUGH_DEBOUNCE_MS = 5000 // 5 seconds | ||
| public static readonly PENDING_OPERATION_TIMEOUT_MS = 30000 // 30 seconds | ||
| private providerProfileMutationQueue = Promise.resolve() | ||
| private historyTaskCreationQueue = Promise.resolve() | ||
|
|
@@ -343,14 +341,8 @@ | |
| this.mdmService = mdmService | ||
| void this.updateGlobalState("codebaseIndexModels", EMBEDDING_MODEL_PROFILES) | ||
|
|
||
| // Initialize the per-task file-based history store. | ||
| // The globalState write-through is debounced separately (not on every mutation) | ||
| // since per-task files are authoritative and globalState is only for downgrade compat. | ||
| this.taskHistoryStore = new TaskHistoryStore(this.contextProxy.globalStorageUri.fsPath, { | ||
| onWrite: async () => { | ||
| this.scheduleGlobalStateWriteThrough() | ||
| }, | ||
| }) | ||
| // Initialize the authoritative per-task file-based history store. | ||
| this.taskHistoryStore = new TaskHistoryStore(this.contextProxy.globalStorageUri.fsPath) | ||
| this.initializeTaskHistoryStore().catch((error) => { | ||
| this.log(`Failed to initialize TaskHistoryStore: ${error}`) | ||
| }) | ||
|
|
@@ -896,7 +888,6 @@ | |
| await this.marketplaceManager?.cleanup() | ||
| this.customModesManager?.dispose() | ||
| this.taskHistoryStore.dispose() | ||
| this.flushGlobalStateWriteThrough() | ||
| this.log("Disposed all disposables") | ||
| ClineProvider.activeInstances.delete(this) | ||
|
|
||
|
|
@@ -1742,9 +1733,7 @@ | |
|
|
||
| try { | ||
| // Update the task history with the new mode first. | ||
| const taskHistoryItem = | ||
| this.taskHistoryStore.get(task.taskId) ?? | ||
| (this.getGlobalState("taskHistory") ?? []).find((item) => item.id === task.taskId) | ||
| const taskHistoryItem = this.getTaskHistoryItem(task.taskId) | ||
|
|
||
| if (taskHistoryItem) { | ||
| await this.updateTaskHistory({ ...taskHistoryItem, mode: newMode }) | ||
|
|
@@ -1982,9 +1971,7 @@ | |
| // been persisted into taskHistory (it will be captured on the next save). | ||
| task.setTaskApiConfigName(apiConfigName) | ||
|
|
||
| const taskHistoryItem = | ||
| this.taskHistoryStore.get(task.taskId) ?? | ||
| (this.getGlobalState("taskHistory") ?? []).find((item) => item.id === task.taskId) | ||
| const taskHistoryItem = this.getTaskHistoryItem(task.taskId) | ||
|
|
||
| if (taskHistoryItem) { | ||
| await this.updateTaskHistory({ ...taskHistoryItem, apiConfigName }) | ||
|
|
@@ -2240,15 +2227,26 @@ | |
|
|
||
| // Task history | ||
|
|
||
| private getTaskHistoryItem(id: string): HistoryItem | undefined { | ||
| const historyItem = this.taskHistoryStore.get(id) | ||
|
|
||
| // Once initialization and migration succeed, the file-backed store is authoritative. | ||
| // Legacy global state is only a fallback while startup is incomplete or has failed. | ||
| if (historyItem || this.taskHistoryStoreInitialized) { | ||
| return historyItem | ||
| } | ||
|
|
||
| return (this.getGlobalState("taskHistory") ?? []).find((item) => item.id === id) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: sed -n '2215,2260p' src/core/webview/ClineProvider.ts
sed -n '90,155p' src/core/task-persistence/TaskHistoryStore.ts
rg -n -C 6 'getTaskHistoryItem|getTaskWithId|taskHistoryStoreInitialized|taskHistory.*globalState|Task not found|legacy' src/core/webview/__tests__/ClineProvider.spec.ts src/core/webview/__tests__/ClineProvider.taskHistory.spec.ts
find .. -name AGENTS.md -o -name .cursorrules -o -name CONTRIBUTING.md | head -30Repository: Zoo-Code-Org/Zoo-Code Length of output: 32022 🏁 Script executed: printf '%s\n' '--- focused test setup and cases ---'
sed -n '1,180p' src/core/webview/__tests__/ClineProvider.spec.ts
sed -n '4940,5055p' src/core/webview/__tests__/ClineProvider.spec.ts
printf '%s\n' '--- initialization flag and legacy-state references ---'
rg -n -C 5 'taskHistoryStoreInitialized|taskHistoryStore\s*=|getGlobalState\("taskHistory"\)|globalState\.get' src/core/webview/ClineProvider.ts src/core/webview/__tests__/ClineProvider.spec.ts
printf '%s\n' '--- repository instruction files in the checkout ---'
git ls-files '*AGENTS.md' '*CONTRIBUTING.md' '*cursorrules*' | head -80Repository: Zoo-Code-Org/Zoo-Code Length of output: 25058 🤖 get_repo_knowledge executed:
Length of output: 11446 🏁 Script executed: sed -n '1,180p' src/core/webview/__tests__/ClineProvider.spec.ts
sed -n '4940,5055p' src/core/webview/__tests__/ClineProvider.spec.ts
rg -n -C 5 'taskHistoryStoreInitialized|taskHistoryStore\s*=|getGlobalState\("taskHistory"\)|globalState\.get' src/core/webview/ClineProvider.ts src/core/webview/__tests__/ClineProvider.spec.ts
git ls-files '*AGENTS.md' '*CONTRIBUTING.md' '*cursorrules*' | head -80Repository: Zoo-Code-Org/Zoo-Code Length of output: 24912 Cover the cold-start legacy-history fallback.
🤖 Prompt for AI Agents |
||
| } | ||
|
Check warning on line 2240 in src/core/webview/ClineProvider.ts
|
||
|
|
||
| async getTaskWithId(id: string): Promise<{ | ||
| historyItem: HistoryItem | ||
| taskDirPath: string | ||
| apiConversationHistoryFilePath: string | ||
| uiMessagesFilePath: string | ||
| apiConversationHistory: Anthropic.MessageParam[] | ||
| }> { | ||
| const historyItem = | ||
| this.taskHistoryStore.get(id) ?? (this.getGlobalState("taskHistory") ?? []).find((item) => item.id === id) | ||
| const historyItem = this.getTaskHistoryItem(id) | ||
|
|
||
| if (!historyItem) { | ||
| throw new Error("Task not found") | ||
|
|
@@ -3133,44 +3131,6 @@ | |
| return history | ||
| } | ||
|
|
||
| /** | ||
| * Schedule a debounced write-through of task history to globalState. | ||
| * Only used for backward compatibility during the transition period. | ||
| * Per-task files are authoritative; globalState is the downgrade fallback. | ||
| */ | ||
| private scheduleGlobalStateWriteThrough(): void { | ||
| if (this.globalStateWriteThroughTimer) { | ||
| clearTimeout(this.globalStateWriteThroughTimer) | ||
| } | ||
|
|
||
| this.globalStateWriteThroughTimer = setTimeout(async () => { | ||
| this.globalStateWriteThroughTimer = null | ||
| try { | ||
| const items = this.taskHistoryStore.getAll() | ||
| await this.updateGlobalState("taskHistory", items) | ||
| } catch (err) { | ||
| this.log( | ||
| `[scheduleGlobalStateWriteThrough] Failed: ${err instanceof Error ? err.message : String(err)}`, | ||
| ) | ||
| } | ||
| }, ClineProvider.GLOBAL_STATE_WRITE_THROUGH_DEBOUNCE_MS) | ||
| } | ||
|
|
||
| /** | ||
| * Flush any pending debounced globalState write-through immediately. | ||
| */ | ||
| private flushGlobalStateWriteThrough(): void { | ||
| if (this.globalStateWriteThroughTimer) { | ||
| clearTimeout(this.globalStateWriteThroughTimer) | ||
| this.globalStateWriteThroughTimer = null | ||
| } | ||
|
|
||
| const items = this.taskHistoryStore.getAll() | ||
| this.updateGlobalState("taskHistory", items).catch((err) => { | ||
| this.log(`[flushGlobalStateWriteThrough] Failed: ${err instanceof Error ? err.message : String(err)}`) | ||
| }) | ||
| } | ||
|
|
||
| /** | ||
| * Broadcasts a task history update to the webview. | ||
| * This sends a lightweight message with just the task history, rather than the full state. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.