Skip to content

feat: support concurrent patch rollout - #37

Open
wu8685 wants to merge 7 commits into
branch-cls-mdsfrom
xset-map-fields
Open

wu8685 wants to merge 7 commits into
branch-cls-mdsfrom
xset-map-fields

Conversation

@wu8685

@wu8685 wu8685 commented Oct 28, 2026 •

Copy link
Copy Markdown

Summary

  • add schema-aware patch capture, conflict detection, rollback and compaction
  • support independent ByPartition and ByLabel rollout, including new-instance policy
  • add safe mode exit, emergency feature-gate freeze, per-patch conditions, metrics and events
  • support asynchronous active-patch withdrawal without losing independent lanes
  • cover map/list corner cases and restart recovery

Replacement and observation follow-up

  • plan and count create-before-delete replacement pairs as one logical replica
  • keep each physical member's applied patch membership separate in a versioned observation snapshot
  • require the correct desired replacement to be Available before deleting the origin
  • preserve ByLabel commands and issued patch intent through recreation and revision-history GC
  • prevent paused rollouts, reclaimed lower IDs and replacement placeholders from shifting partition scope
  • distinguish controller plan compaction from user withdrawal
  • fix readiness sorting when a Ready transition timestamp is absent

Verification

  • go test -race ./... -p 2 -count=1
  • go vet ./...
  • git diff --check
  • focused replacement/restart/rollback/command-conflict/compaction tests

Fixes #38

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅

@wu8685

wu8685 commented Oct 28, 2026

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

CLA Assistant Lite bot: 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 can sign the CLA by just posting a Pull Request Comment same as the below format.

I have read the CLA Document and I hereby sign the CLA

haotian.wk seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.You can retrigger this bot by commenting recheck in this Pull Request

@ColdsteelRail

Copy link
Copy Markdown
Member

Review 基于当前 head e80d210c20fe21ae5ed33e569898dda2fb4b20f2,重点核对新功能与现有 kuperator CollaSet 调谐逻辑的兼容性。建议修复以下两个 P1 问题,并完成 kuperator 下游回归后再合并。

1. [P1] legacy 模式的 Pod 创建失败恢复发生回归

位置:synccontrols/sync_control.go:543-546。

这里把 DecideContextRevisionAfterCreate 的参数从 syncContext.UpdatedRevision 改成了本次尝试创建的 revision,且没有限定在 ConcurrentPatches 模式。

当 ByLabel 或分区扩容仍使用旧模板、创建返回 Invalid / Forbidden 时,原逻辑会将 JustCreate / RecreateUpdate 的 ResourceContext revision 切到最新版本,使已经提交的修复模板能够在后续创建时生效;修改后仍写回失败的旧版本,后续会继续重试旧模板。这个问题会影响未开启新功能的现有 CollaSet。

最小复现实际调用 RealSyncControl.Scale:Context 指向 old,UpdatedRevision 为 fixed,旧模板创建返回 Invalid。相同用例在 base 39f75f2 通过,在本 PR head 失败,Context 仍为 old。

建议保留 legacy 路径原有的恢复语义,仅在新模式下使用实例对应的目标组合版本。

2. [P1] ConcurrentPatches 模式下,版本已更新的实例无法正常结束升级生命周期

位置:synccontrols/x_update.go:185-191。

新分支会排除 IsUpdatedRevision=true 且没有 decoration/PVC 变化的实例,但现有 Update 的收尾阶段仍依赖 targetToUpdateSet 判断是否调用 GetTargetUpdateFinishStatus(sync_control.go:885-906)。

实例原地更新到目标版本后,下一轮 reconcile 会被排除出候选集。若此时已获准执行升级,IsAllowUpdateOps=true,收尾逻辑既不会取消生命周期,也不会检查升级是否完成,因此持续保留 operating 状态;ByLabel 命令清理也会因实例仍处于更新生命周期而受阻。

最小复现实际调用 RealSyncControl.Update:实例已到目标版本,仍处于更新生命周期,且已获准操作。旧模式能够正常清理生命周期,新模式下 operating 状态仍保留。

建议保留正在升级的实例参与收尾判断,或将生命周期收尾范围与实际执行更新的候选集分开。

合并前验证要求:在 kuperator 中验证下游兼容性

请开发者在 kuperator 项目中引入本次 kube-xset 改动,运行完整单元测试、CollaSet 控制器集成测试和真实集群 E2E 测试,并在 PR 中附上 kuperator 提交、kube-xset 依赖版本、执行命令及测试结果。

重点覆盖:

  • 未开启 ConcurrentPatches 的现有 CollaSet,包括扩缩容、ByPartition / ByLabel 发布、原地更新、重建和替换。
  • PodDecoration、PVC、持久实例命名,以及创建失败后切换修复模板的恢复行为。
  • 升级生命周期完整收尾及命令消费。
  • 新模式下多个 Patch 独立推进、暂停、回退、扩容策略、journal compaction 和控制器重启恢复。

仅通过 kube-xset 自身测试不足以证明下游兼容性。PR 中现有的 E2E 用例验证的是纯状态机边界,仍需补充实际控制器和集群层面的验证。

本次审查中:原 PR 的 go test ./... -count=1 全部通过;将 PR 代码接入本地 kuperator 后,编译及命名、标签相关单测通过。完整 CollaSet 集成回归因本机测试用 kube-apiserver 启动时被系统终止而未能执行,因此尚不能据此确认全量兼容。

@ColdsteelRail

Copy link
Copy Markdown
Member

补充 review(基于 e80d210):进一步核查了模式切换、撤销、journal 清理和先扩后缩时的版本选择,确认一个新的取消/清理边界问题。

[P1] admission 前置 compaction 会静默吞掉撤销请求

位置:patches/journal.go:138-143。

CompactRevisionPlansForCapture 只依据 oldPlan 和旧 status 判断可清理的 Patch,然后直接从 currentPlan 中删除同一 ID,没有先检查本次请求是否修改了该 Patch 的用户字段 rollout。

可复现的时序:

  1. P1 仍在 activePatches 中,partition=0,旧 status 的 selected/updated/ready/available 都已等于 replicas,满足 barrier,但 controller 尚未清理 journal。
  2. 用户仅把 P1 的 rollout.targetState 改为 Absent,模板暂时保持 Head;这本来应由 CaptureUpdate 的显式撤销分支应用 reversePatch。
  3. admission 先调用 CompactRevisionPlansForCapture,把 old/current 两侧的 P1 都删掉。后续 CaptureUpdate 看不到 targetState 变化,将请求作为无变更接受。

本地最小复现结果:请求无错误,返回 Canceled=false、activePatches=[],模板仍为新值。也就是取消请求被成功接受,但实际没有撤销,还丢失了用于回滚的 journal 条目。相同请求在旧 status 尚未达到 barrier 时能正常撤销。

建议先比较本次 rollout 修改,再决定能否 compaction;如果完成的 Patch 不允许撤销,也应明确拒绝请求,要求用户按新的 template 变更发起回滚,不能静默忽略。请补充 CompactRevisionPlansForCapture -> CaptureUpdate 连续调用的回归测试,覆盖 barrier 后立即取消,以及该阶段修改 partition/paused 的处理。

其他核查结论与验证范围

  • 精确反向修改未完成 Patch:现有用例通过,转换为原 Patch 的 Absent 撤销,不创建 P3;撤销期间保留字段占有权,清理后才能重新修改相同字段,这与避免过早释放冲突保护的设计一致。不同 Patch 的 FieldSet 已禁止重叠,因此无需直接删改其他不可变 Patch 的内容。
  • 关闭模式:现有用例覆盖 activePatches 非空时拒绝、journal 为空时允许,以及禁止关闭模式与修改 template 同时发生。开启时 legacy rollout 已稳定的前提,仍需由实际 workload 接入层提供校验和测试证据;这次未将接入层尚未核实的行为作为已确认缺陷。
  • 替换版本:新增最小用例覆盖 Base、P1、P2、P1+P2 四种组合,以及重启后重建虚拟 revision,再调用普通替换/替换升级的版本选择逻辑,均通过。这只证明版本选择子路径;完整先扩后缩、替换中推进/撤销 Patch、运维生命周期收尾,仍需实际控制器 E2E,不能用纯规划器用例代替。

上述新增问题与前一条评论中的升级生命周期收尾问题是不同的缺陷,请分别修复并补充回归。

@wu8685

wu8685 commented Oct 30, 2026

Copy link
Copy Markdown
Author

已按 review 修复并推送 commit 2253a70。

修复内容:

  1. legacy Scale 创建遇到 Invalid/Forbidden 时,恢复 revision 继续保持原有语义,切到 UpdatedRevision;仅 ConcurrentPatches 使用 identity 对应的组合 revision。新增 RealSyncControl.Scale 回归用例,同时覆盖 legacy 与 ConcurrentPatches。
  2. 将 lifecycle 收尾与实际 update candidate 解耦:实例已经到达目标组合 revision、且 lifecycle 已获准执行时,即使不再需要执行 update,也会继续检查 readiness 并正常 FinishUpdateTarget。新增直接调用 RealSyncControl.Update 的回归用例。
  3. admission compaction 在删除已完成 journal 前检查本次 rollout 修改。barrier 阶段的取消、partition 回退、paused 修改不会再被静默吞掉;新增 CompactRevisionPlansForCapture -> CaptureUpdate 连续调用测试,确认取消会应用 reversePatch 并保留 Absent journal。

验证结果:

  • kube-xset go test ./... -count=1:PASS
  • kube-xset go vet ./...:PASS
  • kuperator 基于最新 origin/main commit 4c92818,通过 local replace 引入本 PR kube-xset:除真实 E2E package 外的全部 unit/controller integration PASS;其中 CollaSet controller 36 specs PASS
  • 本地 kind 集群部署上述 kuperator 镜像,执行 go test ./test/e2e -count=1 -timeout=30m -args -ginkgo.focus="\\[apps\\] CollaSet":PASS,27/27 focused specs,耗时 741.780s,controller restart 0

测试结束后已清理本次新增的 kuperator namespace、webhook、RBAC 和 CollaSet 相关 CRD;保留集群原有 ResourceContext CRD 与 InPlaceSet V2。

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.

3 participants