Skip to content

[Bug]: Main CN authentication stalls at logtail barriers during catalog contention, causing ANLI Proxy 20508 failures #29965

Open

Description

@LeftHandCold

Is there an existing issue for the same bug?

  • I have checked the existing issues.

Related to the receiver/catalog-contention investigation in #29275, but this report tracks normal authentication becoming unavailable before logtail recovery, not the previously observed 185-second forced-subscription recovery loop. No precise open issue for this Main incident was found.

Branch Name

main

Commit ID

  • Observed: d382fd5fa2081fcff6bcf712160d4f93c541a05d.
  • Latest main inspected: 5a5501b3e1048f3d19d96824cc68a62b7f0fcb07. Its pkg/vm/engine/disttae/cache/catalog.go is byte-identical to the incident version. This is a source check, not an end-to-end reproduction on latest main.
  • Nightly harness: a8a59e037061a12c641ec409ba458d7af54ec6c7.

Other Environment Information

  • Main nightly run 38149989746.
  • ANLI TP write job 114584791861.
  • Namespace: mo-main-family-38149989746-1.
  • Affected TP CN: nightly-regression-dis-tp-cn-24x2z, UUID 62373261-3336-3733-3136-323265326130.
  • Shared TN, one TP CN and two AP CNs; two Proxy pods both recorded backend authentication timeouts targeting the TP CN.
  • Final resource artifact: all nine pods ready/running, restart counts zero. No OOMKilled evidence for this event.
  • Failing scenario: insert_l2, eight writers, eight searchers, 200 rounds, 128 dimensions, 10 IVF lists.

Actual Behavior

The ANLI scenario reported 512 search failures and 508 write failures, with Proxy error 20508. Five subsequent DML scenarios were skipped. The same scenario had passed in the preceding run; this alone does not establish an introducing commit.

This is a real availability failure, not an ANN result-comparison failure. Proxy established backend connections but timed out after 10 seconds at read-auth-response. The generic text "possibly due to too many active transactions" must not be treated as proof of transaction-admission saturation.

Timeline

Times below are 2026-10-11 UTC; add eight hours for 2026-10-12 CST.

Time Observation
22:18:43.043470 TP background statistics logs check.move.on, state=false (task enabled), before calling its preparation path.
22:18:45.901 ANLI has 7,413 searches, 32,716 inserted rows and zero errors. Subsequent progress samples temporarily stop advancing.
22:19:07.304 First visible Proxy backend authentication timeout: read-auth-response, approximately 10 seconds. Both proxies target the TP CN. Client 20508 errors start in the same second.
22:19:14.110967 Statistics alpha-task completion reports 90 tables and takes=957.462944ms. This duration excludes the preceding preparation path.
22:20:14.111037 Next statistics preparation cycle begins after check.move.on.
22:20:22.643 / 22:20:52.643 TP mo_logtail_queue_size{type="receive"} samples are 16 / 15.
22:20:31.684 Another Proxy authentication-timeout burst; further client errors.
22:20:39.722045 CN connection timing records Authenticate=18.037506430s, versus CheckTenant=57.8ms, CheckUser=60.0ms and CheckRole=115.1ms. These sub-durations do not explain most of the authentication wait.
22:21:00.431 onward TN records closed response admission / context cancellation for affected session activity. This is not claimed to be the initial queue-full transition.
22:21:01.866362 TP transaction client finally changes to paused. This is roughly 114 seconds after the first authentication timeout.
22:21:02.007-.496 System-table subscription completion records have durations approximately 134ms / 123ms / 125ms.
22:21:06.967556 TP transaction client returns to normal: 5.101194 seconds paused.
22:21:08.154668 Statistics alpha-task completion: 83 tables, takes=6.272957954s. ANLI counters are advancing again by this time.
22:21:34 Scenario fails with the accumulated 512/508 errors; it did not remain permanently hung.

The two check.move.on to alpha-completion intervals are approximately 31.07s and 54.04s. Subtracting the logged alpha durations leaves approximately 30.11s and 47.77s outside alphaTask. Code places preparation before alphaTask, but these residuals are not isolated measurements of BTree lock-hold time or of GetTableByIdAndTime alone.

Incident profiles identify the blocking paths

Two TP goroutine aggregates were requested for 22:18:45-22:19:30 and 22:20:15-22:20:55 UTC. Both contain all three paths:

Authentication:
  MOServer.handshake -> MysqlProtocolImpl.Authenticate
  -> Session.AuthenticateUser -> prepareAuthenticationSnapshot
  -> acquireLogtailReadBarrier -> AcquireLogtailReadBarrier
  -> acquireAppliedLogtailReadBarrier -> logTailSubscriber.readBarrier
  -> LogtailClient.ReadBarrier -> runtime.selectgo

Background statistics:
  GetMOTableStatsExecutor -> tableStatsExecutor -> prepare
  -> getChangedTableList -> buildTablePairFromCache
  -> CatalogCache.GetTableByIdAndTime
  -> BTreeG.Ascend -> nodeScan -> GetTableByIdAndTime.func1

Logtail receiver:
  PushClient.receiveLogtails -> receiveOneLogtail
  -> dispatchUpdateResponse -> consumeUpdateLogTail
  -> applyToCatalogCache -> CatalogCache.InsertTable / DeleteTable
  -> setTableItemLocked -> BTreeG.SetHint -> sync.RWMutex.Lock

The first aggregate has weights 104 for the authentication-barrier stack, 8 for the statistics scan, and 8 for the receiver's catalog write-lock waits. The second has weights 134, 12 and 12 respectively. These are aggregate profile weights, not numbers of simultaneous goroutines or seconds of continuous lock ownership.

In the CPU profile selected for 22:18:40-22:19:30, stacks through the statistics GetTableByIdAndTime path account for 28.20 CPU-s of 119.82 sampled CPU-s (23.5%). This is an inclusive sampled CPU fraction, not wall-time attribution, whole-host utilization or proof that this path alone accounts for every failed connection.

Additional bounded metrics: TP RSS samples are about 31.1-37.7 GiB; the sampled wait-active queue is zero. The Go GC pause sum increases by about 39.8ms across the approximately four-minute sampled interval. These do not support a seconds-long stop-the-world pause or OOM as the explanation, but do not eliminate all CPU scheduling or allocation overhead.

Expected Behavior

Background catalog/statistics work should not prevent a healthy CN from making bounded progress on logtail consumption and ordinary connection authentication, causing repeated backend handshake deadline failures.

Any fix must preserve the authentication snapshot/read-barrier correctness guarantee. Bypassing that barrier, authenticating against stale catalog state, or merely increasing Proxy timeouts is not an established safe fix.

Steps to Reproduce

This report is based on the actual integration run and server profiles, not a synthetic timeout injection. A minimal deterministic end-to-end reproduction has not been completed.

  1. Use the Main revision and the shared Family topology above.
  2. Run the existing ANLI insert_l2 scenario through Proxy while the TP CN has the accumulated Family catalog and its normal background statistics task.
  3. Capture connection-stage timing, authentication goroutines, catalog receiver stacks, statistics preparation cost and logtail receive-queue samples together.
  4. Observe the documented 10-second backend authentication timeouts and client 20508 failures.

Next discriminating experiment: hold catalog contents and foreground connection workload fixed in an isolated test, compare normal statistics preparation against a narrowly controlled no-scan baseline, and measure catalog writer progress plus authentication/barrier latency. Do not disable statistics or correctness barriers on a shared production cluster merely to test this hypothesis.

Additional information

Exact-version source supports the mechanism

  • GetTableByIdAndTime scans a name-ordered catalog BTree to find a table ID at a snapshot; the source explicitly documents O(n) lookup. Retained versions are traversed too. It has no context parameter.
  • getChangedTableList repeats this lookup for returned table IDs; buildTablePairFromCache resolves each against the catalog.
  • The pinned dependency, tidwall/btree v1.7.0 Ascend, retains the BTree read lock for the traversal and callbacks. SetHint requires its write lock. This is ordinary RWMutex contention, not evidence of a bug in the BTree implementation.
  • prepareAuthenticationSnapshot acquires the logtail barrier before reading authentication catalog data.
  • LogtailClient.Receive completes barrier responses while consuming the same stream. A receiver blocked applying an earlier catalog update cannot immediately consume later barrier responses.

Assessment: The handshake failures, authentication barrier waits, catalog receiver write-lock waits and repeated statistics scans are observed. The source explains how catalog scan contention can delay logtail and therefore authentication. Statistics-driven catalog contention is the leading initiating-cause hypothesis, not yet a uniquely proven cause for the entire incident. Aggregate stacks do not identify one continuously held lock, the exact catalog cardinality is not reconstructed, and no controlled intervention has yet removed this bottleneck while keeping the rest of the workload constant.

Existing-issue and regression boundary

