Skip to content

Reduce catalog contention for RecordMetricsCritical - #4776

Open
ilidemi wants to merge 5 commits into
mainfrom
customer-cached-dynconf
Open

Reduce catalog contention for RecordMetricsCritical#4776
ilidemi wants to merge 5 commits into
mainfrom
customer-cached-dynconf

Conversation

@ilidemi

@ilidemi ilidemi commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

When an instance is overloaded with an overly parallel snapshot, RecordMetricsCritical can repeatedly fail to run, not being able to get a catalog connection. This change removes a few silly ways in which catalog was hammered or its client goroutines were delayed.

Fixes:

  1. The helper function for getting or constructing the catalog pool was acquiring a mutex, checking if the pool is already constructed, and doing a ping. This made all callers serialize on a network call under a mutex. Additionally, issuing a ping doesn't pin the connection to the caller or anything like that, so it just gets released back after stalling everyone. Change this to double-checked locking and keep the ping just to construction, after pool is constructed there's no outer mutex wait at all.
  2. Every partition reads PEERDB_S3_BYTES_PER_AVRO_FILE and PEERDB_S3_PART_SIZE dynconf, after resolving the above they were the next biggest catalog consumers. We want them to stay hot-configurable but there's no need to reread the values this often. Added a 30 second cache on both, global so workers don't need to re-poll individually. It is making dynconf more messy, but hopefully in a contained way, as the contention problem needs to be solved but redoing dynconf is out of scope right now.
  3. We do 6 catalog updates per partition for bookkeeping. 2 of them were easy to merge, so doing that and reducing to 5.

Results from running 8x8 snapshots from our mysql perf instance and analyzing 120s traces:

Measurement Base +avoid poolMutex +dynconf cache +merged bookkeeping (avg of 2 traces)
Catalog poolMutex wait 41.33m 0 0 0
Catalog query pool wait 5.91m 53.43m 29.71m 25.65m
Mean query pool wait 0.47s 5.88s 4.08s 3.80s
Dynconf pool-wait events 181 145 14 13
RecordMetricsCritical waiting 29.96s 25.59s 18.53s 14.16s

As you can see from the last row, this doesn't fully solve the situation (especially given goroutines can't have priorities). Proper solution is to limit the parallelism allowed/not-discouraged per instance size, but reducing repeat calls doesn't hurt.

Closes DBI-1107

@ilidemi
ilidemi requested a review from a team as a code owner September 5, 2026 06:07
@claude

claude Bot commented Sep 5, 2026

Copy link
Copy Markdown

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: Three unrelated ClickHouse-cluster e2e subtests each hit the fixed 60s SetupCDCFlowStatusQuery poll cap (UNEXPECTED STATUS TIMEOUT STATUS_SETUP/STATUS_SNAPSHOT) in two of three matrix jobs while the third passed the same suite, matching a known slow-cluster-setup flake the repo already skips a test for — though the PR's catalog-contention changes touch the same status path, so a re-run is warranted to confirm.
Confidence: 0.8

✅ Automatically retrying the workflow

View workflow run

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: Infrastructure flake: the flow-api container failed to start on all 3 attempts because Docker could not bind host port 40003 ("address already in use"), so core services never came up and zero tests ran, while the other two matrix legs on the same commit passed.
Confidence: 0.96

✅ Automatically retrying the workflow

View workflow run

Comment thread flow/internal/catalog.go
exceptions.NewCatalogError(fmt.Errorf("unable to establish connection with catalog: %w", err))
}
}
if pool.Load() == nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe do early return instead of branching two times:

if pool.Load() == nil {
  if pool.Load() == nil {
     // do smth
  }
}

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.

2 participants