Skip to content

brain/index: refuse --rebuild while a live brain holds the db - #67

Merged
eSlider merged 1 commit into
mainfrom
feat/index-live-guard
Aug 21, 2026
Merged

brain/index: refuse --rebuild while a live brain holds the db#67
eSlider merged 1 commit into
mainfrom
feat/index-live-guard

Conversation

@eSlider

@eSlider eSlider commented Aug 21, 2026

Copy link
Copy Markdown
Owner

Problem

index --rebuild deletes var/kb.lbug even while a live brain-serve holds it open. With the compose brain bind-mounting ~/2dph/var at /data/var, a host-side rebuild replaces the file under the running service:

  • serve keeps reading the removed inode → stale reads, silent divergence
  • the fresh db lands owned by the writer's uid → service (uid 1001) can no longer open it writable (OpenDatabase: failed to open database with status 1 on every ingest)

Recovery required fishing the old db out of /proc/<pid>/fd/4 and chown-ing the restored file.

Fix

Refuse --rebuild when the db is live. Two probes before removal:

  1. internal/brain.LiveHolders(path) — scans /proc/<pid>/fd, compares device+inode of each fd target against the db. Crosses bind mounts and container PID namespaces for same-uid processes; also flags deleted-inode holders (stale server). Cgo-free, unit-tested.
  2. API probe (repo-default db only) — brain API answering /stats on 127.0.0.1:$KB_PORT; cross-uid container fds are unreadable from host /proc.

Scoped via git-repo root (independent of KB_ROOT) so throwaway/test roots stay unblocked; CI sees neither probe fire.

Escapes: --force per-invocation; KB_INDEX_ALLOW_LIVE=1 env opt-out — set in the compose index service since its swap+restart flow is sanctioned. Runbook warning added.

Test plan

  • unit: LiveHolders (open fd / deleted-inode / clean), BrainAPIAlive httptest matrix
  • functional: synthetic fd holder → refused exit 2 with pid+cmdline; reasons aggregate
  • real compose brain up → refused exit 2 via API probe; file untouched (inode + stats verified)
  • ALLOW_LIVE proceeds; temp KB_ROOT unaffected; --force proceeds
  • gofmt/vet, plain + zig-cgo suites green

Deleting kb.lbug under a running brain-serve leaves it serving the
removed inode and can lock the service out of the fresh db (observed
with the compose container bind-mounting var/ as another uid).

Two probes before removal, both overridable with --force:
- /proc fd scan by device+inode: catches same-namespace holders and
  deleted-inode staleness (internal/brain.LiveHolders)
- for the repo-default db only: an API answering on 127.0.0.1:$KB_PORT,
  since cross-uid container fds are invisible to host /proc

KB_INDEX_ALLOW_LIVE=1 opts out for sanctioned swap+restart flows; the
compose index service sets it. Runbook updated.
@eSlider
eSlider merged commit 23d9901 into main Aug 21, 2026
4 checks passed
eSlider added a commit that referenced this pull request Aug 21, 2026
fix(onlyoffice/import-contact): library client, idempotent E2E (#67)
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