Skip to content

Watcher can repeatedly re-index dirty repos and cause large disk write amplification #937

Closed

Description

@wymfly

Summary

We observed background watcher processes causing very large sustained disk writes during normal MCP usage. On a macOS workstation using Codex with several indexed repos, host write counters exceeded 1 TB/day.

The issue appears to be write amplification from the watcher/indexing path, not application logs.

What we observed

  • Multiple processes were active for different repos.
  • Some processes wrote hundreds of MB within a short sampling window while idle from the user's perspective.
  • Codex SQLite TRACE/DEBUG/INFO logging was ruled out: log inserts were blocked and the log WAL stopped growing.
  • Re-indexing repeated even without commits, because the worktree stayed dirty/untracked.

Suspected cause

The watcher treats a dirty/untracked worktree as changed on every poll. If the dirty state persists, each poll can trigger another re-index.

The incremental indexing path reduces some parsing work, but persistence still appears to rewrite/replace the project SQLite DB and may also rewrite the compressed artifact. For larger repos this can turn each watcher poll into hundreds of MB of writes.

This is amplified when multiple MCP clients/sessions run in the same repo or overlapping repos, since each process can independently trigger indexing and there does not appear to be a cross-process index lock/deduplication guard.

Expected behavior

The watcher should not repeatedly re-index the same unchanged dirty state.

Possible mitigations:

  • Trigger background watcher re-index only on Git HEAD changes, or
  • Track a dirty-state signature and re-index only when that signature changes, and
  • Add a cross-process lock/deduplication guard so multiple MCP processes cannot rebuild the same repo index concurrently.

Longer term, incremental indexing should ideally avoid near-full DB/artifact rewrites when only a small set of files changed.

Related issues

This seems related to several existing reports, but combines them into one disk-write-amplification failure mode:

Activity

  1. added
    bugSomething isn't working
    stability/performanceServer crashes, OOM, hangs, high CPU/memory
    priority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.
    on Jul 7, 2026
  2. DeusData commented on Jul 7, 2026

    @DeusData
    Owner

    Thanks for the write-amplification report. Triage: high-priority watcher/performance bug under #593.

    Persistent dirty or untracked state should not cause repeated full re-index/write cycles while the user is idle. A fix should reproduce a dirty-repo polling loop and prove the watcher coalesces or suppresses repeat work without missing real file changes.

  3. Artemonim commented on Jul 8, 2026

    @Artemonim

    I disabled the auto-update of the MCP itself, since on the contrary it works too rarely for me while the agent is running, and I built the index rebuild into my local CI, which the agents themselves call when they finish part of the work.

  4. added this to the 0.9.1-rc milestone on Jul 8, 2026
  5. DeusData commented on Jul 12, 2026

    @DeusData
    Owner

    Fixed on main by 9789a7c (merged in 94be485, PR #1045), shipping in v0.9.1.

    What changed, mapped to your asks:

    1. Dirty-state signature (your mitigation 2): the watcher now hashes git status --porcelain -uall -z entries plus each listed file's (size, mtime_ns). A persistently dirty/untracked tree reindexes once per distinct state — idle polls over the same dirty tree no longer trigger anything, killing the repeated full reindex → DB/artifact rewrite cycle. Editing a dirty file again, adding/removing files, or reverting to clean each trigger exactly one reindex, so no real change is missed.
    2. Lost-update fix (adjacent flaw found while reproducing): the watcher used to record the new HEAD at check time, so a commit observed while the pipeline lock was busy was marked seen and never indexed. Baselines now commit only after a successful reindex; busy-skips and failures retry on the next poll.
    3. Cross-process guard (your mitigation 3): partially pre-existing — the in-process pipeline lock already serializes; a cross-process dedup lock across independent MCP server processes remains open and is best tracked separately if you still see multi-process rebuild storms on v0.9.1.

    The near-full DB/artifact rewrite per incremental run (your "longer term" point) is tracked in #867, where a contributor fix stack (#868–#870) is in review.

    Thanks for the write-counter evidence — it made the amplification loop unambiguous.

  6. added a commit that references this issue on Jul 12, 2026
    9789a7c
  7. added a commit that references this issue on Jul 15, 2026
    f48e41a
  8. added a commit that references this issue on Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.stability/performanceServer crashes, OOM, hangs, high CPU/memory

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions