Skip to content

syncer(dm): initialize binlog metrics from checkpoint - #12767

Open
expxiaoli wants to merge 4 commits into
pingcap:masterfrom
expxiaoli:fix_12765_checkpoint
Open

syncer(dm): initialize binlog metrics from checkpoint#12767
expxiaoli wants to merge 4 commits into
pingcap:masterfrom
expxiaoli:fix_12765_checkpoint

Conversation

@expxiaoli

@expxiaoli expxiaoli commented Jul 27, 2026

Copy link
Copy Markdown

What problem does this PR solve?

Issue Number: ref #12765

When a DM task is loaded on another worker but remains stopped or paused, the syncer is initialized and its metrics are registered, but the replication loop does not run. As a result, the syncer binlog file and position gauges keep their default values even when a valid persisted checkpoint exists.

The master binlog gauge continues to be updated by status polling, so the default syncer file number 0 can produce a significantly inflated master-to-syncer binlog gap.

This PR addresses the checkpoint metric initialization part of the issue.

What is changed and how it works?

  • After loading the persisted checkpoint during syncer initialization, initialize the syncer binlog position gauge from the global checkpoint.
  • Parse the checkpoint binlog filename and initialize the syncer binlog file gauge with its file index.
  • Set the file gauge to NaN when the checkpoint has no valid binlog filename, such as a fresh or GTID-only checkpoint, instead of exposing the default value 0 as a valid file number.
  • Add unit tests for file-position, missing-position, and GTID-only checkpoints.

Check List

Tests

  • Unit test
GOPRIVATE=none GONOPROXY=none GONOSUMDB=none GOSUMDB=sum.golang.org GOPROXY=https://proxy.golang.org go test ./dm/syncer -run '^TestInitSyncerBinlogMetrics$' -count=1
git diff --check

Questions

Will it cause performance regression or break compatibility?

No performance regression or compatibility break is expected. The gauges are initialized once during syncer initialization, and no replication or checkpoint behavior is changed. For checkpoints without a binlog filename, the file gauge now reports NaN until a valid file position is available instead of reporting a misleading zero.

Do you need to update user documentation, design documentation or monitoring documentation?

No. Metric names and labels are unchanged; this PR only corrects their initialization values.

Release note

Fix DM syncer binlog metrics to initialize from the persisted checkpoint after a worker restart or rescheduling.

Summary by CodeRabbit

  • Bug Fixes
    • Ensure binlog synchronization gauges are set as soon as synchronization tasks begin.
    • Keep binlog position metrics updated after checkpoint adjustments during synchronization.
    • When binlog filename parsing isn’t possible, the binlog file gauge now reports an unavailable value safely while preserving the binlog position.
  • Tests
    • Expanded coverage for valid, empty, malformed, and GTID-only checkpoint formats.
    • Added lifecycle coverage for initializing and refreshing binlog metrics.

@ti-chi-bot ti-chi-bot Bot added first-time-contributor Indicates that the PR was contributed by an external member and is a first-time contributor. release-note Denotes a PR that will be considered when it comes time to generate release notes. do-not-merge/needs-triage-completed area/dm Issues or PRs related to DM. labels Jul 27, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jul 27, 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 benjamin2037 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

@pingcap-cla-assistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@ti-chi-bot ti-chi-bot Bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Jul 27, 2026
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ac897fce-2801-4a4d-abd7-31b901cd9b45

📥 Commits

Reviewing files that changed from the base of the PR and between 6cefba8 and 3998b80.

📒 Files selected for processing (2)
  • dm/syncer/syncer.go
  • dm/syncer/syncer_metrics_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • dm/syncer/syncer.go

📝 Walkthrough

Walkthrough

Syncer now initializes binlog file and position gauges from checkpoints during setup and refreshes them after checkpoint selection and GTID adjustment. Unavailable filenames produce a NaN file gauge. Tests cover checkpoint formats and metric lifecycle behavior.

Changes

Binlog metric initialization

Layer / File(s) Summary
Checkpoint-based metric updates
dm/syncer/syncer.go
Syncer updates binlog position and file gauges during initialization and runtime refresh. It parses filename indexes, uses NaN for unavailable filenames, and warns for malformed non-empty filenames.
Metric lifecycle validation
dm/syncer/syncer_metrics_test.go
Tests cover valid, empty, malformed, and GTID-only checkpoints. Lifecycle tests verify persisted checkpoint loading, metadata refresh, SQL time-query handling, resource setup, and propagated errors.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Poem

I’m a rabbit checking gauges today,
Checkpoints guide each hop and play.
Files can parse or show NaN,
Positions follow the syncer’s plan.
Tests twitch their noses—green!

🚥 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 summarizes the main change: initializing DM syncer binlog metrics from the checkpoint.
Description check ✅ Passed The description covers the problem, issue reference, implementation, unit tests, compatibility impact, documentation impact, and release note.
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 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions
The command is terminated due to an error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions


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.

@expxiaoli expxiaoli left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fresh tasks can replace the persisted checkpoint with a start-time or metadata location in Run. Refresh the binlog gauges after GTID adjustment so they reflect the final start point before the first event.

@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)
dm/syncer/syncer_metrics_test.go (1)

42-65: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add coverage for malformed non-empty binlog filenames.

The cases cover empty and GTID-only checkpoints, but not a checkpoint with a non-empty, unparsable filename. Add one to verify the file gauge becomes NaN while the position gauge preserves the checkpoint position.

Proposed test case
 		{
 			name:         "fresh checkpoint with empty binlog filename",
 			checkpoint:   binlog.MustZeroLocation(mysql.MySQLFlavor),
 			expectedFile: math.NaN(),
 			expectedPos:  float64(binlog.MinPosition.Pos),
 		},
+		{
+			name: "checkpoint with malformed binlog filename",
+			checkpoint: binlog.NewLocation(mysql.Position{
+				Name: "not-a-binlog-name",
+				Pos:  123,
+			}, nil),
+			expectedFile: math.NaN(),
+			expectedPos:  123,
+		},

As per coding guidelines, DM fixes and features should include unit tests.

🤖 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 `@dm/syncer/syncer_metrics_test.go` around lines 42 - 65, Add a test case
alongside the existing checkpoint cases in the syncer metrics test table using a
non-empty, malformed binlog filename and a known position. Assert that the file
gauge result is math.NaN() while the position gauge retains the checkpoint’s
position, reusing the existing checkpoint construction and expected-value
conventions.

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.

Nitpick comments:
In `@dm/syncer/syncer_metrics_test.go`:
- Around line 42-65: Add a test case alongside the existing checkpoint cases in
the syncer metrics test table using a non-empty, malformed binlog filename and a
known position. Assert that the file gauge result is math.NaN() while the
position gauge retains the checkpoint’s position, reusing the existing
checkpoint construction and expected-value conventions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5ce2776c-08f2-4670-b8d6-77ba7def8487

📥 Commits

Reviewing files that changed from the base of the PR and between 02a72ff and 8e089ff.

📒 Files selected for processing (2)
  • dm/syncer/syncer.go
  • dm/syncer/syncer_metrics_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • dm/syncer/syncer.go

Cover the non-empty filename parse failure path and initialize the test context required by its warning log.
@expxiaoli

Copy link
Copy Markdown
Author

🧹 Nitpick comments (1)

dm/syncer/syncer_metrics_test.go (1)> 42-65: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add coverage for malformed non-empty binlog filenames.
The cases cover empty and GTID-only checkpoints, but not a checkpoint with a non-empty, unparsable filename. Add one to verify the file gauge becomes NaN while the position gauge preserves the checkpoint position.

Proposed test case

 		{
 			name:         "fresh checkpoint with empty binlog filename",
 			checkpoint:   binlog.MustZeroLocation(mysql.MySQLFlavor),
 			expectedFile: math.NaN(),
 			expectedPos:  float64(binlog.MinPosition.Pos),
 		},
+		{
+			name: "checkpoint with malformed binlog filename",
+			checkpoint: binlog.NewLocation(mysql.Position{
+				Name: "not-a-binlog-name",
+				Pos:  123,
+			}, nil),
+			expectedFile: math.NaN(),
+			expectedPos:  123,
+		},

As per coding guidelines, DM fixes and features should include unit tests.

🤖 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 `@dm/syncer/syncer_metrics_test.go` around lines 42 - 65, Add a test case
alongside the existing checkpoint cases in the syncer metrics test table using a
non-empty, malformed binlog filename and a known position. Assert that the file
gauge result is math.NaN() while the position gauge retains the checkpoint’s
position, reusing the existing checkpoint construction and expected-value
conventions.

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.

Nitpick comments:
In `@dm/syncer/syncer_metrics_test.go`:
- Around line 42-65: Add a test case alongside the existing checkpoint cases in
the syncer metrics test table using a non-empty, malformed binlog filename and a
known position. Assert that the file gauge result is math.NaN() while the
position gauge retains the checkpoint’s position, reusing the existing
checkpoint construction and expected-value conventions.

ℹ️ Review info

🧹 Nitpick comments (1)

dm/syncer/syncer_metrics_test.go (1)> 42-65: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add coverage for malformed non-empty binlog filenames.
The cases cover empty and GTID-only checkpoints, but not a checkpoint with a non-empty, unparsable filename. Add one to verify the file gauge becomes NaN while the position gauge preserves the checkpoint position.

Proposed test case

 		{
 			name:         "fresh checkpoint with empty binlog filename",
 			checkpoint:   binlog.MustZeroLocation(mysql.MySQLFlavor),
 			expectedFile: math.NaN(),
 			expectedPos:  float64(binlog.MinPosition.Pos),
 		},
+		{
+			name: "checkpoint with malformed binlog filename",
+			checkpoint: binlog.NewLocation(mysql.Position{
+				Name: "not-a-binlog-name",
+				Pos:  123,
+			}, nil),
+			expectedFile: math.NaN(),
+			expectedPos:  123,
+		},

As per coding guidelines, DM fixes and features should include unit tests.

🤖 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 `@dm/syncer/syncer_metrics_test.go` around lines 42 - 65, Add a test case
alongside the existing checkpoint cases in the syncer metrics test table using a
non-empty, malformed binlog filename and a known position. Assert that the file
gauge result is math.NaN() while the position gauge retains the checkpoint’s
position, reusing the existing checkpoint construction and expected-value
conventions.

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.

Nitpick comments:
In `@dm/syncer/syncer_metrics_test.go`:
- Around line 42-65: Add a test case alongside the existing checkpoint cases in
the syncer metrics test table using a non-empty, malformed binlog filename and a
known position. Assert that the file gauge result is math.NaN() while the
position gauge retains the checkpoint’s position, reusing the existing
checkpoint construction and expected-value conventions.

ℹ️ Review info

Fix it

@expxiaoli

Copy link
Copy Markdown
Author

/retest-required

@D3Hunter D3Hunter left a comment

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.

Summary

  • Total findings: 2
  • Inline comments: 2
  • Summary-only findings (no inline anchor): 0
Findings (highest risk first)

🟡 [Minor] (2)

  1. Initializer name contradicts the helper's refresh behavior (dm/syncer/syncer.go:554)
  2. Checkpoint-to-metric lifecycle wiring is not covered (dm/syncer/syncer_metrics_test.go:30; dm/syncer/syncer.go:548; dm/syncer/syncer.go:1860)

Comment thread dm/syncer/syncer.go Outdated
return nil
}

func (s *Syncer) initSyncerBinlogMetrics(checkpoint binlog.Location) {

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.

🟡 [Minor] Initializer name contradicts the helper's refresh behavior

Why
The helper is not limited to initialization: Run deliberately calls it again after the effective global checkpoint may change. Naming this repeated gauge update initSyncerBinlogMetrics obscures that it is safe and intended to overwrite existing metric values.

Scope
dm/syncer/syncer.go:554

Risk if unchanged
Future callers may treat the helper as a one-time setup operation, miss required refreshes after checkpoint changes, or add initialization-only work that is unsafe on the second call.

Evidence
The new comment at dm/syncer/syncer.go:1857 explicitly says to "Refresh" the metrics, but line 1860 invokes initSyncerBinlogMetrics; the same method is first called from Init at line 548, and the test name TestInitSyncerBinlogMetrics reinforces the one-time interpretation.

Change request
Prefer setSyncerBinlogMetrics or updateSyncerBinlogMetrics; the current name is confusing. Rename the helper and its test so both initialization and later refresh call sites describe the same repeated update semantics.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Make sense, I will fix it.

Comment thread dm/syncer/syncer_metrics_test.go Outdated
"github.com/stretchr/testify/require"
)

