Skip to content

bug: direct optimize can leave a pending Mutation recovery barrier that a long-lived server never clears #554

Closed

Description

@blarghmatey

Summary

A scheduled omnigraph optimize --cluster … --graph … (direct storage, S3) run against a graph that a long-running omnigraph-server has open can leave a pending Mutation recovery operation on main that the server cannot clear. Every subsequent write through that server fails, for as long as the process stays up. Restarting the server clears it.

We hit this four times in eleven days on a production deployment; the last one froze cross-repo data for ~15 hours.

Error

recovery required for operation 01M0VH9C6D8A4XQCEK5YJKSMPX: pending Mutation
recovery operation on branch 'main' blocks the synchronous write/control
recovery barrier (datasets dataset for node type 'RepoSymbol'); reopen the
graph read-write before retrying

Clients retry and give up — ours budgets ~6s of backoff, deliberately sized to ride out a barrier raised by a concurrent writer and to report a genuinely stuck branch rather than sleep through it. This is the second case: the same operation id comes back on every retry, from multiple independent client processes, for hours.

Why we believe optimize is the trigger

The operation ids are ULIDs, so they date themselves. All four wedges decode to within five seconds of the same wall-clock time, on four different days:

01M01SA4K02SX45CBB16CE60PQ   2026-08-15 04:01:36 UTC
01M0H7PDHBAB7YHQ65E34MH4BJ   2026-08-21 04:01:35 UTC
01M0RYWR2BHBNKPT1QEQPFC1TE   2026-08-24 04:01:40 UTC
01M0VH9C6D8A4XQCEK5YJKSMPX   2026-08-25 04:01:37 UTC

Our scheduled optimize runs daily at 03:20 UTC. Our indexer runs every 4h on the hour, so 04:00 is the first writer to arrive after compaction, and its first write lands at ~04:01.

The dataset named in the barrier is one optimize had just rewritten:

omnigraph optimize → s3://…/graphs/code-bridge.omni (direct, remote)
  node type 'InterfaceBinding'   frags 11 → 1 ✓
  node type 'RepoSymbol'         frags 14 → 1 ✓

repair reports nothing

Preview against the same graph while wedged — no drift anywhere, including on the dataset the barrier names:

repair s3://…/graphs/code-bridge.omni — preview mode, 3 datasets
  node type 'InterfaceBinding'   no_op   no_drift    842
  node type 'PackageMap'         no_op   no_drift   1037
  node type 'RepoSymbol'         no_op   no_drift   2023

So this is not manifest/Lance-HEAD drift, and repair is not the remedy.

What clears it

kubectl rollout restart on the server. Immediately afterwards the same client, same config, same command wrote successfully (symbols=5303 edges=16883 bindings=108). That matches the error's own hint — "reopen the graph read-write" — if the recovery runs at read-write open and the server never reopens while it is up.

What we could not establish

optimize compacts this graph every day, and the wedge appeared on four days out of fourteen, so compaction is necessary and not sufficient. Two candidate discriminators checked and rejected:

  • optimize compacted RepoSymbol 14 → 1 on clean days too (verified on a clean day: byte-identical output);
  • server uptime spanning the optimize run was the same on clean days (one pod spanned both windows on two consecutive clean days).

Something about the specific write or the compaction result decides it, and we do not know what.

Questions

  1. Is direct-storage optimize concurrent with a serving omnigraph-server on the same graph a supported configuration? Our deployment docs assumed it was, on the basis that the publisher CAS lets optimize rebase-and-retry against concurrent writers — which appears to be about write conflicts and not about the recovery barrier.
  2. Should the server detect that a store was rewritten underneath it and reopen, rather than requiring a restart?
  3. Is a pending Mutation recovery operation expected to be clearable without reopening the graph? If so, what should clear it — and if not, is there a supported way to signal a serving process to reopen?

Environment

  • Storage: S3, cluster-managed, storage format 6
  • Client CLI where the errors were reproduced by hand: omnigraph 0.10.0, internal-schema 6
  • Server: 0.10.0-era build from the moving edge tag. I have not pinned the exact server commit; happy to if it would help.
  • Graph: ~2k RepoSymbol rows, ~840 InterfaceBinding, ~1k PackageMap

Happy to supply fuller logs, the ULID decoding, or to test a patch against this deployment.

Activity

  1. added
    bugSomething isn't working
    needs-triageAwaiting maintainer triage
    acceptedTriaged and validated; open for a PR
    P-highHigh priority
    and removed
    needs-triageAwaiting maintainer triage
    on Aug 25, 2026
  2. self-assigned this
    on Aug 28, 2026
  3. ragnorc commented on Sep 9, 2026

    @ragnorc
    Contributor

    The same failure mode recurs on 0.10.0 via a different operation — filed as #694.

    There the trigger is a branch merge that fails (resource limit, deterministic) rather than
    a concurrent direct optimize, and the barrier is a pending BranchMerge recovery
    operation rather than a Mutation one. Everything downstream matches this report: reads stay
    healthy, every write path returns the same operation id indefinitely, and only a server
    restart clears it.

    Two details from #694 that may be useful here:

    • The wedge happened with no concurrency and no external writer — a single server-side
      merge rejected by its own ordered_scan_input_batch_bytes limit. So the trigger surface
      is wider than "direct-storage writer racing a serving process".
    • A second wedge followed from a 0.6 MB / 1,819-entity payload against a main holding
      ~50k artifacts, which suggests the scan scales with the merge target rather than the
      branch delta.

    Flagging it here since this issue is closed as completed — whatever addressed the Mutation
    barrier doesn't appear to cover the BranchMerge one.

  4. azimafroozeh commented on Sep 9, 2026

    @azimafroozeh
    Collaborator

    The fix in #561 covers Mutation/Load, not BranchMerge. The 0.10.0 code matches this symptom, but I haven’t reproduced it yet. I’ll investigate in #694.

  5. aaltshuler commented on Oct 5, 2026

    @aaltshuler
    Contributor

    Resolved in #561; #744 subsequently removed the recovery-sidecar protocol. The issue-specific live-handle regression remains in place. Closing as completed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P-highHigh priorityacceptedTriaged and validated; open for a PRbugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions