Skip to content

<fix>[host]: ZSTAC-85091 keep in-flight migrate dest mem reserve on recalc#4365

Open
zstack-robot-2 wants to merge 4 commits into
5.5.28from
sync/jin.ma/fix/ZSTAC-85091
Open

<fix>[host]: ZSTAC-85091 keep in-flight migrate dest mem reserve on recalc#4365
zstack-robot-2 wants to merge 4 commits into
5.5.28from
sync/jin.ma/fix/ZSTAC-85091

Conversation

@zstack-robot-2

Copy link
Copy Markdown
Collaborator

Summary

热迁移期间周期任务 RecalculateHostCapacity 重算可用内存时,把仍在 Migrating(hostUuid=源)VM 的目标机预留内存擦除,调度器叠加双订导致大内存 VM 触发宿主机 OOM。现场:available before 158G → now 364G,多算回 ~206G 预留。

Root Cause

重算 available = total − 已落库 Running VM 内存,未计入热迁移 in-flight VM 在目标机的预留(VM 状态未切到该 host)。

Fix

重算抬高某 host available 前,经 VmSchedHistory.destHostUuid 判定该 host 是否为某 Migrating VM 的迁移目标;是则不抬高(保留预留)。仅冻结迁移目标机,其余 host 正常回收,迁移结束后恢复。

Changes

  • HostAllocatorManagerImpl: isMigrationDestHost(hostUuid) 精确过滤
  • IT: RecalculateHostCapacityKeepInflightReserveCase(目标机冻结/无关机恢复/迁移结束恢复 三断言)

Testing

  • compute 编译通过
  • IT RED/GREEN:宿主机 JDK8 forked-VM 退出崩溃无法坐实,逻辑已 review;待 Docker/CI 补跑
  • CI pipeline

Resolves: ZSTAC-85091

sync from gitlab !10325

RecalculateHostCapacity reset available memory to total minus landed
Running VMs, erasing the destination reservation of VMs still mid
live-migration (hostUuid=source). Scheduler then double-booked freed
memory and OOM'd big VMs. Guard recalc to never raise a host's available
memory while any VM is Migrating. Test: RecalculateHostCapacityKeepInflightReserveCase.

Resolves: ZSTAC-85091

Change-Id: I37fde9329dffb3e44e03b6b251c9f73145713bbb
Scope the recalc freeze to hosts that are migration dest via sched
history, not all hosts; restore available once VM lands. Test adds
recovery and unrelated-host assertions.

Resolves: ZSTAC-85091

Change-Id: I37fde9329dffb3e44e03b6b251c9f73145713bbb
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 37 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0d9db23f-fccb-40a0-8005-6c74c154046c

📥 Commits

Reviewing files that changed from the base of the PR and between 780d53c and 571138c.

📒 Files selected for processing (1)
  • test/src/test/groovy/org/zstack/test/integration/kvm/capacity/RecalculateHostCapacityKeepInflightReserveCase.groovy

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Could not fetch remote config from http://open.zstack.ai:20001/code-reviews/zstack-cloud.yaml: TimeoutError: The operation was aborted due to timeout
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

HostAllocatorManagerImpl 中新增迁移目的主机判断逻辑,并在主机可用内存重算时对该类主机进行回钳;同时新增集成测试验证迁移中与迁移完成后的容量重算结果。

Changes

迁移目的主机容量回钳

Layer / File(s) Summary
迁移目的主机判断与容量回钳
compute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.java
补充 VmInstanceVO/VmSchedHistoryVO 相关导入;新增 isMigrationDestHost(hostUuid) 方法,查询处于 Migrating 状态的 VM 并通过 VmSchedHistoryVO 判断目的主机;在 HostCapacityUpdaterRunnable.call 中新增分支,当目标主机为迁移目的地且重算可用内存高于重算前值时,将 avail 回钳为 before
集成测试:重算保留 inflight reserve
test/src/test/groovy/org/zstack/test/integration/kvm/capacity/RecalculateHostCapacityKeepInflightReserveCase.groovy
新增 RecalculateHostCapacityKeepInflightReserveCase,构造两台 KVM 主机环境,创建 VM 并将状态置为 Migrating,分别模拟目标主机和无关主机的 inflight reserve;断言迁移进行中时目标主机可用内存不被抬升、无关主机恢复真实可用内存;VM 变为 Running 后再次断言目标主机恢复基准值。

估计代码审查工作量

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 小兔跳过迁移路,
内存回钳稳如故。
目的主机莫抬升,
重算之时需自律。
inflight reserve 不丢失,
容量守护乐无虞!

🚥 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 标题准确概括了重算时保留迁移目标机预留内存这一主要改动。
Description check ✅ Passed 描述与本次修改高度相关,清楚说明了问题、原因、修复和测试内容。
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
  • Commit unit tests in branch sync/jin.ma/fix/ZSTAC-85091

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: 2

🧹 Nitpick comments (1)
compute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.java (1)

254-260: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

把迁移目的地主机集合提前一次性查出来。

isMigrationDestHost() 现在在每个 host 的更新回调里都会重新查询一遍全部 Migrating VM 和调度历史。RecalculateHostCapacityMsg 支持 zone/cluster 级批量重算,这会把一次重算放大成按 host 数量线性增长的额外 DB 往返。建议在进入循环前先查出需要冻结的 destHostUuid 集合,再在循环里做 contains 判断。

Also applies to: 287-298

🤖 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
`@compute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.java`
around lines 254 - 260, In HostAllocatorManagerImpl, the host capacity
recalculation loop is calling isMigrationDestHost() for every HostUsedCpuMem
entry, causing repeated queries for Migrating VMs and scheduling history during
batch RecalculateHostCapacityMsg handling. Precompute the destination host UUID
set once before iterating hostUsedCpuMemList, then replace the per-host
isMigrationDestHost() call with a simple contains check inside the
HostCapacityUpdater callback to avoid linear DB overhead.
🤖 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
`@compute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.java`:
- Around line 287-298: 当前 isMigrationDestHost 通过 VmSchedHistoryVO 的 isExists()
只要命中任意历史记录就会把 host 误判为当前迁移目的主机。请在 HostAllocatorManagerImpl 的 isMigrationDestHost
中改为只根据每个 Migrating VM 的最新调度历史,或限定到当前迁移 attempt 对应的 VmSchedHistoryVO 记录,再判断
destHostUuid 是否等于目标 hostUuid,避免旧迁移记录持续命中导致误冻结。

In
`@test/src/test/groovy/org/zstack/test/integration/kvm/capacity/RecalculateHostCapacityKeepInflightReserveCase.groovy`:
- Around line 111-119: The VM migration test in
RecalculateHostCapacityKeepInflightReserveCase is relying on whatever host the
scheduler picks, so the assertions against host capacity can be wrong. Fix the
setup in the VM creation flow and the simulated migration completion so the
source host is explicitly controlled, and update VmInstanceVO.hostUuid to
destHost.uuid when marking the VM back to Running. This keeps the reserve
recalculation logic aligned with the intended host and makes the later
assertions deterministic.

---

Nitpick comments:
In
`@compute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.java`:
- Around line 254-260: In HostAllocatorManagerImpl, the host capacity
recalculation loop is calling isMigrationDestHost() for every HostUsedCpuMem
entry, causing repeated queries for Migrating VMs and scheduling history during
batch RecalculateHostCapacityMsg handling. Precompute the destination host UUID
set once before iterating hostUsedCpuMemList, then replace the per-host
isMigrationDestHost() call with a simple contains check inside the
HostCapacityUpdater callback to avoid linear DB overhead.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2578603b-aa9a-4381-b1d7-e02e2debcce2

📥 Commits

Reviewing files that changed from the base of the PR and between 430ea54 and a1ec5e8.

📒 Files selected for processing (2)
  • compute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.java
  • test/src/test/groovy/org/zstack/test/integration/kvm/capacity/RecalculateHostCapacityKeepInflightReserveCase.groovy

Comment on lines +287 to +298
private boolean isMigrationDestHost(String hostUuid) {
List<String> migratingVmUuids = Q.New(VmInstanceVO.class)
.eq(VmInstanceVO_.state, VmInstanceState.Migrating)
.select(VmInstanceVO_.uuid)
.listValues();
if (migratingVmUuids.isEmpty()) {
return false;
}
return Q.New(VmSchedHistoryVO.class)
.in(VmSchedHistoryVO_.vmInstanceUuid, migratingVmUuids)
.eq(VmSchedHistoryVO_.destHostUuid, hostUuid)
.isExists();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

不要用“任意历史记录存在”来判断当前迁移目的主机。

VmSchedHistoryVO 是历史表,这里的 isExists() 只要命中某个 Migrating VM 过去留下的一条 destHostUuid = hostUuid 记录,就会把该 host 当成当前目的地。VM 发生过多次迁移时,旧记录仍会命中,导致当前并非目的地的 host 也被持续冻结,和这次修复“只冻结实际目的主机”的目标相反。这里至少要收敛到该 VM 最新的一条调度历史,或当前迁移 attempt 对应的记录。

🤖 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
`@compute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.java`
around lines 287 - 298, 当前 isMigrationDestHost 通过 VmSchedHistoryVO 的 isExists()
只要命中任意历史记录就会把 host 误判为当前迁移目的主机。请在 HostAllocatorManagerImpl 的 isMigrationDestHost
中改为只根据每个 Migrating VM 的最新调度历史,或限定到当前迁移 attempt 对应的 VmSchedHistoryVO 记录,再判断
destHostUuid 是否等于目标 hostUuid,避免旧迁移记录持续命中导致误冻结。

RecalculateHostCapacityMsg has no reply handler; the IT used bus.call which
hung 5 min on future timeout. Switch to bus.send + retryInSecs polling
assertions. Also fill VmSchedHistoryVO NOT NULL columns (accountUuid,
schedType) that crashed the forked VM.

Resolves: ZSTAC-85091

Change-Id: I37fde9329dffb3e44e03b6b251c9f73145713bbb
Rewrite to write-groovy-test rules: all preconditions via SDK Action
(createVmInstance + migrateVm), no SQL/dbf.persist construction. Intercept
the KVM migrate agent path mid-flight (VM Migrating, dest reserved), trigger
RecalculateHostCapacityMsg (internal periodic task, no SDK Action), assert
dest availableMemory keeps the reservation (56G) instead of rising to total
(64G). Verified: destAvailInflight=destAvailAfterRecalc=60129542144.

Resolves: ZSTAC-85091

Change-Id: I37fde9329dffb3e44e03b6b251c9f73145713bbb
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.

2 participants