func TestInitSyncerBinlogMetrics(t *testing.T) {

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.

🟡 [Minor] Checkpoint-to-metric lifecycle wiring is not covered

Why
The changed behavior depends on publishing the loaded checkpoint in Init and then republishing the final start checkpoint after metadata, start-time, and GTID adjustments in Run, but the new test validates only the conversion helper in isolation.

Scope
dm/syncer/syncer_metrics_test.go:30; dm/syncer/syncer.go:548; dm/syncer/syncer.go:1860

Risk if unchanged
A missing or misplaced lifecycle call can leave an idle or newly started task reporting zero, NaN, or a stale persisted checkpoint until its first binlog event, while this regression test still passes and the intended observability fix is lost.

Evidence
TestInitSyncerBinlogMetrics constructs standalone gauges and invokes s.initSyncerBinlogMetrics(tc.checkpoint) directly; it never exercises Syncer.Init, LoadMeta, setGlobalPointByTime, adjustGlobalPointGTID, or either production call site.

Change request
Please add an integration test for this path that asserts the exposed gauges after Init loads a persisted checkpoint and after Run selects a different metadata, start-time, or GTID-adjusted checkpoint before any binlog event is consumed.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Make sense, I will fix it.

@D3Hunter D3Hunter left a comment

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.

Additional review finding

🟠 [Major] The original alerting issue is only partially fixed

The production change correctly restores the syncer file and position gauges from the persisted checkpoint. It changes the reported example from 347650 - 0 = 347650 to the real file gap, 347650 - 346652 = 998.

However, dm/metrics/alertmanager/dm_worker.rules.yml:128 still evaluates every exported syncer metric without checking task state. Because 998 > 1, an intentionally paused task can still trigger DM_binlog_file_gap_between_master_syncer after this PR. This fixes the misleading zero-based inflation, but it does not satisfy issue #12765's first expected behavior: paused or stopped tasks should not trigger the gap alert.

Please treat this as a partial fix and keep #12765 open until the alert is restricted to running tasks, for example by filtering or joining with dm_worker_task_state == 2. The join labels should preserve task and source identity so multi-source tasks do not produce ambiguous matches.

The checkpoint-to-metric lifecycle test gap is already covered by the existing inline comment at dm/syncer/syncer_metrics_test.go:30, so I did not duplicate it.

@D3Hunter D3Hunter left a comment

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.

rest lgtm

@expxiaoli

Copy link
Copy Markdown
Author

Additional review finding

🟠 [Major] The original alerting issue is only partially fixed

The production change correctly restores the syncer file and position gauges from the persisted checkpoint. It changes the reported example from 347650 - 0 = 347650 to the real file gap, 347650 - 346652 = 998.

However, dm/metrics/alertmanager/dm_worker.rules.yml:128 still evaluates every exported syncer metric without checking task state. Because 998 > 1, an intentionally paused task can still trigger DM_binlog_file_gap_between_master_syncer after this PR. This fixes the misleading zero-based inflation, but it does not satisfy issue #12765's first expected behavior: paused or stopped tasks should not trigger the gap alert.

Please treat this as a partial fix and keep #12765 open until the alert is restricted to running tasks, for example by filtering or joining with dm_worker_task_state == 2. The join labels should preserve task and source identity so multi-source tasks do not produce ambiguous matches.

The checkpoint-to-metric lifecycle test gap is already covered by the existing inline comment at dm/syncer/syncer_metrics_test.go:30, so I did not duplicate it.

Additional review finding

🟠 [Major] The original alerting issue is only partially fixed

The production change correctly restores the syncer file and position gauges from the persisted checkpoint. It changes the reported example from 347650 - 0 = 347650 to the real file gap, 347650 - 346652 = 998.

However, dm/metrics/alertmanager/dm_worker.rules.yml:128 still evaluates every exported syncer metric without checking task state. Because 998 > 1, an intentionally paused task can still trigger DM_binlog_file_gap_between_master_syncer after this PR. This fixes the misleading zero-based inflation, but it does not satisfy issue #12765's first expected behavior: paused or stopped tasks should not trigger the gap alert.

Please treat this as a partial fix and keep #12765 open until the alert is restricted to running tasks, for example by filtering or joining with dm_worker_task_state == 2. The join labels should preserve task and source identity so multi-source tasks do not produce ambiguous matches.

The checkpoint-to-metric lifecycle test gap is already covered by the existing inline comment at dm/syncer/syncer_metrics_test.go:30, so I did not duplicate it.

filtering dm_worker_task_state == 2 fix is covered by #12766

Rename the checkpoint metric helper to reflect its update semantics and add lifecycle coverage for Init and Run call sites.
@ti-chi-bot ti-chi-bot Bot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Jul 31, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

@expxiaoli: The following test 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-dm-integration-test 3998b80 link true /test pull-dm-integration-test

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

area/dm Issues or PRs related to DM. do-not-merge/needs-triage-completed first-time-contributor Indicates that the PR was contributed by an external member and is a first-time contributor. release-note Denotes a PR that will be considered when it comes time to generate release notes. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants