Skip to content

<fix>[storage]: exclude deleted volumes from recalculate and sync physical capacity on reconnect#3677

Open
zstack-robot-2 wants to merge 6 commits intomasterfrom
sync/jin.ma/fix/ZSTAC-80937
Open

<fix>[storage]: exclude deleted volumes from recalculate and sync physical capacity on reconnect#3677
zstack-robot-2 wants to merge 6 commits intomasterfrom
sync/jin.ma/fix/ZSTAC-80937

Conversation

@zstack-robot-2
Copy link
Copy Markdown
Collaborator

Root Cause

PrimaryStorageCapacityRecalculator.recalculate() 第78行将 VolumeStatus.Deleted 计入 needCountVolumeStates,Deleted 卷在删除时已归还虚拟容量,但 recalculate() 又将其计入占用,导致双重扣减,存储池分配率异常增长。

次要问题:doConnect() 成功后直接发 RecalculatePrimaryStorageCapacityMsg,未先同步物理容量。

Solution

  1. PrimaryStorageCapacityRecalculator.java: 从 needCountVolumeStates 中去除 VolumeStatus.Deleted
  2. PrimaryStorageBase.java: doConnect() 改用 SyncPrimaryStorageCapacityMsg 替代 RecalculatePrimaryStorageCapacityMsg,确保先同步物理容量再重算

Testing

  • 编译验证通过 (mvn compile -pl storage -am)
  • 代码审查确认改动最小化:2文件6行

Jira

Resolves: ZSTAC-80937

sync from gitlab !9538

…sical capacity on reconnect

Resolves: ZSTAC-80937

Change-Id: I5380a3cb223c675c20418831fffe5c107089b5ea
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 3, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7f8f1fdf-61c9-4987-8823-05dfcb62f60e

📥 Commits

Reviewing files that changed from the base of the PR and between 2d721c3 and e1ecdd3.

⛔ Files ignored due to path filters (1)
  • docs/cache/fix-artifacts/ZSTAC-80937/phase-5.json is excluded by !**/*.json
📒 Files selected for processing (1)
  • storage/src/main/java/org/zstack/storage/volume/VolumeBase.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • storage/src/main/java/org/zstack/storage/volume/VolumeBase.java

Walkthrough

连接成功时发送的容量消息由 RecalculatePrimaryStorageCapacityMsg 改为 SyncPrimaryStorageCapacityMsg;计算已用容量时排除 VolumeStatus.Deleted;对备份存储类型列表做防御性拷贝;同步卷大小时按差值发送增/减容量消息;新增单元与集成测试覆盖上述行为。

Changes

Cohort / File(s) Summary
主存储消息类型更新
storage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.java
连接成功流程中发送容量消息由 RecalculatePrimaryStorageCapacityMsg 改为 SyncPrimaryStorageCapacityMsg(实例化、设置 primaryStorageUuid、以 PrimaryStorageConstant.SERVICE_ID 注册并通过总线发送)。
容量计算过滤条件调整
storage/src/main/java/org/zstack/storage/primary/PrimaryStorageCapacityRecalculator.java
聚合计算已用容量时移除对 VolumeStatus.Deleted 的统计,仅包含 CreatingReady 状态的卷。
备份存储选择器防御性拷贝
storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java
在处理 SelectBackupStorageMsg 时对 selector.getPreferBackupStorageTypes()new ArrayList<>(...),以避免修改插件返回的原始集合。
卷大小同步与容量调整
storage/src/main/java/org/zstack/storage/volume/VolumeBase.java
syncVolumeVolumeSize 中保存 oldSizenewSize,当主存储 UUID 非空且大小变化时,在更新自身记录之前发送 DecreasePrimaryStorageCapacityMsgIncreasePrimaryStorageCapacityMsg(diskSize 为差值)。
单元与集成测试新增
storage/src/test/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageSelectBsTest.java, test/src/test/groovy/.../NfsSyncVolumeSizeCapacityCase.groovy
新增回归与集成测试:验证防御性拷贝不污染插件状态,以及 NFS 场景下同步卷大小后主存储可用容量按差值调整并被持久化。
sequenceDiagram
    participant Client as "请求/调度"
    participant VolumeService as "VolumeBase"
    participant Backend as "PrimaryStorage Backend"
    participant MessageBus as "MessageBus"
    participant PrimaryStorage as "PrimaryStorage(处理容量消息)"
    participant DB as "数据库"
    Note left of VolumeService: syncVolumeVolumeSize 高层流程
    Client->>VolumeService: 触发 syncVolumeSize(volumeUuid)
    VolumeService->>Backend: 查询卷实际 size
    Backend-->>VolumeService: 返回 actualSize (newSize)
    VolumeService->>DB: 读取 VolumeVO(oldSize)
    DB-->>VolumeService: 返回 oldSize
    alt newSize > oldSize
        VolumeService->>MessageBus: 发送 DecreasePrimaryStorageCapacityMsg(diskSize = newSize - oldSize)
    else newSize < oldSize
        VolumeService->>MessageBus: 发送 IncreasePrimaryStorageCapacityMsg(diskSize = oldSize - newSize)
    end
    MessageBus->>PrimaryStorage: 转发容量调整消息
    PrimaryStorage->>DB: 更新 PrimaryStorageCapacityVO.availableCapacity
    DB-->>PrimaryStorage: 持久化完成
    VolumeService->>DB: 更新并持久化 VolumeVO.size = newSize
    DB-->>VolumeService: 持久化完成
Loading

代码审查工作量评估

🎯 4 (Complex) | ⏱️ ~45 分钟

庆祝诗

🐰 新尺跃一跳,容量悄然算,
卷大卷小各有痕,删卷不再添乱,
拷贝护原貌,测试来验真,
小兔鼓掌跳舞,🥕 欢庆今朝晴。

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Title check ✅ Passed 标题准确总结了PR的主要变更:从待计算的卷状态中排除已删除卷,并在重新连接时更改为同步物理容量。
Description check ✅ Passed 描述清晰地解释了根本原因、解决方案和测试验证,与PR的所有关键变更直接相关。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sync/jin.ma/fix/ZSTAC-80937

Comment @coderabbitai help to get the list of available commands and usage tips.

…g handler

Resolves: ZSTAC-80789

Change-Id: Ic9d89a477688248e24ba9099e8ca198cb04c9d92
…during syncVolumeSize

Resolves: ZSTAC-80937

Change-Id: Idb146c176e50dd3748ae408c0ae1dab5a999935c
storage

Resolves: ZSTAC-80937

Change-Id: Ie3a540bd1091e143c62dc4a3cc0c9557cc27e078
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In
`@storage/src/test/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageSelectBsTest.java`:
- Line 47: Rename the test method testDefensiveCopyPreventsBeaMutation to
testDefensiveCopyPreventsBeanMutation to correct the typo; update the method
declaration in ExternalPrimaryStorageSelectBsTest (and any internal references
or reflective usages in the same file) so all occurrences match the new name and
the test continues to run under the framework.
🪄 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: 0d869a78-1918-47a5-80f8-2e95cbd964f0

📥 Commits

Reviewing files that changed from the base of the PR and between d3137fd and 7da801e.

⛔ Files ignored due to path filters (1)
  • storage/pom.xml is excluded by !**/*.xml
📒 Files selected for processing (4)
  • storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeBase.java
  • storage/src/test/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageSelectBsTest.java
  • test/src/test/groovy/org/zstack/test/integration/storage/primary/nfs/capacity/NfsSyncVolumeSizeCapacityCase.groovy
✅ Files skipped from review due to trivial changes (1)
  • storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java

}

@Test
public void testDefensiveCopyPreventsBeaMutation() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

测试方法名有拼写错误,建议改为 Bean

Line [47] 的 testDefensiveCopyPreventsBeaMutationBea 应为 Bean,建议修正以提升可读性和检索一致性。

✏️ 建议修改
-    public void testDefensiveCopyPreventsBeaMutation() {
+    public void testDefensiveCopyPreventsBeanMutation() {

As per coding guidelines, “命名应尽量用完整的单词组合表达意图”。

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void testDefensiveCopyPreventsBeaMutation() {
public void testDefensiveCopyPreventsBeanMutation() {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@storage/src/test/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageSelectBsTest.java`
at line 47, Rename the test method testDefensiveCopyPreventsBeaMutation to
testDefensiveCopyPreventsBeanMutation to correct the typo; update the method
declaration in ExternalPrimaryStorageSelectBsTest (and any internal references
or reflective usages in the same file) so all occurrences match the new name and
the test continues to run under the framework.

MaJin1996 and others added 2 commits April 16, 2026 11:37
Resolves: ZSTAC-80937

Change-Id: I7566666e79706c6c66726f616d706965687a7474
Resolves: ZSTAC-80937

Change-Id: I7566666e79706c6c66726f616d706965687a7474

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
storage/src/main/java/org/zstack/storage/volume/VolumeBase.java (1)

2172-2215: ⚠️ Potential issue | 🔴 Critical

避免并发 syncVolumeSize 把同一笔容量变化重复结算。

Line 2172 先缓存了 oldSize,但这条路径本身没有像其他 volume 变更一样走 syncThreadId 串行化。这样两个并发的 sync 都可能拿到同一个旧值,并在 Line 2200 各自发送一次容量增减消息,最终把一次 size 变化重复记到主存储 availableCapacity 上。

建议把 syncVolumeVolumeSize() 的落库和容量调整放进按 volume 串行的 task 里,或者至少基于数据库当前 size 做原子 compare-and-update 后再发送 delta 消息。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@storage/src/main/java/org/zstack/storage/volume/VolumeBase.java` around lines
2172 - 2215, Concurrent syncVolume operations read the cached oldSize and can
both send duplicate primary-storage capacity deltas; in VolumeBase (the
syncVolumeSize/syncVolumeVolumeSize path around refreshVO(), self.setSize(), and
the DecreasePrimaryStorageCapacityMsg/IncreasePrimaryStorageCapacityMsg sends)
either (A) move the DB write of the new size and the logic that sends the
capacity adjustment into the volume-level serialized task (use the same
syncThreadId/serialization used by other volume updates), or (B) perform an
atomic compare-and-update against the database current size before sending a
delta: read current size from DB, compute sizeDiff = newSize - currentDbSize, do
an UPDATE ... WHERE uuid=? AND size=? to CAS the size, and only if the update
affected a row send the corresponding
DecreasePrimaryStorageCapacityMsg/IncreasePrimaryStorageCapacityMsg; apply this
change around the code that currently caches oldSize and sends the bus messages
to avoid double-counting.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@storage/src/main/java/org/zstack/storage/volume/VolumeBase.java`:
- Around line 2172-2215: Concurrent syncVolume operations read the cached
oldSize and can both send duplicate primary-storage capacity deltas; in
VolumeBase (the syncVolumeSize/syncVolumeVolumeSize path around refreshVO(),
self.setSize(), and the
DecreasePrimaryStorageCapacityMsg/IncreasePrimaryStorageCapacityMsg sends)
either (A) move the DB write of the new size and the logic that sends the
capacity adjustment into the volume-level serialized task (use the same
syncThreadId/serialization used by other volume updates), or (B) perform an
atomic compare-and-update against the database current size before sending a
delta: read current size from DB, compute sizeDiff = newSize - currentDbSize, do
an UPDATE ... WHERE uuid=? AND size=? to CAS the size, and only if the update
affected a row send the corresponding
DecreasePrimaryStorageCapacityMsg/IncreasePrimaryStorageCapacityMsg; apply this
change around the code that currently caches oldSize and sends the bus messages
to avoid double-counting.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 44baf68a-ae08-4124-9811-21082b930e7f

📥 Commits

Reviewing files that changed from the base of the PR and between 7da801e and 2d721c3.

⛔ Files ignored due to path filters (1)
  • docs/cache/fix-artifacts/ZSTAC-80937/phase-5.json is excluded by !**/*.json
📒 Files selected for processing (1)
  • storage/src/main/java/org/zstack/storage/volume/VolumeBase.java

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