fix: ignore topology changes that predate a replication task - #52728
Conversation
|
[ci-v2-notice] To rerun ci-v2 checks, comment with:
If you have any questions or requests, please contact @zhikunyao. |
Adversarial review found no issues requiring changes. Verified:
Low — non-blocking
|
…er topology queries on unconfigured clusters Cherry-pick of two upstream fixes onto poc_2.6.17. milvus-io#52728 — a replicator resumes from the checkpoint the target reports, which after `restore secondary` is the position the backup was taken at. Replaying from there walks over topology changes that removed this edge before it was re-created, and those were acted on as if current: the replicator deleted its own replicate pchannel metadata and stopped moments after starting, and the same messages, forwarded to the secondary, turned it back into a standalone primary. The task's initialized checkpoint carries the time tick of the AlterReplicateConfig that created it, so anything at or before it predates the task and is now disregarded, both when deciding to remove the task and when deciding to forward the message. milvus-io#52687 — GetReplicateConfiguration never returned on a cluster that had never been given a replicate configuration, blocking until the caller's deadline. Such a cluster reports a nil configuration, and the watcher treated the resulting nil helper as "no assignment received yet". Wait only until an assignment arrives, return the possibly-nil helper, make the ConfigHelper read accessors nil-receiver safe, and reject replicate messages explicitly on a cluster with no configuration. Includes the unit tests from both upstream PRs; internal/cdc/util had no test file before. Signed-off-by: zhenshan.cao <zhenshan.cao@zilliz.com>
…opology queries on unconfigured clusters (#52729) Cherry-picks onto `poc_2.6.17` for a customer running this branch, so a single image carries everything they need. Two fixes are open upstream against master (#52728, #52687); the other two are already on 2.6 and are brought over because they bear on the same area. ### #52728 — CDC stops itself on a topology change that predates the task A replicator resumes from the checkpoint the target cluster reports, which after `milvus-backup restore secondary` is the position the backup was taken at. Replaying from there walks over every topology change made since — including ones that removed this edge before it was re-created — and those were acted on as if they were current: - the replicator deleted its own `replicating-pchannel/` key and stopped 0.2 s after starting; - the same message, forwarded to the secondary, made it a standalone primary again, dropping its replicate checkpoint. Nothing recovered from it: the topology query still reported the edge (only the per-pchannel task keys were gone), and re-applying the same configuration was short-circuited as unchanged and returned success. `ReplicatePChannelMeta.initialized_checkpoint` already carries the time tick of the `AlterReplicateConfig` that created the task, so a message at or before it predates the task and cannot be an instruction for it. It is now disregarded both when deciding to remove the task and when deciding to forward the message. A task with no initialized time tick keeps the previous behaviour. ### #52687 — topology query on a cluster with no replication blocks `GetReplicateConfiguration` never returned on a cluster that had never been given a replicate configuration — the state of every freshly installed cluster. Such a cluster reports a nil configuration, and the watcher's wait condition treated the resulting nil helper as "no assignment received yet", so the call ran to the caller's deadline and surfaced as `DEADLINE_EXCEEDED`, indistinguishable from an unhealthy cluster. Wait only until an assignment has been received and return the possibly-nil helper; make the `ConfigHelper` read accessors nil-receiver safe; reject replicate messages explicitly on a cluster with no configuration, which previously could not be reached because the lookup blocked first. ### Tests Both upstream PRs' unit tests are included. `internal/cdc/util` had no test file before: - ordering check in both directions, including the no-initialized-time-tick case that keeps the old behaviour; - a current detach still removes the replication, a detach that predates the task does not; - the consume loop neither forwards a stale topology change nor ends on it. ### Also picked from 2.6 - **#51144 — recover pchannels from collection metadata** (`51749e6f8d`). streamingcoord recovery adds WAL topics missing after stats initialization by reading them back from RootCoord collection metadata. Directly relevant here: the customer reinstalled the secondary, and recovery on a rebuilt cluster is exactly this path. - **#51365 — correct the CDC replication lag metric** (`e6b5d9308d`). The replication lag series was wrong, so the one signal an operator would watch to notice replication had stopped could not be trusted. Given the failure this branch fixes is otherwise silent, the metric being correct matters. Both apply cleanly; the only conflict was two tests appended to the same file, resolved by keeping both. `#50849 — ignore stale replicated txn body` was checked and is already present on `poc_2.6.17`. `#51454 — skip unreplicable replicated ddl` is on master only and depends on message-property changes that are not on 2.6; it is left for a regular cherry-pick to 2.6 rather than being adapted here. --------- Signed-off-by: zhenshan.cao <zhenshan.cao@zilliz.com> Signed-off-by: chyezh <chyezh@outlook.com> Signed-off-by: bigsheeper <yihao.dai@zilliz.com> Co-authored-by: chyezh <chyezh@outlook.com> Co-authored-by: bigsheeper <yihao.dai@zilliz.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
/lgtm |
|
[approval-status] effective-owner-approvals=1 [liliu-z(review-comment)]; do-not-merge/disable-approve-self=not-required; do-not-merge/doc-need-two-approve=not-required; ignored=[none] |
|
/ci-rerun-code-check |
|
/ci-rerun-build-ut-cov |
A replicator resumes from the checkpoint reported by the target cluster. After `milvus-backup restore secondary` that checkpoint is the position the backup was taken at, so the replicator replays every topology change made since — including ones that removed this very edge before it was re-created. Those messages were acted on as if they were current. The replicator deleted its own replicate pchannel metadata and stopped 0.2s after starting, and the same messages were forwarded to the secondary, where a configuration that no longer lists it turns it back into a standalone primary. Replication then stopped with no error anywhere: the topology still reported the edge, because the configuration key is untouched and only the per-pchannel task keys are gone, and re-applying the same configuration is short-circuited as unchanged. Ordinary CDC catch-up never exposes this, since the checkpoint stays close to the live position and only new messages are read. The task's initialized checkpoint carries the time tick of the AlterReplicateConfig that created it, so anything at or before it predates the task itself and cannot be an instruction for it. Disregard such messages in both places they are consumed: the removal decision, and the forwarding decision in the consume loop. Tasks written by an older version carry no initialized time tick and keep the previous behaviour. Signed-off-by: Zack <noreply@zilliz.com>
f30715f to
e6f6dbc
Compare
|
Rebased onto current master (no code change, same 4 files / +277 lines). The branch was cut on Aug 20 and its pinned conan recipes (e.g. libavrocpp, fixed on master by #52704 the next day) no longer resolve, which is why the Build stage failed in the reruns. @bigsheeper @liliu-z could you re-apply /lgtm once CI is green? Thanks. |
internal/cdc/util had no test file; the predicate that decides whether an AlterReplicateConfig removes a replication task was uncovered. The existing consume-loop test only asserts that a removal is acted on, never that a message which predates the task is left alone. Add unit tests for both directions of the ordering check, including the case where the task carries no initialized time tick and the previous behaviour is kept, and a consume-loop test asserting a stale topology change is neither forwarded to the secondary nor ends the loop. Signed-off-by: Zack <noreply@zilliz.com> Signed-off-by: zhenshan.cao <zhenshan.cao@zilliz.com>
e6f6dbc to
d68d4b1
Compare
✅ CI Loop Results
|
| Stage | Result | Duration | Tests |
|---|---|---|---|
| ✅ Build | SUCCESS | 10.4min | - |
| ✅ Code-Check | SUCCESS | 5.2min | - |
| ✅ UT-Integration | SUCCESS | 26.8min | - |
| ✅ UT-GO | SUCCESS | 23.1min | - |
| ✅ UT-CPP-Cov | SUCCESS | 39.8min | 8827 total, 8827 passed, 0 failed |
Total: 77min | Pipeline | Artifacts
Overall Coverage: 75.2%
Diff Coverage: Go 100.0% (17 hit, 0 miss, 17 measurable lines, 22 unmeasured)
Diff Coverage HTML: view changed lines
Go Patch Warning: WARNING: Go patch coverage is partial; 22 changed lines were unmeasured.
Total Patch Coverage: 100.0% (17/17 measurable lines, 22 unmeasured)
|
/lgtm |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: liliu-z, xiaofan-luan The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
[approval-status] effective-owner-approvals=2 [xiaofan-luan(comment),liliu-z(review-comment)]; do-not-merge/disable-approve-self=not-required; do-not-merge/doc-need-two-approve=not-required; ignored=[none] |
issue: #52727
Problem
A replicator does not start reading at the live position. It resumes from the checkpoint
the target cluster reports, and after
milvus-backup restore secondarythat checkpoint isthe position the backup was taken at. The replicator therefore replays the source WAL from
far behind, walking over every topology change made since — including ones that removed this
very edge before it was re-created.
IsReplicationRemovedByAlterReplicateConfigMessagedecides purely on the message contents,with no ordering check, so a historical detach is executed as if it had just been issued:
replicating-pchannel/key and stops(
replicate_stream_client_impl.go:337-370), and the same predicate ends the consume loop(
channel_replicator.go:191-199);overwriteAlterReplicateConfigMessagefinds the secondary absent from the configuration and turns it back into a standalone
primary.
In the field this stopped replication 0.2 s after it started, and left a state nothing
recovers from: the topology query still reports the edge, because only the per-pchannel task
keys are gone and the configuration key is untouched, and re-applying the same configuration
is short-circuited as unchanged and returns success.
Ordinary CDC catch-up never exposes this — the checkpoint stays close to the live position
and only new messages are read.
Change
ReplicatePChannelMeta.initialized_checkpointalready carries the time tick of theAlterReplicateConfigthat created the task, so no new field or protocol change is needed.A message at or before it predates the task and cannot be an instruction for it.
util.IsStaleTopologyChangeimplements that comparison.IsReplicationRemovedByAlterReplicateConfigMessagereturns false for such a message, sothe replicator neither deletes its metadata nor ends its consume loop.
flipped back to a standalone primary by a configuration from the past.
and keeps the previous behaviour.
The pchannel-increasing path is unaffected: it deliberately sets the initialized time tick to
one below the creating message, so that message is still delivered, and it carries the edge,
which takes the existing skip path.
Tests
internal/cdc/utilhad no test file at all. Added:TestIsStaleTopologyChange— older than the task, exactly the creating message, newer thanthe task, and the no-initialized-time-tick case.
TestIsReplicationRemovedByAlterReplicateConfigMessage— a current detach still removes thereplication; a current configuration that keeps the edge does not; a detach that predates
the task does not remove it (the regression); the no-initialized-time-tick case still
removes; an ignored message never removes.
TestChannelReplicatorConsumeLoopSkipsStaleTopologyChange— a stale topology change isneither forwarded (the stream client mock has no
Replicateexpectation, so forwardingfails the test) nor ends the consume loop.
The existing
TestChannelReplicatorConsumeLoopDeletesLagSeriesOnRemovalcovers the oppositedirection and still passes: its task carries no initialized checkpoint, so the removal path
is unchanged for it.