Skip to content

fix(workspace): route Postgres-backed watch/update through the shared DB instead of local wiki.db - #2160

Open
Aman-goel-04 wants to merge 1 commit into
repowise-dev:mainfrom
Aman-goel-04:fix/2148-workspace-postgres-incremental
Open

fix(workspace): route Postgres-backed watch/update through the shared DB instead of local wiki.db#2160
Aman-goel-04 wants to merge 1 commit into
repowise-dev:mainfrom
Aman-goel-04:fix/2148-workspace-postgres-incremental

Conversation

@Aman-goel-04

Copy link
Copy Markdown
Contributor

Summary

  • Fixes the incremental-update gate, the config-drift branch, reconcile_repo_head_commit, and _merged_repo_excludes in update.py. All four assumed a repo-local .repowise/wiki.db as the "already indexed" signal, which never exists under a shared Postgres database, so repowise watch --workspace full-reindexed on every single file change instead of updating incrementally.
  • Fixes reconcile_repo_head_commit's early return specifically: on Postgres, repositories.head_commit/updated_at were never stamped after an update, which left the UI's "index behind checkout" warning and freshness timestamp permanently stuck even after a successful sync.
  • Gives create_engine() a short_lived parameter (default True) so PostgreSQL uses NullPool for every short-lived engine (one connection per checkout, closed on dispose) instead of SQLAlchemy's default AsyncAdaptedQueuePool (up to 15 idle server slots per engine, and the failure class behind [Bug] Async PostgreSQL cleanup races with event-loop shutdown #2062, a pooled connection surviving past its creating event loop and getting handed to a later, unrelated caller). The two long-lived engines (FastAPI app, MCP server) opt out with short_lived=False to keep pooled reuse across requests.

Related Issues

Fixes #2148

Test Plan

  • Tests pass (pytest), full suite (tests/unit/, tests/providers/): 17528 passed, 4 pre-existing failures unrelated to this change (confirmed identical against a clean main via git stash: an OS-path baseline test, a Python-version-dependent regex test, and a test hardcoding a master branch name). tests/unit/workspace/test_incremental_update.py + tests/unit/server/mcp/test_config.py: 19/19 passed.
  • Lint passes (ruff check .)
  • Web build passes (npm run build) (if frontend changes) — not applicable, Python-only change

Manual reproduction (before fix): two disposable repos against a Docker postgres:16 instance via REPOWISE_DB_URL. Confirmed Postgres held a repositories row for test-repo with a matching state.json.last_sync_commit, and no local wiki.db. Running repowise watch --workspace -v and editing a tracked file reliably produced tier=full on every single-line change across dozens of edits.

Regression tests added:

  • test_shared_db_indexed_repo_takes_incremental_path: a repo indexed in a configured shared DB (simulated with a file-backed sqlite+aiosqlite URL, no live Postgres required) takes the incremental path even with no local wiki.db. Fails on main with full pipeline must not run for indexed repos.
  • test_shared_db_config_drift_does_not_crash: combines config_changed=True with the shared-DB/no-local-wiki.db case. Regression test for an ordering bug caught during self-review: an earlier version of this fix computed has_persisted_index after the config-drift branch already read it, which would raise UnboundLocalError the moment a shared-DB repo's config actually drifted. The existing test_config_drift_runs_full_reindex only covers the local-SQLite case and never would have caught it.
  • test_mcp_lifespan_uses_cli_database_env_var updated to assert the MCP server's engine is created with short_lived=False, locking in the long-lived opt-out so a future cleanup pass can't silently drop it.

On the connection-leak half of #2148: I was not able to reproduce an accumulating leak (a rising floor between update cycles) from watch alone, across ~65 cycles spanning sequential single-repo, sequential dual-repo, and forced-concurrent dual-repo edits against a live Postgres instance with serve + mcp also running. Every observed connection spike drained back to baseline. This is consistent with full_index.py's existing try/finally: await engine.dispose() already disposing correctly on both the success and exception paths. The short_lived/NullPool change in this PR closes the mechanism regardless (an idle pooled connection can never be picked up by a later, unrelated caller after this change, since there's no pool to hold it), rather than relying on a leak having been empirically observed.

Checklist

  • My code follows the project's code style
  • I have added tests for new functionality
  • All existing tests still pass
  • I have updated documentation if needed. Docstrings on create_engine, reconcile_repo_head_commit, and _merged_repo_excludes updated to describe the shared-DB behavior

@repowise-bot

repowise-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown

🔍 2 things to check

  1. create_engine in .../persistence/database.py signature changed. Review 124 callers outside this PR: .../cli/_repo_session.py::open_repo_db, .../augment_cmd/search.py::_pagerank_file_order, .../augment_cmd/search.py::_search_enrich (+121 more)
  2. Run tests/integration/test_mcp.py, tests/integration/test_persistence.py, .../persistence/test_embed_batch_pg.py (+7 more) first: they import the changed files

✅ Health of changed files: 3.3 → 3.5 (+0.1)
⚠️ Change risk: moderate, riskier than 52% of this repo's commits.

✅ Health gate: passed

👀 Suggested reviewers @RaghavChamadiya


📊 See the full report for this PR
Your repo map with this PR's blast radius lit up, every caller of the contracts it changes, and health before and after. No sign-in. · ⭐ Star Repowise · 📥 Install bot · Silence on a single PR with [skip repowise] in the title · Per-repo toggle on repowise.dev/settings?tab=bot · Updated 2026-09-08 06:50 UTC

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.

[Bug] Workspace watch under PostgreSQL full-reindexes every change and leaks Postgres connections

1 participant