Activity

  1. added
    kind/bugSomething isn't working
    needs-triageNeeds evaluation before prioritization. Not yet decided whether to proceed
    on Oct 12, 2026
  2. LeftHandCold commented on Oct 12, 2026

    @LeftHandCold
    CollaboratorAuthor

    Root-cause investigation and scoped main fix

    PR: #29983, commit d32ae8663d0251b565b76b8b43cb0356e518e82c, based on main 02a2823c2007d5e4fb4ada148e93743df337551a.

    What is proven

    There is a reproducible catalog lookup amplification defect in changed-table statistics preparation:

    1. getChangedTableList resolves requested table IDs individually through buildTablePairFromCache.
    2. CatalogCache.GetTableByIdAndTime scans a BTree ordered by account/database/name/timestamp, not table ID. A missing or dropped ID can scan the entire scope. M requests against N retained entries can therefore cause O(M*N) work.
    3. Every traversal holds the tree read lock. The logtail receiver uses the same tree's write lock in InsertTable / DeleteTable. Repeated scans can interleave with many short catalog writes, delaying completion of an entire apply batch.
    4. Authentication's catalog-consistency read barrier is behind that work on the logtail stream. A delayed stream can consequently exceed Proxy's authentication response deadline.

    This is cumulative scan/write contention, not an assertion that one reader holds a lock continuously for tens of seconds. Go's writer preference does not remove the repeated work between successive writes.

    The original incident profiles contain all three corresponding paths: statistics scanning the catalog, catalog apply waiting for the BTree write lock, and authentication waiting at the logtail read barrier. This is not evidence that ANLI result parsing, Cosine computation, or Astra application transaction handling caused the failure.

    Additional incident evidence

    Original ANLI job, MO d382fd5fa2081fcff6bcf712160d4f93c541a05d.

    All timestamps below are UTC on 2026-10-11; add eight hours for Beijing time on 2026-10-12.

    • First observed Proxy authentication timeout: 22:19:07.304, before the later transaction-client pause.
    • Authentication sample at 22:20:39.722 took 18.0375s; its separately logged tenant/user/role checks took about 57.8ms / 60ms / 115ms.
    • In the native Prometheus sample interval 22:20:22.643 -> 22:20:52.643, consume-one-entry-catalog-cache recorded 4,482 completed observations, with a duration-sum increase of 29.740047441s. Every completed observation was within the 45.562053899ms histogram bucket boundary. These are completion-based aggregates, not exact individual timings or proof of exclusive wall-clock occupancy.
    • The enclosing consume-catalog-table duration sum increased 29.915443905s in that interval. It is nested and must not be added to the preceding value.
    • The same catalog-cache duration sum was 0.476467360s in the earlier 22:19:22-to-22:19:52 interval and 0.022110553s in the later 22:21:22-to-22:21:52 interval.
    • Transaction admission paused at 22:21:01.866 and recovered at 22:21:06.968, about 5.101s, rather than explaining the entire earlier authentication-error period.
    • The final workload recorded 512 search failures / 508 write failures. No new Pod restart or OOM was observed.

    Counterfactual reproduction on shanghai50

    An isolated finite-work harness uses real CatalogCache lookup and InsertTable / DeleteTable methods. Both arms use the same requested IDs, retained catalog, and fixed writer workload. It injects no sleep or artificially held lock.

    Synthetic scope: 45,559 names x 8 versions, 4,000 requested IDs (90 visible matches), and a fixed 1,000-row insertion followed by deletion workload.

    Path Reader completion Full catalog writer completion
    Existing per-ID lookup 38.418s 18.826s
    Actual production batch API from this PR 10.849ms 12.529ms
    No-scan controls N/A 1.46ms baseline / 1.66ms candidate

    Both arms check returned IDs and visible versions. This establishes the repeated-scan mechanism and the patch's reduction in work. The synthetic single-scope distribution is not claimed to match the original incident exactly; the recovered incident catalog count did not establish its complete scope/version distribution.

    The small permanent benchmark also measures 4,096 names x four versions with 64 missing IDs: 8.425ms -> 0.155ms, 23,040B / 65 allocations -> 3,208B / 5 allocations. Singleton lookups remain approximately 132us / 131us, with identical allocation counts. These are catalog measurements, not predicted application QPS.

    Fix and risk boundary

    The PR groups changed-table ID lookups by account/database and traverses each scope once, preserving the existing snapshot/name visibility rules, tombstones, replacement identities, order, duplicates, missing IDs, view exclusion, and callback metadata.

    Production scope is two files (+117/-20 lines). No persistent index/cache, worker, retry, timeout, protocol, authentication barrier, GC rule, routing rule, or vector execution change is introduced. Ordinary scalar lookups and singleton behavior remain unchanged. Scratch state is per-call and O(requested IDs).

    This is not presented as a proven newly introduced regression: the scalar lookup and statistics loop have long histories (#17721 and #20222). Their presence does not identify which later workload or change first exposed the 2026 incident.

    Verification and remaining acceptance

    All passed on shanghai50 with Go 1.27.1, the repository CGo wrapper, verified native-artifact provenance, four CPU affinity slots, and exact delivery-file hashes:

    • Full disttae/cache and disttae packages, normal and race modes.
    • New snapshot visibility, scope/identity, metadata/order, and exported changed-table consumer tests.
    • Concurrent writer/GC test under race, 100 repetitions.
    • Existing real engine tests TestMoTableStatsMoCtl and TestHandleGetChangedList.
    • Formatting, diff checks, and complete self-review of snapshot visibility, reverse consumers, lock ordering, and memory ownership.

    Keep this issue open for same-workload Family acceptance. The code defect and its controlled counterfactual are proven; the original 512/508-error workload has not been replayed end to end. The PR does not claim that every authentication timeout is uniquely caused by this defect, impose a latency bound on arbitrarily large catalog scopes, or eliminate all other scalar catalog consumers. GitHub CI and subsequent Family results must be tracked separately from the completed local/50 validation.

  3. added
    severity/s0Active / top priority for current sprint. Owner has committed to working on it now.
    and removed
    needs-triageNeeds evaluation before prioritization. Not yet decided whether to proceed
    on Oct 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

kind/bugSomething isn't workingseverity/s0Active / top priority for current sprint. Owner has committed to working on it now.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions