Skip to content

downstreamadapter: harden table trigger takeover#5436

Merged
ti-chi-bot[bot] merged 37 commits into
pingcap:masterfrom
hongyunyan:codex/pr-5182-trigger-takeover
Jun 30, 2026
Merged

downstreamadapter: harden table trigger takeover#5436
ti-chi-bot[bot] merged 37 commits into
pingcap:masterfrom
hongyunyan:codex/pr-5182-trigger-takeover

Conversation

@hongyunyan

@hongyunyan hongyunyan commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

What problem does this PR solve?

Issue Number: close #5083

This is PR 3 of 3 split from #5182 and is stacked on PR 2.

Background:

PR 1 persists maintainer epochs before ownership changes. PR 2 fences stale
maintainer control messages at the maintainer and dispatcher-manager boundary.
This final PR hardens the table trigger takeover cases that still depend on
dispatcher-manager bootstrap recovery.

Motivation:

During a same-capture higher-epoch maintainer replacement, a reused dispatcher
manager can still have table trigger event or redo dispatchers from the previous
owner. Replacing a mismatched trigger in place can duplicate DDL ownership, while
dropping stale remove operators during bootstrap can lose the cleanup intent for
an already reported dispatcher.

What is changed and how it works?

This PR adds takeover-specific hardening:

  • Verifies that a reused dispatcher manager either has no table trigger
    dispatcher yet or already has the trigger dispatcher requested by the current
    bootstrap owner.
  • Creates missing event and redo table trigger dispatchers during bootstrap, but
    rejects mismatched trigger IDs instead of replacing them in place.
  • Restores stale remove operators in bootstrap responses only when the same
    bootstrap snapshot reports the dispatcher they remove.
  • Keeps current-epoch bootstrap operators while filtering stale create operators.
  • Adds unit coverage for stale remove restoration, bootstrap snapshot filtering,
    close acknowledgements, closed-epoch tombstones, and table trigger ID mismatch
    handling.
  • Updates the in-flight syncpoint scheduling integration case to search all CDC
    node logs because the merge task may run on any dispatcher-manager owner after
    scheduling.

Stack:

  • #5434: coordinator epoch persistence and owner scheduling foundation.
  • #5435: maintainer and dispatcher-manager receiver-side epoch fence.
  • #5436: table trigger takeover hardening and integration coverage.

Check List

Tests

  • Unit test
  • Integration test

Questions

Will it cause performance regression or break compatibility?

No expected performance regression. The added checks run during maintainer
bootstrap and bootstrap response reconstruction, not on the event write path.

The behavior is compatible with rolling upgrades because it builds on the epoch
0 compatibility rules introduced in the previous PRs.

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

No.

Release note

Fix a bug where delayed stale maintainer requests could create duplicate dispatchers during maintainer failover.

Validation

  • make fmt
  • go test ./downstreamadapter/dispatcherorchestrator ./downstreamadapter/dispatchermanager

Summary by CodeRabbit

  • New Features
    • Added maintainer-epoch checks for maintainer bootstrap updates and redo control message processing.
    • Extended redo control messages to include sender context and maintainer_epoch for tighter stale protection.
  • Bug Fixes
    • Prevented stale maintainer/redo requests from overriding newer dispatcher or redo state, including during bootstrap and close handling.
    • Improved bootstrap/trigger validation and ensured “write path closed” behavior releases fencing without surfacing bootstrap errors.
  • Tests
    • Expanded unit and orchestration coverage for maintainer-epoch gating, stale redo drops, pending-queue precedence, and updated multi-node integration log assertions.

@ti-chi-bot

ti-chi-bot Bot commented Jun 18, 2026

Copy link
Copy Markdown

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@ti-chi-bot ti-chi-bot Bot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. release-note Denotes a PR that will be considered when it comes time to generate release notes. labels Jun 18, 2026
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds maintainer-epoch fields to redo protobuf messages, passes sender metadata through redo/control handlers, and updates bootstrap orchestration to validate maintainer updates and trigger state. Tests and one integration script expand coverage for the new epoch-aware paths.

Changes

Maintainer Epoch Fencing for Redo Messages and Bootstrap

Layer / File(s) Summary
Proto fields and send-side stamping
heartbeatpb/heartbeat.proto, maintainer/maintainer.go
Adds maintainer_epoch to redo meta/resolved-ts messages and populates it on outgoing redo messages.
Maintainer update gate and message plumbing
downstreamadapter/dispatchermanager/dispatcher_manager_info.go, downstreamadapter/dispatchermanager/helper.go, downstreamadapter/dispatchermanager/heartbeat_collector.go
Refactors maintainer update eligibility into CanUpdateMaintainer, adds From node.ID to redo wrapper types, and passes sender IDs through heartbeat collection.
Redo handlers with epoch fencing
downstreamadapter/dispatchermanager/helper.go
Fences redo and control handlers with MaintainerFenceMu, drops stale epochs through shared helpers, and moves cache-event handling after unlock.
Bootstrap orchestration and trigger validation
downstreamadapter/dispatcherorchestrator/dispatcher_orchestrator.go
Uses CanUpdateMaintainer during bootstrap, validates trigger dispatcher IDs, centralizes bootstrap-trigger error handling, fixes fence unlocks on write-path-closed paths, and updates bootstrap operator reporting.
Tests and integration coverage
downstreamadapter/dispatchermanager/helper_test.go, downstreamadapter/dispatcherorchestrator/dispatcher_orchestrator_test.go, tests/integration_tests/in_flight_syncpoint_during_scheduling/run.sh
Adds maintainer-epoch and redo-handler tests, expands bootstrap and close coverage, and broadens the integration log match across CDC nodes.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant DispatcherOrchestrator
  participant DispatcherManager

  Client->>DispatcherOrchestrator: bootstrap request
  DispatcherOrchestrator->>DispatcherManager: CanUpdateMaintainer(from, epoch)
  DispatcherOrchestrator->>DispatcherManager: validate / ensure table triggers
  DispatcherManager-->>DispatcherOrchestrator: accepted or stale/error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • pingcap/ticdc#5434: Maintainer-epoch fields and maintainer lifecycle plumbing are part of the same epoch-aware control path.
  • pingcap/ticdc#5435: Dispatcher-manager maintainer-epoch admission and stale-request handling overlap with the gating changes here.

Suggested labels

lgtm, approved

Suggested reviewers

  • 3AceShowHand
  • asddongmen
  • lidezhu

Poem

🐇 I hopped by epochs, swift and neat,
Old stale whispers lost their feet.
Fresh paws tap the maintainer gate,
And bootstrap checks now match the state.
A burrow bright with fenced-up lanes —
No duplicate hops, no sink-side pains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.31% 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 is concise and accurately summarizes the main change: hardening table trigger takeover in downstreamadapter.
Description check ✅ Passed The PR description matches the required template and includes issue number, change summary, tests, questions, release note, and validation.
Linked Issues check ✅ Passed The changes address #5083 by fencing stale maintainer/bootstrap messages and preventing duplicate dispatchers during failover.
Out of Scope Changes check ✅ Passed The non-test code and the integration log change are directly tied to the takeover hardening scope.
✨ 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.

@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

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors the bootstrap request handling in DispatcherOrchestrator by extracting helper functions to verify and initialize table trigger event and redo dispatchers, and updates operator retrieval to properly handle stale remove operators. It also adds extensive unit tests and updates an integration test to search logs across all CDC nodes. The review feedback highlights a critical mutex leak in handlePostBootstrapRequest due to a missing unlock when fenced, as well as a regression in the new helper functions which fail to gracefully handle write path closed errors during shutdown.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines 481 to 484
if m.fenced.Load() {
manager.MaintainerFenceMu.Unlock()
manager.LocalFence()
return nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

In handlePostBootstrapRequest, if m.fenced.Load() is true, the function returns early without unlocking manager.MaintainerFenceMu. This will cause a mutex leak and potential deadlocks during shutdown or subsequent requests. We should unlock manager.MaintainerFenceMu before calling manager.LocalFence() and returning.

Suggested change
if m.fenced.Load() {
manager.MaintainerFenceMu.Unlock()
manager.LocalFence()
return nil
}
if m.fenced.Load() {
manager.MaintainerFenceMu.Unlock()
manager.LocalFence()
return nil
}

Comment on lines +343 to +347
if err := manager.NewTableTriggerEventDispatcher(id, startTs, false); err != nil {
log.Error("failed to create table trigger event dispatcher",
zap.Stringer("changefeedID", cfId), zap.Error(err))
return err
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

If manager.NewTableTriggerEventDispatcher fails because the write path is closed, it is an expected state during shutdown or fencing. Logging it as an Error and returning the error to the maintainer is a regression from the original behavior (where it was logged as Info and returned nil). We should handle IsWritePathClosedError specially and return nil to allow clean shutdown.

		if err := manager.NewTableTriggerEventDispatcher(id, startTs, false); err != nil {
			if dispatchermanager.IsWritePathClosedError(err) {
				log.Info("dispatcher manager write path closed while creating table trigger event dispatcher",
					zap.Stringer("changefeedID", cfId), zap.Error(err))
				return nil
			}
			log.Error("failed to create table trigger event dispatcher",
				zap.Stringer("changefeedID", cfId), zap.Error(err))
			return err
		}

Comment on lines +375 to +379
if err := manager.NewTableTriggerRedoDispatcher(id, startTs, false); err != nil {
log.Error("failed to create table trigger redo dispatcher",
zap.Stringer("changefeedID", cfId), zap.Error(err))
return err
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Similarly, if manager.NewTableTriggerRedoDispatcher fails because the write path is closed, we should handle IsWritePathClosedError specially and return nil instead of logging an Error and returning the error to the maintainer.

		if err := manager.NewTableTriggerRedoDispatcher(id, startTs, false); err != nil {
			if dispatchermanager.IsWritePathClosedError(err) {
				log.Info("dispatcher manager write path closed while creating table trigger redo dispatcher",
					zap.Stringer("changefeedID", cfId), zap.Error(err))
				return nil
			}
			log.Error("failed to create table trigger redo dispatcher",
				zap.Stringer("changefeedID", cfId), zap.Error(err))
			return err
		}

@hongyunyan hongyunyan marked this pull request as ready for review June 24, 2026 01:52
@ti-chi-bot ti-chi-bot Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jun 24, 2026
@hongyunyan hongyunyan changed the base branch from codex/pr-5182-stale-maintainer-fence to master June 29, 2026 02:33

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
downstreamadapter/dispatcherorchestrator/dispatcher_orchestrator.go (1)

461-463: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep trigger logs as constant messages.

triggerName is internal today, so this is not an injection finding; still, constant messages with structured fields avoid SAST noise and make log queries more stable. As per coding guidelines, “Logs are operational signals; see docs/agents/logging.md before adding, removing, or rewriting logs.”

Proposed refactor
-			log.Error("failed to create "+triggerName,
-				zap.Stringer("changefeedID", cfId), zap.Error(err))
+			log.Error("failed to create table trigger dispatcher",
+				zap.Stringer("changefeedID", cfId),
+				zap.String("triggerName", triggerName),
+				zap.Error(err))
-	log.Error(triggerName+" id mismatch during bootstrap",
+	log.Error("table trigger dispatcher id mismatch during bootstrap",
 		zap.Stringer("changefeedID", cfId),
+		zap.String("triggerName", triggerName),
 		zap.Stringer("expectedDispatcherID", expectedID),
 		zap.Stringer("actualDispatcherID", current.GetId()))
-		log.Info("dispatcher manager write path closed while creating "+triggerName,
-			zap.Stringer("changefeedID", cfId), zap.Error(err))
+		log.Info("dispatcher manager write path closed while creating table trigger dispatcher",
+			zap.Stringer("changefeedID", cfId),
+			zap.String("triggerName", triggerName),
+			zap.Error(err))

Also applies to: 481-484, 497-499

🤖 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 `@downstreamadapter/dispatcherorchestrator/dispatcher_orchestrator.go` around
lines 461 - 463, The trigger creation error logs in DispatcherOrchestrator
should use constant messages instead of concatenating triggerName into the log
text. Update the affected log statements in the create/stop/cleanup paths to
keep a fixed message and move triggerName into structured fields alongside
changefeedID and the error, so the DispatcherOrchestrator logs stay stable and
SAST-friendly.

Sources: Coding guidelines, Linters/SAST tools

downstreamadapter/dispatcherorchestrator/dispatcher_orchestrator_test.go (2)

621-623: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Wait for DispatcherManager shutdown in cleanup.

TryClose(false) is asynchronous here, so these cleanups can return while the manager goroutines are still running against services that the test is already tearing down. Wrap the cleanup in require.Eventually (or equivalent) until TryClose(false) reports closed.

Suggested change
  t.Cleanup(func() {
-		manager.TryClose(false)
+		require.Eventually(t, func() bool {
+			return manager.TryClose(false)
+		}, time.Second, 10*time.Millisecond)
  })

As per coding guidelines, "**/*_test.go: Prefer focused deterministic tests; see docs/agents/testing.md before adding or changing tests."

Also applies to: 690-692, 757-759, 817-819, 865-867

🤖 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 `@downstreamadapter/dispatcherorchestrator/dispatcher_orchestrator_test.go`
around lines 621 - 623, The cleanup in the DispatcherManager tests is returning
before asynchronous shutdown completes, which can leave goroutines running
against torn-down services. Update each affected t.Cleanup that calls
TryClose(false) in dispatcher_orchestrator_test.go to wait until the manager is
actually closed, using require.Eventually or an equivalent polling check against
the DispatcherManager shutdown state. Keep the change localized to the test
helpers/cleanup blocks that reference manager.TryClose(false).

Source: Coding guidelines


775-776: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Bound mock message reads with a timeout.

These raw channel receives will hang the test binary when the response is missing. A small requireMessage helper with select/time.After would make failures deterministic instead of surfacing as package timeouts.

As per coding guidelines, "**/*_test.go: Prefer focused deterministic tests; see docs/agents/testing.md before adding or changing tests."

Also applies to: 788-789, 832-833, 943-945

🤖 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 `@downstreamadapter/dispatcherorchestrator/dispatcher_orchestrator_test.go`
around lines 775 - 776, The raw receives from mc.GetMessageChannel() in
dispatcher_orchestrator_test.go can hang the test when a message is missing.
Update the affected test cases to use a small requireMessage helper that waits
with select and time.After, then unwraps the message for the existing
bootstrapResponseMsg/bootstrapResponse assertions. Apply the same bounded
receive pattern to the other listed channel reads in the dispatcher orchestrator
tests so failures become deterministic instead of package timeouts.

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 `@downstreamadapter/dispatcherorchestrator/dispatcher_orchestrator.go`:
- Around line 367-374: Reject nil-trigger bootstraps in DispatcherOrchestrator
when the reused manager still has a different existing trigger. Update the
nil-id handling in dispatcher_orchestrator.go within the dispatcher_orchestrator
logic so that the nil path checks the manager’s current trigger state before
returning success, instead of blindly allowing ownership transfer. Use the
existing bootstrap/maintainer flow around the nil-id branch and the ownership
transfer path that reaches the trigger handoff logic to ensure a reused manager
only proceeds when it has no trigger or already matches the requested trigger
ID.

---

Nitpick comments:
In `@downstreamadapter/dispatcherorchestrator/dispatcher_orchestrator_test.go`:
- Around line 621-623: The cleanup in the DispatcherManager tests is returning
before asynchronous shutdown completes, which can leave goroutines running
against torn-down services. Update each affected t.Cleanup that calls
TryClose(false) in dispatcher_orchestrator_test.go to wait until the manager is
actually closed, using require.Eventually or an equivalent polling check against
the DispatcherManager shutdown state. Keep the change localized to the test
helpers/cleanup blocks that reference manager.TryClose(false).
- Around line 775-776: The raw receives from mc.GetMessageChannel() in
dispatcher_orchestrator_test.go can hang the test when a message is missing.
Update the affected test cases to use a small requireMessage helper that waits
with select and time.After, then unwraps the message for the existing
bootstrapResponseMsg/bootstrapResponse assertions. Apply the same bounded
receive pattern to the other listed channel reads in the dispatcher orchestrator
tests so failures become deterministic instead of package timeouts.

In `@downstreamadapter/dispatcherorchestrator/dispatcher_orchestrator.go`:
- Around line 461-463: The trigger creation error logs in DispatcherOrchestrator
should use constant messages instead of concatenating triggerName into the log
text. Update the affected log statements in the create/stop/cleanup paths to
keep a fixed message and move triggerName into structured fields alongside
changefeedID and the error, so the DispatcherOrchestrator logs stay stable and
SAST-friendly.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7d6c52a7-c803-41bd-8f9e-3650e5277c16

📥 Commits

Reviewing files that changed from the base of the PR and between a0066dc and da9d721.

⛔ Files ignored due to path filters (1)
  • heartbeatpb/heartbeat.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (9)
  • downstreamadapter/dispatchermanager/dispatcher_manager_info.go
  • downstreamadapter/dispatchermanager/heartbeat_collector.go
  • downstreamadapter/dispatchermanager/helper.go
  • downstreamadapter/dispatchermanager/helper_test.go
  • downstreamadapter/dispatcherorchestrator/dispatcher_orchestrator.go
  • downstreamadapter/dispatcherorchestrator/dispatcher_orchestrator_test.go
  • heartbeatpb/heartbeat.proto
  • maintainer/maintainer.go
  • tests/integration_tests/in_flight_syncpoint_during_scheduling/run.sh

Comment thread downstreamadapter/dispatcherorchestrator/dispatcher_orchestrator.go Outdated

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 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 `@downstreamadapter/dispatcherorchestrator/dispatcher_orchestrator.go`:
- Around line 550-557: The post-bootstrap path in DispatcherOrchestrator is
returning nil before verifying whether the request is current, which can leave
the current maintainer without the expected error response. Update the
post-bootstrap handling in the relevant dispatcher flow to call
IsMaintainerRequestAllowed first, then if the table trigger dispatcher is
missing route the failure through handleDispatcherError instead of returning
nil. Keep using the repository’s predefined error types when constructing or
propagating the missing-trigger error.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7a7069e0-a17a-4cd6-9ff2-399308f36076

📥 Commits

Reviewing files that changed from the base of the PR and between e46e493 and 3c2766f.

📒 Files selected for processing (2)
  • downstreamadapter/dispatchermanager/helper.go
  • downstreamadapter/dispatcherorchestrator/dispatcher_orchestrator.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • downstreamadapter/dispatchermanager/helper.go

Comment thread downstreamadapter/dispatcherorchestrator/dispatcher_orchestrator.go Outdated
@ti-chi-bot ti-chi-bot Bot added needs-1-more-lgtm Indicates a PR needs 1 more LGTM. approved labels Jun 30, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: 3AceShowHand, asddongmen

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

The pull request process is described here

Details Needs approval from an approver in each of these files:
  • OWNERS [3AceShowHand,asddongmen]

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 lgtm and removed needs-1-more-lgtm Indicates a PR needs 1 more LGTM. labels Jun 30, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

[LGTM Timeline notifier]

Timeline:

  • 2026-06-30 03:08:59.476751043 +0000 UTC m=+95281.177130456: ☑️ agreed by 3AceShowHand.
  • 2026-06-30 03:17:12.086529953 +0000 UTC m=+95773.786909367: ☑️ agreed by asddongmen.

@hongyunyan hongyunyan added the hold This pull request is not ready to merge. label Jun 30, 2026
@hongyunyan hongyunyan removed the hold This pull request is not ready to merge. label Jun 30, 2026
@ti-chi-bot ti-chi-bot Bot merged commit c1fd88c into pingcap:master Jun 30, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved lgtm release-note Denotes a PR that will be considered when it comes time to generate release notes. 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.

Duplicate dispatcher can be created during maintainer failover before orphan dispatcher drains

3 participants