Skip to content

syncer: add region syncer observability#10914

Open
okJiang wants to merge 1 commit into
tikv:masterfrom
okJiang:codex/region-syncer-observability
Open

syncer: add region syncer observability#10914
okJiang wants to merge 1 commit into
tikv:masterfrom
okJiang:codex/region-syncer-observability

Conversation

@okJiang

@okJiang okJiang commented Jun 18, 2026

Copy link
Copy Markdown
Member

What problem does this PR solve?

Issue Number: ref #10719

What is changed and how does it work?

Add RegionSyncer metrics for follower readiness, full sync attempts, last full sync duration, history buffer capacity and misses, downstream lag, and stream lifecycle events.

Update the PD Grafana dashboard to show the new RegionSyncer and history buffer signals without adding alerts.

Check List

Tests

  • Unit test
  • Integration test
  • Manual test (add detailed scripts or steps below)

Manual test:

  • node -e "JSON.parse(require('fs').readFileSync('metrics/grafana/pd.json','utf8')); console.log('pd.json ok')"

Release note

None.

Summary by CodeRabbit

  • New Features
    • Enhanced Grafana monitoring for the Region storage dashboard with new panels covering client readiness, full-sync counts/durations, history buffer sizing and events, downstream lag, and stream event rates.
    • Expanded Prometheus instrumentation for finer-grained region syncer observability.
  • Bug Fixes
    • Improved “client ready” metric updates to reflect more lifecycle states and error/reset scenarios.
  • Tests
    • Added/extended unit tests to assert new history-buffer, full-sync, downstream lag, and stream-event Prometheus metrics, including failure and timeout paths.

@ti-chi-bot ti-chi-bot Bot added release-note-none Denotes a PR that doesn't merit a release note. dco-signoff: yes Indicates the PR's author has signed the dco. labels Jun 18, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign overvenus for approval. For more information see the Code Review Process.
Please ensure that each of them provides their approval before proceeding.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot ti-chi-bot Bot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Jun 18, 2026
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds Prometheus instrumentation for region syncer client readiness, full-sync outcomes, history buffer state, downstream lag, and stream events, updates syncer lifecycle code to emit those metrics, adds tests for the new gauges and counters, and exposes the metrics in seven new Grafana panels.

Changes

Region Syncer Prometheus Metrics Instrumentation

Layer / File(s) Summary
Metric definitions and helpers
pkg/syncer/metrics.go
Adds metric label constants, collectors, per-label maps, registration, and helper functions for client-ready, full-sync, history buffer, and stream event metrics.
Client-ready metric lifecycle
pkg/syncer/client.go
Sets the client-ready gauge in reset, error-response handling, first successful sync, goroutine cleanup, and stream receive error paths.
History buffer metric instrumentation
pkg/syncer/history_buffer.go
Adds observeMetricsLocked(), calls it from constructor, record, reset, and resize paths, and classifies resize counters as grow or shrink.
Full-sync reason tracking and lag observation
pkg/syncer/server.go
Adds downstream lag state, stream bind/unbind lifecycle, full-sync reason selection, deferred full-sync metric observation, error reason handling, and catch-up lag updates.
Stream event metrics and send handling
pkg/syncer/server.go
Increments bind, unbind, send-error, stream-closed, context-canceled, and send-timeout counters in stream response and teardown paths.
Tests for new metrics
pkg/syncer/history_buffer_test.go, pkg/syncer/server_test.go
Adds metric assertions for history buffer behavior, full-sync outcomes, downstream lag, stream events, and client-ready gauge transitions.
Grafana dashboard panels
metrics/grafana/pd.json
Adds seven Region syncer panels for client readiness, full-sync counts and duration, history buffer sizing and events, downstream lag, and stream events.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
    participant Client as RegionSyncer client
    participant Server as RegionSyncServer
    participant Buffer as historyBuffer
    participant Metrics as metrics.go

    Client->>Metrics: setRegionSyncerClientReadyMetrics(false/true)
    Server->>Metrics: observeFullSyncMetrics(result, reason, duration)
    Server->>Metrics: observeDownstreamLagMetricsLocked(sendIndex)
    Buffer->>Metrics: observeHistoryBufferMetrics(length, capacity, maxCapacity)
    Server->>Metrics: incStreamEventMetrics(event)
Loading

Possibly related PRs

  • tikv/pd#10712: Touches pkg/syncer/history_buffer.go in the same locked resize and record paths that this PR instruments.
  • tikv/pd#10716: Touches pkg/syncer/history_buffer.go and pkg/syncer/server.go in the same syncer state paths that this PR extends with metrics.

Suggested labels: lgtm, approved

Suggested reviewers: lhy1024, rleungx, bufferflies

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding RegionSyncer observability.
Description check ✅ Passed The PR description includes the issue reference, change summary, manual test, and release note required by the template.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
pkg/syncer/server_test.go (1)

265-353: ⚡ Quick win

Use bounded contexts for potentially blocking test calls.

Line 279, Line 292, Line 334, and Line 349 use context.Background() on paths that can block; if behavior regresses, these tests can hang instead of failing fast. Prefer context.WithTimeout per call site.

Suggested patch
 func TestFullSyncMetrics(t *testing.T) {
 	re := require.New(t)
+	testCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
@@
-	re.NoError(syncFullRegionsForTest(context.Background(), successSyncer, successSyncStream, 10))
+	re.NoError(syncFullRegionsForTest(testCtx, successSyncer, successSyncStream, 10))
@@
 	done := make(chan error, 1)
 	go func() {
-		done <- syncFullRegionsForTest(context.Background(), failureSyncer, failureSyncStream, startIndex)
+		done <- syncFullRegionsForTest(testCtx, failureSyncer, failureSyncStream, startIndex)
 	}()
@@
 func TestDownstreamLagAndStreamEventMetrics(t *testing.T) {
 	re := require.New(t)
+	testCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
@@
-	re.NoError(syncer.sendDownstream(context.Background(), downstream, syncStream, false))
+	re.NoError(syncer.sendDownstream(testCtx, downstream, syncStream, false))
@@
-	re.Error(timeoutSyncer.sendDownstream(context.Background(), "pd-timeout-follower", timeoutSyncStream, false))
+	re.Error(timeoutSyncer.sendDownstream(testCtx, "pd-timeout-follower", timeoutSyncStream, false))

As per coding guidelines, "Use context-aware timeouts and backoff for retries".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/syncer/server_test.go` around lines 265 - 353, The test functions
TestFullSyncMetrics and TestDownstreamLagAndStreamEventMetrics use
context.Background() on potentially blocking operations like
syncFullRegionsForTest and sendDownstream calls, which can cause tests to hang
indefinitely if there's a regression instead of failing fast. Replace each
context.Background() call with context.WithTimeout to add appropriate timeouts
at each call site, ensuring tests fail quickly rather than hang when blocking
behavior regresses.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/syncer/server.go`:
- Around line 86-88: The `downstream` and `downstreamLagGauge` fields lack
proper synchronization protection, causing data races between write operations
(lines 136-167) and read operations in send paths (lines 148-157). Add a
dedicated RWMutex field to the struct alongside these metric fields, then
protect all mutations and reads of `downstream` and `downstreamLagGauge` with
appropriate lock/RLock calls to ensure consistent access and prevent nil-gauge
panics during unbind/shutdown scenarios.

---

Nitpick comments:
In `@pkg/syncer/server_test.go`:
- Around line 265-353: The test functions TestFullSyncMetrics and
TestDownstreamLagAndStreamEventMetrics use context.Background() on potentially
blocking operations like syncFullRegionsForTest and sendDownstream calls, which
can cause tests to hang indefinitely if there's a regression instead of failing
fast. Replace each context.Background() call with context.WithTimeout to add
appropriate timeouts at each call site, ensuring tests fail quickly rather than
hang when blocking behavior regresses.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e427aad5-7dc7-42ca-85b7-980b8a0e5a0b

📥 Commits

Reviewing files that changed from the base of the PR and between f59e8a8 and 2d0351d.

📒 Files selected for processing (7)
  • metrics/grafana/pd.json
  • pkg/syncer/client.go
  • pkg/syncer/history_buffer.go
  • pkg/syncer/history_buffer_test.go
  • pkg/syncer/metrics.go
  • pkg/syncer/server.go
  • pkg/syncer/server_test.go

Comment thread pkg/syncer/server.go
Comment on lines +86 to 88
downstream string
downstreamLagGauge prometheus.Gauge
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Synchronize downstream metric fields with a single lock domain.

Lines 136-167 mutate downstream/downstreamLagGauge without synchronization, while Lines 148-157 read them in send paths. This lock mismatch can race during unbind/shutdown and lead to nil-gauge panics or inconsistent lag reporting.

Suggested fix (use a dedicated mutex for metric fields)
 type regionSyncStream struct {
@@
+	metricsMu syncutil.Mutex
 	downstream         string
 	downstreamLagGauge prometheus.Gauge
 }
@@
 func (s *regionSyncStream) setDownstreamMetrics(name string, leaderNextIndex uint64) {
+	s.metricsMu.Lock()
 	s.downstream = name
 	s.downstreamLagGauge = regionSyncerDownstreamLagGauge.WithLabelValues(name)
-	s.observeDownstreamLagMetrics(leaderNextIndex)
+	s.metricsMu.Unlock()
+	s.observeDownstreamLagMetrics(leaderNextIndex)
 }
@@
 func (s *regionSyncStream) observeDownstreamLagMetricsLocked(leaderNextIndex uint64) {
-	if s.downstreamLagGauge == nil {
+	s.metricsMu.Lock()
+	gauge := s.downstreamLagGauge
+	s.metricsMu.Unlock()
+	if gauge == nil {
 		return
 	}
 	sendIndex := s.getSendIndexLocked()
 	if sendIndex >= leaderNextIndex {
-		s.downstreamLagGauge.Set(0)
+		gauge.Set(0)
 		return
 	}
-	s.downstreamLagGauge.Set(float64(leaderNextIndex - sendIndex))
+	gauge.Set(float64(leaderNextIndex - sendIndex))
 }
@@
 func (s *regionSyncStream) deleteDownstreamMetrics() {
-	if s.downstream == "" {
-		return
-	}
-	regionSyncerDownstreamLagGauge.DeleteLabelValues(s.downstream)
-	s.downstream = ""
-	s.downstreamLagGauge = nil
+	s.metricsMu.Lock()
+	downstream := s.downstream
+	s.downstream = ""
+	s.downstreamLagGauge = nil
+	s.metricsMu.Unlock()
+	if downstream != "" {
+		regionSyncerDownstreamLagGauge.DeleteLabelValues(downstream)
+	}
 }

As per coding guidelines, "Guard shared state with mutex/RWMutex; keep lock ordering consistent".

Also applies to: 136-167

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/syncer/server.go` around lines 86 - 88, The `downstream` and
`downstreamLagGauge` fields lack proper synchronization protection, causing data
races between write operations (lines 136-167) and read operations in send paths
(lines 148-157). Add a dedicated RWMutex field to the struct alongside these
metric fields, then protect all mutations and reads of `downstream` and
`downstreamLagGauge` with appropriate lock/RLock calls to ensure consistent
access and prevent nil-gauge panics during unbind/shutdown scenarios.

Source: Coding guidelines

Comment thread pkg/syncer/server.go
oldStream.deleteDownstreamMetrics()
incStreamEventMetrics(streamEventUnbind)
}
syncStream.setDownstreamMetrics(name, leaderNextIndex)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This introduces a lock-order inversion: binding holds s.mu and then takes sendMu, while sendDownstream takes sendMu before s.mu.RLock. This is why the deadlock-tagged race tests fail. Please avoid taking sendMu while holding s.mu, or move this observation after releasing s.mu.

Signed-off-by: okjiang <819421878@qq.com>
@okJiang okJiang force-pushed the codex/region-syncer-observability branch from 2d0351d to 6a17cc9 Compare July 3, 2026 04:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
pkg/syncer/metrics.go (1)

207-209: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor: string-concatenation map key is fragile.

fullSyncMetricKey joins result and reason with "/". It's safe today since none of the constants contain /, but a struct key {result, reason} would be more type-safe and avoid any future delimiter collision.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/syncer/metrics.go` around lines 207 - 209, The current fullSyncMetricKey
helper builds a composite key by concatenating result and reason with a slash,
which is fragile and can collide if either value ever contains that delimiter.
Update the metrics keying in fullSyncMetricKey and its callers to use a
type-safe composite struct or equivalent map key instead of string
concatenation, keeping the result and reason fields separate and adjusting any
lookup/storage logic that currently depends on the joined string.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@pkg/syncer/metrics.go`:
- Around line 207-209: The current fullSyncMetricKey helper builds a composite
key by concatenating result and reason with a slash, which is fragile and can
collide if either value ever contains that delimiter. Update the metrics keying
in fullSyncMetricKey and its callers to use a type-safe composite struct or
equivalent map key instead of string concatenation, keeping the result and
reason fields separate and adjusting any lookup/storage logic that currently
depends on the joined string.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2ddec7c6-51c4-4ba8-b288-b0d95ed3901c

📥 Commits

Reviewing files that changed from the base of the PR and between 2d0351d and 6a17cc9.

📒 Files selected for processing (7)
  • metrics/grafana/pd.json
  • pkg/syncer/client.go
  • pkg/syncer/history_buffer.go
  • pkg/syncer/history_buffer_test.go
  • pkg/syncer/metrics.go
  • pkg/syncer/server.go
  • pkg/syncer/server_test.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • pkg/syncer/history_buffer.go
  • pkg/syncer/client.go
  • pkg/syncer/history_buffer_test.go
  • metrics/grafana/pd.json
  • pkg/syncer/server_test.go
  • pkg/syncer/server.go

@ti-chi-bot

ti-chi-bot Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

@okJiang: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
pull-unit-test-next-gen-2 6a17cc9 link true /test pull-unit-test-next-gen-2
pull-unit-test-next-gen-3 6a17cc9 link true /test pull-unit-test-next-gen-3

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

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

Labels

dco-signoff: yes Indicates the PR's author has signed the dco. release-note-none Denotes a PR that doesn't merit a release note. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants