Skip to content

<feature>[vm_local_volume_cache]: vm local volume cache imp#3484

Closed
MatheMatrix wants to merge 1 commit into
feature-5.5.6-local-cachefrom
sync/haidong.pang/feature/ZSTAC-81715@@3
Closed

<feature>[vm_local_volume_cache]: vm local volume cache imp#3484
MatheMatrix wants to merge 1 commit into
feature-5.5.6-local-cachefrom
sync/haidong.pang/feature/ZSTAC-81715@@3

Conversation

@MatheMatrix

Copy link
Copy Markdown
Owner

Related: ZSTAC-81715

Change-Id: I6d747a786b73746a6f756a6c72787479726e6765

sync from gitlab !9343

@coderabbitai

coderabbitai Bot commented Mar 13, 2026

Copy link
Copy Markdown

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

新增本地卷缓存子系统与主机块设备查询:引入缓存池/缓存实体、Inventory/VO/SQL、API/消息、KVM 代理命令与常量、VM/卷创建链路的缓存配置传播;将 KVM 挂载流程改为 FlowChain 并新增扩展点与主机块设备查询接口(APIGetBlockDevicesMsg)。

Changes

Cohort / File(s) Summary
主机块设备 API 与消息
header/.../host/APIGetBlockDevicesMsg.java, header/.../host/APIGetBlockDevicesEvent.java, header/.../host/HostBlockDeviceStruct.java, header/.../host/GetBlockDevicesOnHostMsg.java, header/.../host/GetBlockDevicesOnHostReply.java, header/.../host/*Doc_zh_cn.groovy
新增 GET /hosts/{uuid}/block-devices 的 API 消息/事件/结构与中文文档;新增主机端请求/回复消息以从 KVM agent 获取块设备列表。
HostBase API 路由
compute/.../host/HostBase.java
HostBase 增加对 APIGetBlockDevicesMsg 的分支处理,转发 GetBlockDevicesOnHostMsg 并根据回复发布 APIGetBlockDevicesEvent 或错误。
本地卷缓存 DB 与模型
conf/db/upgrade/V5.5.12__schema.sql, header/.../localVolumeCache/*
新增三张升级表及本地卷缓存相关 VO/VO_/Inventory/枚举/状态/容量模型与 CachePoolMetadata(JSON 序列化)。
VM 创建与消息/Spec 扩展(缓存配置传播)
header/.../vm/APICreateVmInstanceMsg.java, header/.../vm/CreateVmInstanceMsg.java, header/.../vm/InstantiateNewCreatedVmInstanceMsg.java, compute/.../vm/InstantiateVmFromNewCreatedStruct.java, header/.../vm/VmInstanceSpec.java, compute/.../vm/VmInstanceBase.java, compute/.../vm/VmInstanceManagerImpl.java, compute/.../vm/VmInstanceUtils.java
在 API、消息、struct 与 VmInstanceSpec/VolumeSpec 中新增根卷与数据盘索引级别的缓存字段,并在 VM 实例化链路中传递这些配置。
主机/卷 Inventory 与 VO 关联
header/.../host/HostVO.java, header/.../host/HostInventory.java, header/.../volume/VolumeAO.java, header/.../volume/VolumeInventory.java
Host 增加 localVolumeCachePools 关联并在 Inventory 中填充;Volume 增加 localVolumeCache 关联、isLocalCached 判定并在 Inventory/VO 转换中传播。
卷分配/创建与系统标签传播
compute/.../vm/VmAllocateHostFlow.java, compute/.../vm/VmAllocateVolumeFlow.java, compute/.../vm/VmInstantiateOtherDiskFlow.java, storage/.../VolumeManagerImpl.java, storage/.../VolumeSystemTags.java
主机分配阶段计算并附加 volumeCache 系统标签;在卷分配/创建时为 VolumeSpec 设置 enableVolumeCache/cachePoolUuid/cacheMode;若启用则创建并持久化 VmLocalVolumeCacheVO;在 attach 流中初始化缓存记录。
KVM 代理命令、常量与 TO/VolumeTO 扩展
plugin/kvm/.../KVMAgentCommands.java, plugin/kvm/.../KVMConstant.java, plugin/kvm/.../CacheTO.java, plugin/kvm/.../VolumeTO.java
在 KVMAgentCommands 中新增本地卷缓存及块设备命令/响应定义,新增 HTTP 路径常量;新增 CacheTO 并在 VolumeTO 中携带 cache 与 size 字段。
KVM 挂载 FlowChain 与扩展点
plugin/kvm/.../KVMAttachVolumeExtensionPoint.java, plugin/kvm/.../KVMExtensionEmitter.java, plugin/kvm/.../KVMHost.java
新增 preBeforeAttachVolume 扩展点;将 attach 流改为 FlowChain:先 preBeforeAttachVolume,再 beforeAttachVolume,执行 HTTP Attach/缓存相关命令,成功回调 afterAttachVolume,失败回调 attachVolumeFailed;KVMHost 新增 GetBlockDevicesOnHostMsg 处理与路径配置。
测试与错误码
testlib/.../ApiHelper.groovy, utils/.../CloudOperationsErrorCode.java
扩展测试辅助方法并大幅新增错误码常量以覆盖本地缓存与相关子系统场景。

Sequence Diagram(s)

sequenceDiagram
    rect rgba(200,220,255,0.5)
    participant Client as Client
    participant API as API Server
    participant VMServ as VM Service
    end
    rect rgba(200,255,200,0.5)
    participant HostAlloc as AllocateHostFlow
    participant VolumeAlloc as AllocateVolumeFlow
    end
    rect rgba(255,220,220,0.5)
    participant KVM as KVM Host/Agent
    participant DB as Database
    end

    Client->>API: APICreateVmInstanceMsg (含缓存配置)
    API->>VMServ: 转发创建请求
    VMServ->>VMServ: 填充 VmInstanceSpec(缓存字段)
    VMServ->>HostAlloc: AllocateHostMsg(addCacheSystemTags)
    HostAlloc->>HostAlloc: 计算 requiredSize / poolUuid 列表
    VMServ->>VolumeAlloc: 为各卷设置 VolumeSpec.cache 配置
    VolumeAlloc->>DB: 创建卷;若启用则持久化 VmLocalVolumeCacheVO
    DB-->>VolumeAlloc: 持久化确认
    VMServ->>KVM: AttachDataVolumeCmd (含 CacheTO)
    KVM->>KVM: extEmitter.preBeforeAttachVolume(...)
    KVM->>KVM: extEmitter.beforeAttachVolume(...)
    KVM->>KVM: HTTP 调用 agent(Attach/AllocateCache 等)
    KVM-->>VMServ: 挂载完成/回复(含 VirtualDeviceInfoList)
    VMServ-->>Client: 返回创建结果(含缓存信息)
Loading

Estimated code review effort

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

Poem

🐰 缓存之池在主机淌,
卷序列上刻新章,
流程分链步步详,
KVM 代理忙相帮,
小兔祝你部署康!

🚥 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标题遵循了指定的格式 [scope]: ,长度为59个字符,符合72字符以内的要求。标题清晰地反映了主要变更内容(虚拟机本地卷缓存功能实现)。
Description check ✅ Passed PR描述包含相关的JIRA工单号、变更ID和源信息,与变更集内容相关(都涉及虚拟机本地卷缓存功能的实现)。尽管描述较为简洁,但与实际变更相关。

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sync/haidong.pang/feature/ZSTAC-81715@@3
📝 Coding Plan
  • Generate coding plan for human review comments

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

@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: 12

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (13)
header/src/main/java/org/zstack/header/localVolumeCache/CachePoolMetadata.java-176-181 (1)

176-181: ⚠️ Potential issue | 🟡 Minor

建议在反序列化前做 trim 判空并保持返回契约稳定。

Line 177 目前仅使用 isEmpty();当输入为仅空白字符时会继续走反序列化路径。建议先 trim() 再判空,并在反序列化结果为 null 时返回空对象,避免调用方处理 null 分支。

🔧 建议修改
 public static CachePoolMetadata fromJson(String json) {
-    if (json == null || json.isEmpty()) {
+    if (json == null || json.trim().isEmpty()) {
         return new CachePoolMetadata();
     }
-    return JSONObjectUtil.toObject(json, CachePoolMetadata.class);
+    CachePoolMetadata metadata = JSONObjectUtil.toObject(json.trim(), CachePoolMetadata.class);
+    return metadata == null ? new CachePoolMetadata() : metadata;
 }

As per coding guidelines “注意检查来自 Message 的参数是否做过 trim,用户可能在浏览器上复制粘贴的数据带有空格、换行符等”.

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

In
`@header/src/main/java/org/zstack/header/localVolumeCache/CachePoolMetadata.java`
around lines 176 - 181, Update CachePoolMetadata.fromJson to trim the input
before emptiness check and ensure a stable non-null return: call trim() on the
json parameter and treat blank strings as empty (return new
CachePoolMetadata()), then call JSONObjectUtil.toObject(json,
CachePoolMetadata.class) and if that returns null return new CachePoolMetadata()
so callers never receive null; reference the fromJson method and
JSONObjectUtil.toObject in your change.
header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsgDoc_zh_cn.groovy-8-10 (1)

8-10: ⚠️ Potential issue | 🟡 Minor

文档元信息仍为占位内容

categorydesc(以及请求 desc)仍是占位/空值,建议在合并前补齐为可对外展示的正式说明。

Also applies to: 20-20

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

In
`@header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsgDoc_zh_cn.groovy`
around lines 8 - 10, 在 APIGetBlockDevicesMsgDoc_zh_cn.groovy
中将占位的元信息替换为正式可对外展示的说明:把 category 的 "未知类别" 改为该 API 所属的实际分类,更新 顶层
desc(三引号内的“在这里填写API描述”)为简明的 API 功能与行为描述,并补充请求字段的
desc(request.desc)为每个参数的用途、类型和必需性说明;检查并统一用语风格与现有文档约定(例如返回值/错误说明)以便合并。
header/src/main/java/org/zstack/header/host/HostBlockDeviceStructDoc_zh_cn.groovy-7-79 (1)

7-79: ⚠️ Potential issue | 🟡 Minor

请移除占位标题并补全字段说明

当前标题仍是占位文本,且各字段 desc 基本为空,发布后会直接影响 API/结构文档可用性。建议至少补齐结构标题与每个字段的语义说明。

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

In
`@header/src/main/java/org/zstack/header/host/HostBlockDeviceStructDoc_zh_cn.groovy`
around lines 7 - 79, The file contains a placeholder title and empty field
descriptions which will break API docs; update the structure title (replace
"在这里输入结构的名称") with a meaningful Chinese title and add concise Chinese
descriptions for each field (wwid, vendor, model, wwn, serial, hctl, type, path,
size, source, transport, targetIdentifier) explaining the semantic meaning and
units/type where applicable (e.g., size in bytes), keeping style consistent with
other *StructDoc_zh_cn.groovy files; ensure each field's desc string is
non-empty and reflects the exact content expected by the HostBlockDeviceStruct.
header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsgDoc_zh_cn.groovy-335-377 (1)

335-377: ⚠️ Potential issue | 🟡 Minor

新增缓存参数缺少说明文本

enableRootVolumeCachecacheModerootVolumeCachePoolUuidrootVolumeCacheModedataDiskCacheConfigOnIndexdesc 为空,建议补齐含义、取值约束和生效条件。

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

In
`@header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsgDoc_zh_cn.groovy`
around lines 335 - 377, 为新增的缓存参数补充描述文本:在 APICreateVmInstanceMsgDoc_zh_cn.groovy
中为字段
enableRootVolumeCache、cacheMode、rootVolumeCachePoolUuid、rootVolumeCacheMode、dataDiskCacheConfigOnIndex
的 desc 属性分别填写含义、可选值/格式约束(例如 cacheMode/rootVolumeCacheMode 支持
"WriteBack"、dataDiskCacheConfigOnIndex 为索引->配置的 Map 格式)、以及生效条件或版本信息(如仅在某些
storage 或版本生效);保持其它属性位置不变并使用与现有文档一致的简短中文句式描述,以便与接口文档统一。
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheStateDoc_zh_cn.groovy-7-7 (1)

7-7: ⚠️ Potential issue | 🟡 Minor

文档标题仍为模板占位符,建议替换为真实结构名。

当前标题会直接进入文档产物,影响可读性与检索。

📝 建议修改
-	title "在这里输入结构的名称"
+	title "VmLocalVolumeCacheState"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheStateDoc_zh_cn.groovy`
at line 7, The document title is still the placeholder "在这里输入结构的名称"; replace it
with the real structure name, e.g., change the title line in
VmLocalVolumeCacheStateDoc_zh_cn.groovy from title "在这里输入结构的名称" to title
"VmLocalVolumeCacheState" (or the appropriate localized name for
VmLocalVolumeCacheState) so the generated docs show the correct structure name.
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStateDoc_zh_cn.groovy-7-7 (1)

7-7: ⚠️ Potential issue | 🟡 Minor

文档标题是占位文本,需改为准确名称。

建议使用结构名,避免生成文档出现模板痕迹。

📝 建议修改
-	title "在这里输入结构的名称"
+	title "VmLocalVolumeCachePoolState"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStateDoc_zh_cn.groovy`
at line 7, The documentation title is a placeholder; open
VmLocalVolumeCachePoolStateDoc_zh_cn.groovy and replace the title string
("在这里输入结构的名称") passed to the title call with the accurate structure name (e.g.,
"VmLocalVolumeCachePoolState") or a localized Chinese equivalent so the
generated docs show the real structure name instead of template text.
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy-10-10 (1)

10-10: ⚠️ Potential issue | 🟡 Minor

标题仍是模板占位符,建议改成真实结构名称。

这会直接影响生成文档的专业性和可读性。

📝 建议修改
-	title "在这里输入结构的名称"
+	title "VmLocalVolumeCachePoolInventory"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy`
at line 10, The title string in VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy
is still the placeholder "在这里输入结构的名称"; replace that literal with a real,
descriptive title for the structure (e.g. include the actual type name
VmLocalVolumeCachePoolInventory or a Chinese localized name like
"VmLocalVolumeCachePoolInventory 结构" / "虚拟机本地卷缓存池清单结构") so the generated
documentation shows a proper, professional heading.
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventoryDoc_zh_cn.groovy-7-62 (1)

7-62: ⚠️ Potential issue | 🟡 Minor

文档内容不完整

多处占位符未填写:

  • Line 9: title 为占位符 "在这里输入结构的名称"
  • Line 25: poolUuid 的 desc 为空
  • Line 31: installPath 的 desc 为空
  • Line 38: cacheMode 的 desc 为 "null"
  • Line 46: state 的 desc 为 "null"

建议补充完整的中文描述以便生成准确的 API 文档。

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

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventoryDoc_zh_cn.groovy`
around lines 7 - 62, Update the documentation in
VmLocalVolumeCacheInventoryDoc_zh_cn.groovy to replace the placeholder title and
fill missing descriptions: change the title string ("在这里输入结构的名称") to the proper
Chinese name for the inventory (e.g., "虚拟机本地云盘缓存信息"), add a meaningful desc for
the field "poolUuid" (e.g., "存储池UUID"), add a desc for "installPath" (e.g.,
"云盘在主机上的安装路径"), and replace the "null" desc values for the refs "cacheMode" and
"state" with clear Chinese descriptions (e.g., "缓存模式" and "缓存状态") while keeping
the existing name, path, type and clz entries unchanged so the API doc generator
can produce complete documentation.
storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java-617-617 (1)

617-617: ⚠️ Potential issue | 🟡 Minor

变量命名不符合规范

变量 CachePoolTag 使用了 PascalCase,应使用 lowerCamelCase 命名风格。根据编码规范:方法名、参数名、成员变量和局部变量应使用 lowerCamelCase 风格。

建议修改
-            String CachePoolTag = msg.getSystemTag(VolumeSystemTags.VOLUME_CACHE_POOL_UUID::isMatch);
+            String cachePoolTag = msg.getSystemTag(VolumeSystemTags.VOLUME_CACHE_POOL_UUID::isMatch);
             if (cacheModeTag != null) {
                 cacheVO.setCacheMode(VmLocalVolumeCacheMode.valueOf(VolumeSystemTags.VOLUME_CACHE_MODE.getTokenByTag(cacheModeTag, VolumeSystemTags.CACHE_MODE_TOKEN)));
             }
-            if (CachePoolTag != null) {
-                cacheVO.setPoolUuid(VolumeSystemTags.VOLUME_CACHE_POOL_UUID.getTokenByTag(CachePoolTag, VolumeSystemTags.CACHE_POOL_UUID_TOKEN));
+            if (cachePoolTag != null) {
+                cacheVO.setPoolUuid(VolumeSystemTags.VOLUME_CACHE_POOL_UUID.getTokenByTag(cachePoolTag, VolumeSystemTags.CACHE_POOL_UUID_TOKEN));
             }
🤖 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/VolumeManagerImpl.java` at
line 617, The local variable CachePoolTag in VolumeManagerImpl should be renamed
to lowerCamelCase (cachePoolTag) to follow naming conventions; update the
declaration that calls
msg.getSystemTag(VolumeSystemTags.VOLUME_CACHE_POOL_UUID::isMatch) and replace
all usages of CachePoolTag within the method (and any nearby code in
VolumeManagerImpl that references it) to cachePoolTag so the code compiles and
adheres to the style guide.
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheModeDoc_zh_cn.groovy-7-14 (1)

7-14: ⚠️ Potential issue | 🟡 Minor

文档占位符未填写

title 仍为占位符 "在这里输入结构的名称",desc 为空。建议补充完整的结构名称和描述,例如:

建议修改
-	title "在这里输入结构的名称"
+	title "虚拟机本地卷缓存模式"

 	field {
 		name "WriteBack"
-		desc ""
+		desc "写回模式"
 		type "VmLocalVolumeCacheMode"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheModeDoc_zh_cn.groovy`
around lines 7 - 14, 当前文档为占位符:title 保持为“在这里输入结构的名称”且 field.desc 为空;请将 title
更新为该结构的中文名称(例如“虚拟机本地卷缓存模式”或项目统一术语),并为 field 名为 "WriteBack" 的条目补充描述(简要说明
WriteBack 的含义、行为和适用场景),定位参考标识符包括 title、field { name "WriteBack" , type
"VmLocalVolumeCacheMode" , since "5.5.6" },确保描述语言简洁、与现有文档风格一致。
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO.java-28-31 (1)

28-31: ⚠️ Potential issue | 🟡 Minor

这段注释现在是误导性的。

VmLocalVolumeCachePoolVO 位于 header/localVolumeCache,不是 “kvm plugin”。既然这里用注释解释为什么不加 @ForeignKey,请把真实约束写准确,否则后续维护者会按错误前提理解数据关系。

💡 建议修改
     /**
      * Nullable: null means this cache entry has not been assigned to a pool yet.
-     * References VmLocalVolumeCachePoolVO in kvm plugin — no `@ForeignKey` to avoid cross-module dependency.
+     * Intentionally left without a database-level foreign key.
+     * Keep this comment aligned with the real ownership/lifecycle constraint if that design changes.
      */

As per coding guidelines “对于较长的注释,需要仔细校对并随代码更新,确保内容正确。”

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

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO.java`
around lines 28 - 31, 更新 VmLocalVolumeCacheVO 类上关于 VmLocalVolumeCachePoolVO
的注释以反映真实约束:把“References VmLocalVolumeCachePoolVO in kvm plugin — no `@ForeignKey`
to avoid cross-module dependency.” 改为准确说明 VmLocalVolumeCachePoolVO 位于
header/localVolumeCache 模块,并说明不使用 `@ForeignKey` 的真实原因(例如避免循环依赖或模块耦合),确保引用类名
VmLocalVolumeCachePoolVO 和字段含义保持一致以便后续维护者理解数据关系。
header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEventDoc_zh_cn.groovy-8-23 (1)

8-23: ⚠️ Potential issue | 🟡 Minor

把模板占位符替换成真实文档内容。

Line 8 还是模板标题,Lines 13 和 20 的描述分别是 null 和空串。这个文件会直接生成 API 文档,当前内容对外不可用。

💡 建议修改
-	title "在这里输入结构的名称"
+	title "APIGetBlockDevicesEvent"
@@
-		desc "null"
+		desc "块设备列表"
@@
-		desc ""
+		desc "操作是否成功"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEventDoc_zh_cn.groovy`
around lines 8 - 23, Replace the placeholder documentation strings with real
Chinese descriptions: update the title ("在这里输入结构的名称") to a meaningful API event
title, fill ref { name "blockDevices" path
"org.zstack.header.host.APIGetBlockDevicesEvent.blockDevices" desc "null" type
"List" clz HostBlockDeviceStruct.class } by replacing desc "null" with a concise
Chinese description of the blockDevices list and its contents (mention it
contains HostBlockDeviceStruct entries), and update the field { name "success"
desc "" type "boolean" } to provide a clear Chinese description of what success
indicates; ensure wording matches other zh_cn doc conventions and includes
version "since 5.5.6" unchanged.
header/src/main/java/org/zstack/header/volume/VolumeInventory.java-220-222 (1)

220-222: ⚠️ Potential issue | 🟡 Minor

格式问题:if 关键字后缺少空格。

🔧 建议修复
-        if(vo.getLocalVolumeCache() != null) {
+        if (vo.getLocalVolumeCache() != null) {
             inv.setLocalVolumeCache(vo.getLocalVolumeCache().toInventory());
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@header/src/main/java/org/zstack/header/volume/VolumeInventory.java` around
lines 220 - 222, Fix the formatting around the null-check in VolumeInventory:
change the `if` statement that checks vo.getLocalVolumeCache() so there is a
space after the `if` keyword (i.e., use `if (`) and keep the body that calls
inv.setLocalVolumeCache(vo.getLocalVolumeCache().toInventory()); unchanged;
locate the statement in VolumeInventory.java where vo.getLocalVolumeCache() is
checked and adjust spacing to match project style.
🧹 Nitpick comments (8)
utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java (1)

15875-15943: 建议把 LOCALVOLUMECACHE 拆成 LOCAL_VOLUME_CACHE

这批新增常量把多个单词压成了一个片段,和当前仓库对常量“用下划线分词”的约定不一致,可读性也会差一些。既然这些错误码是这次新增的,建议在合入前统一改成 ORG_ZSTACK_STORAGE_LOCAL_VOLUME_CACHE_10001 这类命名,并同步调整字符串值。 As per coding guidelines, "常量命名:全部大写,使用下划线分隔单词。要求表达清楚,避免使用含糊或不准确的名称。"

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

In
`@utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java`
around lines 15875 - 15943, Rename the newly added constants that use
LOCALVOLUMECACHE to follow the repository constant naming convention using
underscores: change each symbol like ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_10001
and its string value "ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_10001" to
ORG_ZSTACK_STORAGE_LOCAL_VOLUME_CACHE_10001 with the matching string
"ORG_ZSTACK_STORAGE_LOCAL_VOLUME_CACHE_10001"; update all occurrences/usages
(references to the constant names and the literal string values) so callers,
tests, and any error-code maps use the new names consistently.
storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java (1)

660-662: 缓存 VO 持久化在事务外执行

cacheVOSQLBatchWithReturn 执行完成后单独持久化,如果 dbf.persist(cacheVO) 失败,卷已经创建但缓存记录未创建,可能导致数据不一致。

建议将 cacheVO 的持久化移入 SQLBatchWithReturn 块内,确保与卷创建在同一事务中:

建议修改
+        VmLocalVolumeCacheVO finalCacheVO = cacheVO;
         vo = new SQLBatchWithReturn<VolumeVO>() {
             `@Override`
             protected VolumeVO scripts() {
                 dbf.getEntityManager().persist(finalVo);
                 dbf.getEntityManager().flush();
                 dbf.getEntityManager().refresh(finalVo);
+                if (finalCacheVO != null) {
+                    dbf.getEntityManager().persist(finalCacheVO);
+                }
                 return finalVo;
             }
         }.execute();
         // ...
-        if (cacheVO != null) {
-            dbf.persist(cacheVO);
-        }
🤖 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/VolumeManagerImpl.java`
around lines 660 - 662, The persist of cacheVO is done outside the
SQLBatchWithReturn transaction (dbf.persist(cacheVO)), which risks inconsistency
if it fails after volume creation; move the dbf.persist(cacheVO) call into the
existing SQLBatchWithReturn block so cacheVO is saved inside the same
transaction as the volume creation (ensure the null check for cacheVO is applied
inside the batch and that any exceptions will roll back both volume and cache
creation together).
header/src/main/java/org/zstack/header/host/HostVO.java (1)

45-48: 考虑 EAGER fetch 对性能的影响

@OneToMany 使用 FetchType.EAGER 会导致每次加载 HostVO 时都同步加载所有关联的 VmLocalVolumeCachePoolVO。如果主机关联的缓存池数量较多,可能影响查询性能。

建议确认是否在所有 HostVO 的使用场景中都需要加载缓存池数据,如不需要可考虑改为 FetchType.LAZY

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

In `@header/src/main/java/org/zstack/header/host/HostVO.java` around lines 45 -
48, The OneToMany mapping on HostVO for localVolumeCachePools currently uses
FetchType.EAGER which can hurt performance; change the annotation on the
localVolumeCachePools field from FetchType.EAGER to FetchType.LAZY in HostVO and
audit call sites that access HostVO.getLocalVolumeCachePools() to ensure they
load the collection within an active transaction (or use explicit fetch-joins in
queries) or adjust those queries to join-fetch VmLocalVolumeCachePoolVO when
pooled data is required.
compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java (2)

35-39: 重复的 import 声明

第 35-38 行已经有了 java.util.ArrayList, HashSet, List, Map 的具体导入,第 39 行又导入了 java.util.*,这是冗余的。

♻️ 建议的修复
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.*;
+import java.util.Objects;
 import java.util.stream.Collectors;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java` around
lines 35 - 39, Remove the redundant wildcard import: the file already explicitly
imports java.util.ArrayList, java.util.HashSet, java.util.List and
java.util.Map, so delete the line importing java.util.* to avoid
duplicate/ambiguous imports (keep the specific imports intact in
VmAllocateHostFlow.java).

173-181: 同样的边界检查问题

getTotalCacheSize 方法类似,此处也应添加对 index 的边界检查,以确保不会越界访问 DataDiskOfferings

第 176 行的 dataDiskCacheConfig 变量声明虽然在此方法中有使用(获取 poolUuid),但应确保访问安全。

🛡️ 建议的修复
         Map<Integer, APICreateVmInstanceMsg.VolumeCacheConfig> dataDiskCacheConfigs = spec.getDataDiskCacheConfigOnIndex();
         if (dataDiskCacheConfigs != null && !dataDiskCacheConfigs.isEmpty()) {
             for (Integer index : dataDiskCacheConfigs.keySet()) {
                 APICreateVmInstanceMsg.VolumeCacheConfig dataDiskCacheConfig = dataDiskCacheConfigs.get(index);
                 if (dataDiskCacheConfig.getCachePoolUuid() != null) {
                     poolUuids.add(dataDiskCacheConfig.getCachePoolUuid());
                 }
             }
         }

这里 index 没有被用于访问其他列表,所以比 getTotalCacheSize 中的问题轻微,但建议审视数据结构设计是否需要使用 index 作为 key。

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

In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java` around
lines 173 - 181, Ensure you validate the integer key "index" against the data
disks list before trusting it: when iterating dataDiskCacheConfigs (variable
dataDiskCacheConfigs) in VmAllocateHostFlow, check that index is >= 0 and <
spec.getDataDiskOfferings().size() (or the appropriate data-disk collection on
the same spec) before any access that would use that index; if out of range,
skip that entry (or handle gracefully) and still allow reading the cachePoolUuid
from APICreateVmInstanceMsg.VolumeCacheConfig when safe, and add a brief log or
comment to explain the skip.
plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java (1)

148-153: 小格式问题: 缺少空格和多余空格

代码逻辑正确,但有两处小的格式问题:

  1. 第 149 行: if (cacheInv != null){{ 前缺少空格
  2. 第 153 行: return to; 有多余的空格
♻️ 建议的修复
         VmLocalVolumeCacheInventory cacheInv = vol.getLocalVolumeCache();
-        if (cacheInv != null){
+        if (cacheInv != null) {
             CacheTO cacheTO = CacheTO.valueOf(cacheInv);
             to.setCache(cacheTO);
         }
-        return  to;
+        return to;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java` around lines 148 -
153, Fix two small formatting issues in VolumeTO.valueOf: add a space before the
opening brace in the null-check "if (cacheInv != null) {", and remove the extra
space in the return statement so it reads "return to;". These changes touch the
block that calls vol.getLocalVolumeCache(), assigns cacheInv, creates
CacheTO.valueOf(cacheInv), and calls to.setCache(cacheTO).
header/src/main/java/org/zstack/header/vm/CreateVmInstanceMsg.java (1)

45-45: 建议解耦内部消息与 API 嵌套 DTO

CreateVmInstanceMsg 直接依赖 APICreateVmInstanceMsg.VolumeCacheConfig,会把 API 层结构耦合进内部消息模型。建议抽出独立的共享类型(如 org.zstack.header.vm.VolumeCacheConfig),降低后续演进风险。

♻️ 建议方向
- private Map<Integer, APICreateVmInstanceMsg.VolumeCacheConfig> dataDiskCacheConfigOnIndex;
+ private Map<Integer, VolumeCacheConfig> dataDiskCacheConfigOnIndex;
@@
- public Map<Integer, APICreateVmInstanceMsg.VolumeCacheConfig> getDataDiskCacheConfigOnIndex() {
+ public Map<Integer, VolumeCacheConfig> getDataDiskCacheConfigOnIndex() {
@@
- public void setDataDiskCacheConfigOnIndex(Map<Integer, APICreateVmInstanceMsg.VolumeCacheConfig> dataDiskCacheConfigOnIndex) {
+ public void setDataDiskCacheConfigOnIndex(Map<Integer, VolumeCacheConfig> dataDiskCacheConfigOnIndex) {

Also applies to: 425-430

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

In `@header/src/main/java/org/zstack/header/vm/CreateVmInstanceMsg.java` at line
45, CreateVmInstanceMsg currently references the API DTO
APICreateVmInstanceMsg.VolumeCacheConfig via the field
dataDiskCacheConfigOnIndex, coupling the internal message to the API layer;
define a new shared type (e.g. org.zstack.header.vm.VolumeCacheConfig) that
mirrors the needed fields and replace all usages of
APICreateVmInstanceMsg.VolumeCacheConfig in CreateVmInstanceMsg (and related
code paths flagged around lines 425-430) with this new VolumeCacheConfig, update
imports/usages accordingly, and ensure any serialization/validation logic is
moved to or delegated through the new shared class so internal messages no
longer depend on the APICreateVmInstanceMsg type.
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java (1)

20-20: metadata 补全泛型类型,降低运行时类型不确定性

当前 LinkedHashMap 为原始类型,建议改为 LinkedHashMap<String, Object>,减少后续取值时的强转风险。

♻️ 建议修改
- private LinkedHashMap metadata;
+ private LinkedHashMap<String, Object> metadata;
@@
- public LinkedHashMap getMetadata() {
+ public LinkedHashMap<String, Object> getMetadata() {
@@
- public void setMetadata(LinkedHashMap metadata) {
+ public void setMetadata(LinkedHashMap<String, Object> metadata) {

Also applies to: 113-119

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

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java`
at line 20, 字段 metadata 在类 VmLocalVolumeCachePoolInventory 中使用了原始类型
LinkedHashMap,存在运行时类型不确定性;将其声明改为 LinkedHashMap<String, Object>,并同步更新相关引用(包含
metadata 的 getter/setter、构造器或任何方法中对 metadata 的赋值/返回处,如类中 113-119
行涉及的方法)以使用相同的泛型签名,确保编译通过并移除不必要的强制类型转换。
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java`:
- Around line 159-166: The loop over dataDiskCacheConfigs may use keys that are
out of range for spec.getDataDiskOfferings() and also declares an unused
variable dataDiskCacheConfig; update the loop in VmAllocateHostFlow so for each
Integer index from dataDiskCacheConfigs.keySet() you first check that index >= 0
and index < spec.getDataDiskOfferings().size() before accessing
spec.getDataDiskOfferings().get(index), skip or log invalid indexes, and remove
the unused local APICreateVmInstanceMsg.VolumeCacheConfig dataDiskCacheConfig
variable to eliminate dead code while still accumulating totalCacheSize.

In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateVolumeFlow.java`:
- Around line 132-138: In VmAllocateVolumeFlow where you add volume cache tags
(the block using vspec.getEnableVolumeCache()), avoid persisting a "null"
cacheMode tag: only add the "volumeCache::cacheMode::<value>" tag if
vspec.getCacheMode() is non-null/non-empty (similar to the existing null check
for vspec.getCachePoolUuid()); update the logic around vspec.getCacheMode() so
it conditionally appends that tag (or omits it) to prevent writing
"volumeCache::cacheMode::null".
- Around line 76-88: In VmAllocateVolumeFlow, the call to
spec.getDataDiskCacheConfigOnIndex().isEmpty() can NPE when
getDataDiskCacheConfigOnIndex() is null; update the conditional to null-safe
check (e.g., use CollectionUtils.isEmpty(spec.getDataDiskCacheConfigOnIndex())
or check spec.getDataDiskCacheConfigOnIndex() != null &&
!spec.getDataDiskCacheConfigOnIndex().isEmpty()) before iterating, keeping the
rest of the logic that updates dataVolumeSpecs (dataVolumeSpecs,
setEnableVolumeCache, setCachePoolUuid, setCacheMode) unchanged.
- Around line 106-108: In VmAllocateVolumeFlow update the call that passes
spec.getEnableRootVolumeCache() into vspec.setEnableVolumeCache() to guard
against Boolean null-unboxing: explicitly normalize the boxed Boolean from
spec.getEnableRootVolumeCache() to a primitive (e.g. treat null as false) before
calling vspec.setEnableVolumeCache(), leaving setCachePoolUuid(...) and
setCacheMode(...) unchanged.

In `@compute/src/main/java/org/zstack/compute/vm/VmInstantiateOtherDiskFlow.java`:
- Around line 65-80: initCacheRecord() creates a VmLocalVolumeCacheVO and
persists it inside a NoRollbackFlow without tracking whether it was newly
created; if later steps fail the persisted cache can become an orphan. Fix:
record the created cache UUID (e.g. createdCacheRecordUuid) when
initCacheRecord() persists a new VmLocalVolumeCacheVO, and in the flow's
rollback(...) handler check that flag and remove the persisted
VmLocalVolumeCacheVO via
dbf.remove(Q.New(VmLocalVolumeCacheVO.class).eq(VmLocalVolumeCacheVO_.uuid,
createdCacheRecordUuid).find()) to ensure cleanup; apply the same pattern for
the other occurrence mentioned (lines 421-436).

In `@header/src/main/java/org/zstack/header/host/HostBlockDeviceStruct.java`:
- Around line 24-40: The equals/hashCode contract is violated in
HostBlockDeviceStruct because equals uses conditional logic on wwid while
hashCode uses wwid when non-null; make both methods use the same fields by
comparing all three fields (wwid, path, hctl) consistently: change
equals(Object) to return Objects.equals(wwid, that.wwid) && Objects.equals(path,
that.path) && Objects.equals(hctl, that.hctl) and change hashCode() to return
Objects.hash(wwid, path, hctl) so equality and hash code are computed from the
identical set of fields.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java`:
- Around line 22-36: The `@Queryable` annotations on
VmLocalVolumeCachePoolInventory incorrectly set referencedColumnName to capacity
fields (totalCapacity, availableCapacity, allocated, dirty) when joining to
VmLocalVolumeCachePoolCapacityInventory; update each joinColumn in
VmLocalVolumeCachePoolInventory for the fields totalCapacity, availableCapacity,
allocated, and dirty so referencedColumnName = "uuid" (i.e., join by the
capacity inventory primary key) to restore correct JPA association and ensure
queries work.

In `@header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java`:
- Around line 241-243: The disk-level cacheMode fields in APICreateVmInstanceMsg
(the per-disk cacheMode fields near the enableCache/cachePoolUuid declarations
and the other disk-related cacheMode in the same class) lack the same
allowed-value constraint as the top-level cacheMode (which only allows
"WriteBack"); add the same validation so illegal values are rejected
early—either apply the same annotation used on the top-level cacheMode field
(e.g. the `@AllowedValues/`@APIParam constraint) to the per-disk cacheMode fields
or add a validation check in the class validation routine (e.g. validate() or a
`@PostConstruct` validator) that enforces cacheMode equals "WriteBack" for those
fields and returns a clear validation error if not.

In `@plugin/kvm/src/main/java/org/zstack/kvm/CacheTO.java`:
- Around line 43-50: The valueOf method in CacheTO risks a NPE when
VmLocalVolumeCacheInventory.getCacheMode() is null; update CacheTO.valueOf to
null-check inv.getCacheMode() before calling name() (or use Optional-like logic)
and setCacheMode to either null or a safe default string when cache mode is null
so inv.getCacheMode().name() is never invoked on a null reference.

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMAttachVolumeExtensionPoint.java`:
- Line 15: The new abstract method preBeforeAttachVolume on interface
KVMAttachVolumeExtensionPoint breaks backward compatibility; change it to a Java
default method by providing a default no-op implementation in the interface
(retain the exact signature: preBeforeAttachVolume(KVMHostInventory host,
VmInstanceInventory vm, VolumeInventory volume, AttachDataVolumeCmd cmd, Map
data, Completion completion)) so existing implementors do not need to change;
add the default keyword and an empty body that simply calls completion.success()
or otherwise short-circuits appropriately to preserve existing behavior.

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMExtensionEmitter.java`:
- Around line 395-399: preBeforeAttachVolume currently passes the same
Completion to every KVMAttachVolumeExtensionPoint in attachVolumeExts causing
multiple concurrent callbacks; change it to execute extensions serially in a
chain: iterate attachVolumeExts by index (or iterator) and call
ext.preBeforeAttachVolume with a new Completion wrapper that on success advances
to the next extension and on failure immediately calls the original
completion.fail; when the last extension succeeds call the original
completion.success; also wrap each ext call in a try/catch to call original
completion.fail on unexpected exceptions so the original completion is invoked
exactly once.

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java`:
- Around line 3710-3712: The code creates a FlowChain with
FlowChainBuilder.newSimpleFlowChain() and then instantiates a ShareFlow, but
ShareFlow requires a ShareFlowChain produced by
FlowChainBuilder.newShareFlowChain(); replace
FlowChainBuilder.newSimpleFlowChain() with FlowChainBuilder.newShareFlowChain()
(keeping the chain.setName(String.format("attach-volume-%s",
msg.getInventory().getUuid())) and the subsequent new ShareFlow() block) so the
ShareFlow gets the proper ShareFlowChain implementation and its
flow()/done()/error() callbacks are wired correctly.

---

Minor comments:
In
`@header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEventDoc_zh_cn.groovy`:
- Around line 8-23: Replace the placeholder documentation strings with real
Chinese descriptions: update the title ("在这里输入结构的名称") to a meaningful API event
title, fill ref { name "blockDevices" path
"org.zstack.header.host.APIGetBlockDevicesEvent.blockDevices" desc "null" type
"List" clz HostBlockDeviceStruct.class } by replacing desc "null" with a concise
Chinese description of the blockDevices list and its contents (mention it
contains HostBlockDeviceStruct entries), and update the field { name "success"
desc "" type "boolean" } to provide a clear Chinese description of what success
indicates; ensure wording matches other zh_cn doc conventions and includes
version "since 5.5.6" unchanged.

In
`@header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsgDoc_zh_cn.groovy`:
- Around line 8-10: 在 APIGetBlockDevicesMsgDoc_zh_cn.groovy
中将占位的元信息替换为正式可对外展示的说明:把 category 的 "未知类别" 改为该 API 所属的实际分类,更新 顶层
desc(三引号内的“在这里填写API描述”)为简明的 API 功能与行为描述,并补充请求字段的
desc(request.desc)为每个参数的用途、类型和必需性说明;检查并统一用语风格与现有文档约定(例如返回值/错误说明)以便合并。

In
`@header/src/main/java/org/zstack/header/host/HostBlockDeviceStructDoc_zh_cn.groovy`:
- Around line 7-79: The file contains a placeholder title and empty field
descriptions which will break API docs; update the structure title (replace
"在这里输入结构的名称") with a meaningful Chinese title and add concise Chinese
descriptions for each field (wwid, vendor, model, wwn, serial, hctl, type, path,
size, source, transport, targetIdentifier) explaining the semantic meaning and
units/type where applicable (e.g., size in bytes), keeping style consistent with
other *StructDoc_zh_cn.groovy files; ensure each field's desc string is
non-empty and reflects the exact content expected by the HostBlockDeviceStruct.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/CachePoolMetadata.java`:
- Around line 176-181: Update CachePoolMetadata.fromJson to trim the input
before emptiness check and ensure a stable non-null return: call trim() on the
json parameter and treat blank strings as empty (return new
CachePoolMetadata()), then call JSONObjectUtil.toObject(json,
CachePoolMetadata.class) and if that returns null return new CachePoolMetadata()
so callers never receive null; reference the fromJson method and
JSONObjectUtil.toObject in your change.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventoryDoc_zh_cn.groovy`:
- Around line 7-62: Update the documentation in
VmLocalVolumeCacheInventoryDoc_zh_cn.groovy to replace the placeholder title and
fill missing descriptions: change the title string ("在这里输入结构的名称") to the proper
Chinese name for the inventory (e.g., "虚拟机本地云盘缓存信息"), add a meaningful desc for
the field "poolUuid" (e.g., "存储池UUID"), add a desc for "installPath" (e.g.,
"云盘在主机上的安装路径"), and replace the "null" desc values for the refs "cacheMode" and
"state" with clear Chinese descriptions (e.g., "缓存模式" and "缓存状态") while keeping
the existing name, path, type and clz entries unchanged so the API doc generator
can produce complete documentation.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheModeDoc_zh_cn.groovy`:
- Around line 7-14: 当前文档为占位符:title 保持为“在这里输入结构的名称”且 field.desc 为空;请将 title
更新为该结构的中文名称(例如“虚拟机本地卷缓存模式”或项目统一术语),并为 field 名为 "WriteBack" 的条目补充描述(简要说明
WriteBack 的含义、行为和适用场景),定位参考标识符包括 title、field { name "WriteBack" , type
"VmLocalVolumeCacheMode" , since "5.5.6" },确保描述语言简洁、与现有文档风格一致。

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy`:
- Line 10: The title string in VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy
is still the placeholder "在这里输入结构的名称"; replace that literal with a real,
descriptive title for the structure (e.g. include the actual type name
VmLocalVolumeCachePoolInventory or a Chinese localized name like
"VmLocalVolumeCachePoolInventory 结构" / "虚拟机本地卷缓存池清单结构") so the generated
documentation shows a proper, professional heading.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStateDoc_zh_cn.groovy`:
- Line 7: The documentation title is a placeholder; open
VmLocalVolumeCachePoolStateDoc_zh_cn.groovy and replace the title string
("在这里输入结构的名称") passed to the title call with the accurate structure name (e.g.,
"VmLocalVolumeCachePoolState") or a localized Chinese equivalent so the
generated docs show the real structure name instead of template text.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheStateDoc_zh_cn.groovy`:
- Line 7: The document title is still the placeholder "在这里输入结构的名称"; replace it
with the real structure name, e.g., change the title line in
VmLocalVolumeCacheStateDoc_zh_cn.groovy from title "在这里输入结构的名称" to title
"VmLocalVolumeCacheState" (or the appropriate localized name for
VmLocalVolumeCacheState) so the generated docs show the correct structure name.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO.java`:
- Around line 28-31: 更新 VmLocalVolumeCacheVO 类上关于 VmLocalVolumeCachePoolVO
的注释以反映真实约束:把“References VmLocalVolumeCachePoolVO in kvm plugin — no `@ForeignKey`
to avoid cross-module dependency.” 改为准确说明 VmLocalVolumeCachePoolVO 位于
header/localVolumeCache 模块,并说明不使用 `@ForeignKey` 的真实原因(例如避免循环依赖或模块耦合),确保引用类名
VmLocalVolumeCachePoolVO 和字段含义保持一致以便后续维护者理解数据关系。

In
`@header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsgDoc_zh_cn.groovy`:
- Around line 335-377: 为新增的缓存参数补充描述文本:在 APICreateVmInstanceMsgDoc_zh_cn.groovy
中为字段
enableRootVolumeCache、cacheMode、rootVolumeCachePoolUuid、rootVolumeCacheMode、dataDiskCacheConfigOnIndex
的 desc 属性分别填写含义、可选值/格式约束(例如 cacheMode/rootVolumeCacheMode 支持
"WriteBack"、dataDiskCacheConfigOnIndex 为索引->配置的 Map 格式)、以及生效条件或版本信息(如仅在某些
storage 或版本生效);保持其它属性位置不变并使用与现有文档一致的简短中文句式描述,以便与接口文档统一。

In `@header/src/main/java/org/zstack/header/volume/VolumeInventory.java`:
- Around line 220-222: Fix the formatting around the null-check in
VolumeInventory: change the `if` statement that checks vo.getLocalVolumeCache()
so there is a space after the `if` keyword (i.e., use `if (`) and keep the body
that calls inv.setLocalVolumeCache(vo.getLocalVolumeCache().toInventory());
unchanged; locate the statement in VolumeInventory.java where
vo.getLocalVolumeCache() is checked and adjust spacing to match project style.

In `@storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java`:
- Line 617: The local variable CachePoolTag in VolumeManagerImpl should be
renamed to lowerCamelCase (cachePoolTag) to follow naming conventions; update
the declaration that calls
msg.getSystemTag(VolumeSystemTags.VOLUME_CACHE_POOL_UUID::isMatch) and replace
all usages of CachePoolTag within the method (and any nearby code in
VolumeManagerImpl that references it) to cachePoolTag so the code compiles and
adheres to the style guide.

---

Nitpick comments:
In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java`:
- Around line 35-39: Remove the redundant wildcard import: the file already
explicitly imports java.util.ArrayList, java.util.HashSet, java.util.List and
java.util.Map, so delete the line importing java.util.* to avoid
duplicate/ambiguous imports (keep the specific imports intact in
VmAllocateHostFlow.java).
- Around line 173-181: Ensure you validate the integer key "index" against the
data disks list before trusting it: when iterating dataDiskCacheConfigs
(variable dataDiskCacheConfigs) in VmAllocateHostFlow, check that index is >= 0
and < spec.getDataDiskOfferings().size() (or the appropriate data-disk
collection on the same spec) before any access that would use that index; if out
of range, skip that entry (or handle gracefully) and still allow reading the
cachePoolUuid from APICreateVmInstanceMsg.VolumeCacheConfig when safe, and add a
brief log or comment to explain the skip.

In `@header/src/main/java/org/zstack/header/host/HostVO.java`:
- Around line 45-48: The OneToMany mapping on HostVO for localVolumeCachePools
currently uses FetchType.EAGER which can hurt performance; change the annotation
on the localVolumeCachePools field from FetchType.EAGER to FetchType.LAZY in
HostVO and audit call sites that access HostVO.getLocalVolumeCachePools() to
ensure they load the collection within an active transaction (or use explicit
fetch-joins in queries) or adjust those queries to join-fetch
VmLocalVolumeCachePoolVO when pooled data is required.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java`:
- Line 20: 字段 metadata 在类 VmLocalVolumeCachePoolInventory 中使用了原始类型
LinkedHashMap,存在运行时类型不确定性;将其声明改为 LinkedHashMap<String, Object>,并同步更新相关引用(包含
metadata 的 getter/setter、构造器或任何方法中对 metadata 的赋值/返回处,如类中 113-119
行涉及的方法)以使用相同的泛型签名,确保编译通过并移除不必要的强制类型转换。

In `@header/src/main/java/org/zstack/header/vm/CreateVmInstanceMsg.java`:
- Line 45: CreateVmInstanceMsg currently references the API DTO
APICreateVmInstanceMsg.VolumeCacheConfig via the field
dataDiskCacheConfigOnIndex, coupling the internal message to the API layer;
define a new shared type (e.g. org.zstack.header.vm.VolumeCacheConfig) that
mirrors the needed fields and replace all usages of
APICreateVmInstanceMsg.VolumeCacheConfig in CreateVmInstanceMsg (and related
code paths flagged around lines 425-430) with this new VolumeCacheConfig, update
imports/usages accordingly, and ensure any serialization/validation logic is
moved to or delegated through the new shared class so internal messages no
longer depend on the APICreateVmInstanceMsg type.

In `@plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java`:
- Around line 148-153: Fix two small formatting issues in VolumeTO.valueOf: add
a space before the opening brace in the null-check "if (cacheInv != null) {",
and remove the extra space in the return statement so it reads "return to;".
These changes touch the block that calls vol.getLocalVolumeCache(), assigns
cacheInv, creates CacheTO.valueOf(cacheInv), and calls to.setCache(cacheTO).

In `@storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java`:
- Around line 660-662: The persist of cacheVO is done outside the
SQLBatchWithReturn transaction (dbf.persist(cacheVO)), which risks inconsistency
if it fails after volume creation; move the dbf.persist(cacheVO) call into the
existing SQLBatchWithReturn block so cacheVO is saved inside the same
transaction as the volume creation (ensure the null check for cacheVO is applied
inside the batch and that any exceptions will roll back both volume and cache
creation together).

In
`@utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java`:
- Around line 15875-15943: Rename the newly added constants that use
LOCALVOLUMECACHE to follow the repository constant naming convention using
underscores: change each symbol like ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_10001
and its string value "ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_10001" to
ORG_ZSTACK_STORAGE_LOCAL_VOLUME_CACHE_10001 with the matching string
"ORG_ZSTACK_STORAGE_LOCAL_VOLUME_CACHE_10001"; update all occurrences/usages
(references to the constant names and the literal string values) so callers,
tests, and any error-code maps use the new names consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: http://open.zstack.ai:20001/code-reviews/zstack-cloud.yaml (via .coderabbit.yaml)

Review profile: CHILL

Plan: Pro

Run ID: 1fc3cbfa-9d47-472c-9f02-9379302f6f72

📥 Commits

Reviewing files that changed from the base of the PR and between cf48ab8 and 8b9a743.

⛔ Files ignored due to path filters (44)
  • conf/globalConfig/vmLocalVolumeCachePool.xml is excluded by !**/*.xml
  • conf/persistence.xml is excluded by !**/*.xml
  • conf/serviceConfig/host.xml is excluded by !**/*.xml
  • conf/serviceConfig/vmLocalVolumeCache.xml is excluded by !**/*.xml
  • conf/serviceConfig/vmLocalVolumeCachePool.xml is excluded by !**/*.xml
  • conf/springConfigXml/localVolumeCache.xml is excluded by !**/*.xml
  • conf/zstack.xml is excluded by !**/*.xml
  • sdk/src/main/java/SourceClassMap.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CacheTO.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ChangeVmLocalVolumeCachePoolStateAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ChangeVmLocalVolumeCachePoolStateResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateOvnControllerVmAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateVmInstanceAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DeleteVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DeleteVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DisableVolumeCacheAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DisableVolumeCacheResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/EnableVolumeCacheAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/EnableVolumeCacheResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ExtendVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ExtendVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/GetBlockDevicesAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/GetBlockDevicesResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/HostBlockDeviceStruct.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/HostInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/QueryVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/QueryVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ReconnectVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ReconnectVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/SyncVmLocalVolumeCachePoolCapacityAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/SyncVmLocalVolumeCachePoolCapacityResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/UpdateVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/UpdateVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheMode.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolState.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolStatus.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheState.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VolumeInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VolumeTO.java is excluded by !sdk/**
  • test/src/test/resources/springConfigXml/license.xml is excluded by !**/*.xml
📒 Files selected for processing (64)
  • compute/src/main/java/org/zstack/compute/host/HostBase.java
  • compute/src/main/java/org/zstack/compute/vm/InstantiateVmFromNewCreatedStruct.java
  • compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java
  • compute/src/main/java/org/zstack/compute/vm/VmAllocateVolumeFlow.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceManagerImpl.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceUtils.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstantiateOtherDiskFlow.java
  • conf/db/upgrade/V5.5.12__schema.sql
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEvent.java
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEventDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsg.java
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsgDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/GetBlockDevicesOnHostMsg.java
  • header/src/main/java/org/zstack/header/host/GetBlockDevicesOnHostReply.java
  • header/src/main/java/org/zstack/header/host/HostBlockDeviceStruct.java
  • header/src/main/java/org/zstack/header/host/HostBlockDeviceStructDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/HostInventory.java
  • header/src/main/java/org/zstack/header/host/HostVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/CachePoolMetadata.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheConstant.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventoryDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheMode.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheModeDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO_.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolState.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStateDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatus.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatusDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO_.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheState.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheStateDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO_.java
  • header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsgDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/vm/CreateVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/vm/InstantiateNewCreatedVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java
  • header/src/main/java/org/zstack/header/volume/VolumeAO.java
  • header/src/main/java/org/zstack/header/volume/VolumeInventory.java
  • plugin/cbd/src/main/java/org/zstack/cbd/kvm/KvmCbdNodeServer.java
  • plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageFactory.java
  • plugin/iscsi/src/main/java/org/zstack/iscsi/kvm/KvmIscsiNodeServer.java
  • plugin/kvm/src/main/java/org/zstack/kvm/CacheTO.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMApiInterceptor.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMAttachVolumeExtensionPoint.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMExtensionEmitter.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java
  • plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java
  • plugin/vhost/src/main/java/org/zstack/vhost/kvm/KvmVhostNodeServer.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeApiInterceptor.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeSystemTags.java
  • testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy
  • utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java
💤 Files with no reviewable changes (2)
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMApiInterceptor.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeApiInterceptor.java

Comment thread compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java Outdated
Comment thread compute/src/main/java/org/zstack/compute/vm/VmAllocateVolumeFlow.java Outdated
Comment thread compute/src/main/java/org/zstack/compute/vm/VmAllocateVolumeFlow.java Outdated
Comment thread compute/src/main/java/org/zstack/compute/vm/VmInstantiateOtherDiskFlow.java Outdated
Comment thread header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java Outdated
Comment thread plugin/kvm/src/main/java/org/zstack/kvm/CacheTO.java
Comment thread plugin/kvm/src/main/java/org/zstack/kvm/KVMAttachVolumeExtensionPoint.java Outdated
Comment thread plugin/kvm/src/main/java/org/zstack/kvm/KVMExtensionEmitter.java Outdated
Comment thread plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java Outdated
@MatheMatrix MatheMatrix force-pushed the sync/haidong.pang/feature/ZSTAC-81715@@3 branch from 8b9a743 to b31ef5b Compare March 16, 2026 02:20

@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: 5

Caution

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

⚠️ Outside diff range comments (1)
header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java (1)

74-74: ⚠️ Potential issue | 🟡 Minor

代码中不应包含中文注释。

根据编码规范,代码中不应当有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写。

第 74 行的 @summary 创建云主机 应改为英文。

🔧 建议修复
- * `@summary` 创建云主机
+ * `@summary` Create VM instance

As per coding guidelines: "代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写"

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

In `@header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java` at
line 74, Replace the Chinese `@summary` text in APICreateVmInstanceMsg with an
English description: locate the class APICreateVmInstanceMsg and change the
Javadoc `@summary` value from "创建云主机" to a clear English phrase such as "Create VM
instance" or "Create virtual machine" ensuring spelling and capitalization are
correct.
♻️ Duplicate comments (2)
plugin/kvm/src/main/java/org/zstack/kvm/KVMExtensionEmitter.java (1)

395-399: ⚠️ Potential issue | 🔴 Critical

异步回调链式编排存在严重问题:同一 Completion 被多次并发回调。

当前实现将同一个 completion 直接传递给所有扩展点,如果有多个扩展实现,会导致 completion.success()completion.fail() 被多次调用,产生状态机混乱。

参考本文件中其他异步方法(如 doBeforeTakeSnapshotdoBeforeCommitVolumedoBeforePullVolume)的实现模式,应使用迭代器串行链式执行,确保最终只回调一次。

🐛 建议修复
+    private void doPreBeforeAttachVolume(Iterator<KVMAttachVolumeExtensionPoint> it,
+                                         KVMHostInventory host, VmInstanceInventory vm, VolumeInventory vol,
+                                         AttachDataVolumeCmd cmd, Map data, Completion completion) {
+        if (!it.hasNext()) {
+            completion.success();
+            return;
+        }
+
+        KVMAttachVolumeExtensionPoint ext = it.next();
+        ext.preBeforeAttachVolume(host, vm, vol, cmd, data, new Completion(completion) {
+            `@Override`
+            public void success() {
+                doPreBeforeAttachVolume(it, host, vm, vol, cmd, data, completion);
+            }
+
+            `@Override`
+            public void fail(ErrorCode errorCode) {
+                completion.fail(errorCode);
+            }
+        });
+    }
+
     public void preBeforeAttachVolume(KVMHostInventory host, VmInstanceInventory vm, VolumeInventory vol, AttachDataVolumeCmd cmd, Map data, Completion completion) {
-        for (KVMAttachVolumeExtensionPoint ext : attachVolumeExts) {
-            ext.preBeforeAttachVolume(host, vm, vol, cmd, data, completion);
-        }
+        doPreBeforeAttachVolume(attachVolumeExts.iterator(), host, vm, vol, cmd, data, completion);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMExtensionEmitter.java` around
lines 395 - 399, The preBeforeAttachVolume method currently passes the same
Completion to every KVMAttachVolumeExtensionPoint in attachVolumeExts, causing
multiple concurrent callbacks; change it to an iterator-based chain: create an
iterator over attachVolumeExts and implement a private inline nextExtension()
that calls ext.preBeforeAttachVolume(host, vm, vol, cmd, data, new Completion()
{ onSuccess -> proceed to nextExtension(); onFailure -> call original
completion.fail(...) and stop; }), and when iterator is exhausted call original
completion.success(); also catch and fail the original completion on unexpected
exceptions so the original Completion is invoked exactly once.
plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java (1)

3710-3712: ⚠️ Potential issue | 🔴 Critical

newSimpleFlowChain() 改为 newShareFlowChain() 以匹配 ShareFlow

Line 3710 当前创建的是 SimpleFlowChain,但 Line 3712 使用了 new ShareFlow();这与框架常规用法不匹配,可能导致 flow 回调注册/执行异常。

♻️ 建议修改
-        FlowChain chain = FlowChainBuilder.newSimpleFlowChain();
+        FlowChain chain = FlowChainBuilder.newShareFlowChain();
         chain.setName(String.format("attach-volume-%s", msg.getInventory().getUuid()));
         chain.then(new ShareFlow() {
#!/bin/bash
set -euo pipefail

echo "== FlowChainBuilder methods =="
fd 'FlowChainBuilder.java$' | while read -r f; do
  echo "--- $f ---"
  rg -n -C2 'newSimpleFlowChain|newShareFlowChain' "$f"
done

echo
echo "== ShareFlow definition =="
fd 'ShareFlow.java$' | while read -r f; do
  echo "--- $f ---"
  sed -n '1,260p' "$f"
done

echo
echo "== Current attachVolume block in KVMHost =="
fd 'KVMHost.java$' | while read -r f; do
  echo "--- $f ---"
  rg -n -C8 --type java 'attach-volume-|newSimpleFlowChain\(|newShareFlowChain\(|new ShareFlow\(' "$f"
done

echo
echo "== Repository usage patterns around ShareFlow =="
rg -n -C2 --type java 'newShareFlowChain\(\)|newSimpleFlowChain\(|new ShareFlow\('
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java` around lines 3710 -
3712, The code creates a SimpleFlowChain but instantiates a ShareFlow, which is
inconsistent; replace the FlowChain creation call
FlowChainBuilder.newSimpleFlowChain() with FlowChainBuilder.newShareFlowChain()
so the chain type matches the ShareFlow usage (look for the FlowChain variable
named "chain" and the anonymous new ShareFlow() passed to then()). Ensure any
chain configuration remains valid for a ShareFlow after the swap.
🧹 Nitpick comments (9)
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityInventory.java (1)

13-18: 建议添加 serialVersionUID 以确保序列化兼容性。

类实现了 Serializable 接口但未定义 serialVersionUID,在类结构发生变化时可能导致反序列化失败。

♻️ 建议修改
 `@Inventory`(mappingVOClass = VmLocalVolumeCachePoolCapacityVO.class)
 `@PythonClassInventory`
 public class VmLocalVolumeCachePoolCapacityInventory implements Serializable {
+    private static final long serialVersionUID = 1L;
+
     private String uuid;
     private long totalCapacity;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityInventory.java`
around lines 13 - 18, The class VmLocalVolumeCachePoolCapacityInventory
implements Serializable but lacks a serialVersionUID; add a private static final
long serialVersionUID (e.g. 1L or a generated value) to the
VmLocalVolumeCachePoolCapacityInventory class to ensure serialization
compatibility across versions; declare it as private static final long
serialVersionUID = 1L; and commit the change alongside the class definition.
storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java (1)

643-662: 建议将 cacheVO 持久化并入同一批次,降低部分成功风险

现在 VolumeVO 在 SQLBatch 中落库,而 cacheVO 在 Line 660-662 单独持久化。建议合并到同一事务批次内,避免出现“卷创建成功但缓存配置缺失”的中间态。

🤖 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/VolumeManagerImpl.java`
around lines 643 - 662, The code persists VolumeVO inside the SQLBatchWithReturn
(methods in VolumeManagerImpl creating vo) but calls dbf.persist(cacheVO)
afterward, risking partial success; move the cacheVO persistence into the same
SQLBatchWithReturn transaction (inside the scripts() method alongside
dbf.getEntityManager().persist(finalVo)/flush()/refresh()) so both VolumeVO and
cacheVO are saved atomically, then return finalVo as before and remove the
standalone dbf.persist(cacheVO) call; ensure any reloads (dbf.reload(vo)) and
extension callbacks still run after the batch executes.
header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEventDoc_zh_cn.groovy (2)

13-13: 字段描述不完整。

blockDevicesdesc"null",建议填写有意义的描述,例如 "块设备列表"

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

In
`@header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEventDoc_zh_cn.groovy`
at line 13, Replace the placeholder desc "null" for the blockDevices field in
APIGetBlockDevicesEventDoc_zh_cn.groovy with a meaningful Chinese description
(e.g., "块设备列表") so the documentation properly describes the blockDevices
property; locate the desc assignment for blockDevices in the
APIGetBlockDevicesEventDoc_zh_cn class and update its desc string accordingly.

8-8: 文档占位符未完善。

title 字段仍为占位符 "在这里输入结构的名称",建议填写实际的事件名称,例如 "获取块设备事件"

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

In
`@header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEventDoc_zh_cn.groovy`
at line 8, The title field in APIGetBlockDevicesEventDoc_zh_cn.groovy is still a
placeholder; update the title call (the "title" string literal) to a proper
Chinese event name such as "获取块设备事件" (or another accurate translation) so the
documentation reflects the real event name; ensure the change is made in the
title invocation inside the APIGetBlockDevicesEventDoc_zh_cn class/file.
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy (2)

36-53: 字段描述缺失。

metadatatotalCapacityavailableCapacity 字段的 desc 为空,建议补充相应的中文描述以提升文档可读性。

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

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy`
around lines 36 - 53, The doc fields metadata, totalCapacity and
availableCapacity in VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy have empty
desc values; update the field blocks for "metadata", "totalCapacity" and
"availableCapacity" to provide concise Chinese descriptions (e.g., metadata:
“缓存池的元数据,键值对格式”, totalCapacity: “缓存池的总容量,单位为字节”, availableCapacity:
“缓存池的可用容量,单位为字节”) so the documentation is readable and informative; locate the
three field { name "metadata" }, field { name "totalCapacity" } and field { name
"availableCapacity" } entries and fill their desc strings accordingly.

10-10: 文档占位符未完善。

title 字段仍为占位符 "在这里输入结构的名称",建议填写实际的结构名称,例如 "虚拟机本地卷缓存池"

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

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy`
at line 10, Replace the placeholder title string in
VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy: change the current title
"在这里输入结构的名称" to a meaningful Chinese name such as "虚拟机本地卷缓存池" by updating the
title field so the document reflects the actual structure name.
compute/src/main/java/org/zstack/compute/vm/VmAllocateVolumeFlow.java (1)

142-142: 代码格式不一致。

Line 142 的 if(vspec... 与其他位置的 if ( 格式不一致,建议统一在 if( 之间添加空格。

♻️ 建议修改
-                if(vspec.getCachePoolUuid() != null) {
+                if (vspec.getCachePoolUuid() != null) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateVolumeFlow.java` at
line 142, In class VmAllocateVolumeFlow, change the spacing for the conditional
that checks vspec.getCachePoolUuid() so it follows the project's `if (` style
(i.e., add a space after `if`), updating the `if(vspec.getCachePoolUuid() !=
null)` occurrence to `if (vspec.getCachePoolUuid() != null)` to match other `if
(` usages and keep formatting consistent around the vspec/getCachePoolUuid()
check.
header/src/main/java/org/zstack/header/host/HostBlockDeviceStruct.java (1)

6-6: 建议添加 serialVersionUID

类实现了 Serializable 接口,建议显式声明 serialVersionUID 以确保序列化兼容性,避免在类结构变化时出现反序列化问题。

♻️ 建议修复
 public class HostBlockDeviceStruct implements Serializable {
+    private static final long serialVersionUID = 1L;
+
     private String name;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@header/src/main/java/org/zstack/header/host/HostBlockDeviceStruct.java` at
line 6, The class HostBlockDeviceStruct implements Serializable but lacks an
explicit serialVersionUID; add a private static final long serialVersionUID
field to HostBlockDeviceStruct (e.g., set to a stable value like 1L) to ensure
serialization compatibility across versions and avoid InvalidClassException
during deserialization.
compute/src/main/java/org/zstack/compute/vm/InstantiateVmFromNewCreatedStruct.java (1)

32-36: 字段命名存在潜在歧义。

cacheMode(第 33 行)和 rootVolumeCacheMode(第 35 行)两个字段的用途可能存在混淆。建议确认:

  • cacheMode 是否为全局默认缓存模式?
  • rootVolumeCacheMode 是否专门针对根卷的缓存模式?

如果两者语义不同,建议为 cacheMode 添加更具描述性的名称(如 defaultCacheMode)以提高代码可读性。

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

In
`@compute/src/main/java/org/zstack/compute/vm/InstantiateVmFromNewCreatedStruct.java`
around lines 32 - 36, The fields cacheMode and rootVolumeCacheMode in
InstantiateVmFromNewCreatedStruct are ambiguous; rename cacheMode to a clearer
name (e.g., defaultCacheMode) across the class and all usages to indicate it is
the global/default cache mode, keep rootVolumeCacheMode for root-volume-specific
behavior, and update corresponding getters/setters, constructors, serializations
(JSON/mapping), equals/hashCode and any references in codebase (search for
cacheMode, getCacheMode, setCacheMode) to use defaultCacheMode; if you need
compatibility, add a deprecated cacheMode field or retain a transient alias that
forwards to defaultCacheMode.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheModeDoc_zh_cn.groovy`:
- Around line 7-11: Replace the placeholder title "在这里输入结构的名称" with a meaningful
Chinese title for the document and populate the empty desc for the field named
"WriteBack" with a concise Chinese description explaining the WriteBack caching
mode (its behavior, when to use it, and any important effects or constraints);
update the VmLocalVolumeCacheModeDoc_zh_cn.groovy entries for title and the
field{name "WriteBack"} so the API documentation is informative and complete.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatusDoc_zh_cn.groovy`:
- Line 7: Replace the placeholder title string in
VmLocalVolumeCachePoolStatusDoc_zh_cn.groovy: change the `title "在这里输入结构的名称"`
entry to a meaningful Chinese title that describes the documented structure (use
the actual structure name or a concise descriptive phrase) so the `title` field
is no longer a template placeholder.

In
`@header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsgDoc_zh_cn.groovy`:
- Around line 332-378: The new API parameters enableRootVolumeCache, cacheMode,
rootVolumeCachePoolUuid, rootVolumeCacheMode, and dataDiskCacheConfigOnIndex
have empty desc fields; update each column block to provide concise descriptions
explaining the parameter purpose, allowed values/constraints (e.g.,
cacheMode/rootVolumeCacheMode allowed values such as "WriteBack"), interactions
with other params (e.g., enableRootVolumeCache enabling related
rootVolumeCachePoolUuid and rootVolumeCacheMode), default behavior when omitted,
and any format expectations for the Map type dataDiskCacheConfigOnIndex so
callers understand semantics and validation rules.

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java`:
- Around line 5319-5351: toCachePoolMetadata() currently omits mapping of
PoolRsp.mountPoint, causing loss of mount point info; update the method to copy
the mount point from the source object (filesystem.mountPoint or pool.mountPoint
depending on where PoolRsp stores it) into the corresponding field on
CachePoolMetadata (either CachePoolMetadata.FileSystemRef.setMountPoint(...) if
that setter exists, or metadata.setMountPoint(...)) and guard with a null check
so you only set it when mountPoint is non-null; locate this change in the
toCachePoolMetadata() method where filesystem -> FileSystemRef and metadata are
constructed.

In `@storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java`:
- Around line 616-620: Fix three issues in VolumeManagerImpl: (1) validate the
token returned by VolumeSystemTags.VOLUME_CACHE_MODE.getTokenByTag before
calling VmLocalVolumeCacheMode.valueOf (or wrap in try/catch) to convert
cacheModeTag into an enum and throw/return a clear API/business error when token
is null/invalid instead of allowing NullPointerException; (2) rename the local
variable CachePoolTag to cachePoolTag to follow lowerCamelCase style; (3) ensure
cacheVO persistence is done inside the same SQLBatchWithReturn transaction that
saves VolumeVO (move the cacheVO update/save into the same SQLBatchWithReturn
block where VolumeVO is persisted) so the volume creation and cache
configuration are committed atomically.

---

Outside diff comments:
In `@header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java`:
- Line 74: Replace the Chinese `@summary` text in APICreateVmInstanceMsg with an
English description: locate the class APICreateVmInstanceMsg and change the
Javadoc `@summary` value from "创建云主机" to a clear English phrase such as "Create VM
instance" or "Create virtual machine" ensuring spelling and capitalization are
correct.

---

Duplicate comments:
In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMExtensionEmitter.java`:
- Around line 395-399: The preBeforeAttachVolume method currently passes the
same Completion to every KVMAttachVolumeExtensionPoint in attachVolumeExts,
causing multiple concurrent callbacks; change it to an iterator-based chain:
create an iterator over attachVolumeExts and implement a private inline
nextExtension() that calls ext.preBeforeAttachVolume(host, vm, vol, cmd, data,
new Completion() { onSuccess -> proceed to nextExtension(); onFailure -> call
original completion.fail(...) and stop; }), and when iterator is exhausted call
original completion.success(); also catch and fail the original completion on
unexpected exceptions so the original Completion is invoked exactly once.

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java`:
- Around line 3710-3712: The code creates a SimpleFlowChain but instantiates a
ShareFlow, which is inconsistent; replace the FlowChain creation call
FlowChainBuilder.newSimpleFlowChain() with FlowChainBuilder.newShareFlowChain()
so the chain type matches the ShareFlow usage (look for the FlowChain variable
named "chain" and the anonymous new ShareFlow() passed to then()). Ensure any
chain configuration remains valid for a ShareFlow after the swap.

---

Nitpick comments:
In
`@compute/src/main/java/org/zstack/compute/vm/InstantiateVmFromNewCreatedStruct.java`:
- Around line 32-36: The fields cacheMode and rootVolumeCacheMode in
InstantiateVmFromNewCreatedStruct are ambiguous; rename cacheMode to a clearer
name (e.g., defaultCacheMode) across the class and all usages to indicate it is
the global/default cache mode, keep rootVolumeCacheMode for root-volume-specific
behavior, and update corresponding getters/setters, constructors, serializations
(JSON/mapping), equals/hashCode and any references in codebase (search for
cacheMode, getCacheMode, setCacheMode) to use defaultCacheMode; if you need
compatibility, add a deprecated cacheMode field or retain a transient alias that
forwards to defaultCacheMode.

In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateVolumeFlow.java`:
- Line 142: In class VmAllocateVolumeFlow, change the spacing for the
conditional that checks vspec.getCachePoolUuid() so it follows the project's `if
(` style (i.e., add a space after `if`), updating the
`if(vspec.getCachePoolUuid() != null)` occurrence to `if
(vspec.getCachePoolUuid() != null)` to match other `if (` usages and keep
formatting consistent around the vspec/getCachePoolUuid() check.

In
`@header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEventDoc_zh_cn.groovy`:
- Line 13: Replace the placeholder desc "null" for the blockDevices field in
APIGetBlockDevicesEventDoc_zh_cn.groovy with a meaningful Chinese description
(e.g., "块设备列表") so the documentation properly describes the blockDevices
property; locate the desc assignment for blockDevices in the
APIGetBlockDevicesEventDoc_zh_cn class and update its desc string accordingly.
- Line 8: The title field in APIGetBlockDevicesEventDoc_zh_cn.groovy is still a
placeholder; update the title call (the "title" string literal) to a proper
Chinese event name such as "获取块设备事件" (or another accurate translation) so the
documentation reflects the real event name; ensure the change is made in the
title invocation inside the APIGetBlockDevicesEventDoc_zh_cn class/file.

In `@header/src/main/java/org/zstack/header/host/HostBlockDeviceStruct.java`:
- Line 6: The class HostBlockDeviceStruct implements Serializable but lacks an
explicit serialVersionUID; add a private static final long serialVersionUID
field to HostBlockDeviceStruct (e.g., set to a stable value like 1L) to ensure
serialization compatibility across versions and avoid InvalidClassException
during deserialization.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityInventory.java`:
- Around line 13-18: The class VmLocalVolumeCachePoolCapacityInventory
implements Serializable but lacks a serialVersionUID; add a private static final
long serialVersionUID (e.g. 1L or a generated value) to the
VmLocalVolumeCachePoolCapacityInventory class to ensure serialization
compatibility across versions; declare it as private static final long
serialVersionUID = 1L; and commit the change alongside the class definition.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy`:
- Around line 36-53: The doc fields metadata, totalCapacity and
availableCapacity in VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy have empty
desc values; update the field blocks for "metadata", "totalCapacity" and
"availableCapacity" to provide concise Chinese descriptions (e.g., metadata:
“缓存池的元数据,键值对格式”, totalCapacity: “缓存池的总容量,单位为字节”, availableCapacity:
“缓存池的可用容量,单位为字节”) so the documentation is readable and informative; locate the
three field { name "metadata" }, field { name "totalCapacity" } and field { name
"availableCapacity" } entries and fill their desc strings accordingly.
- Line 10: Replace the placeholder title string in
VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy: change the current title
"在这里输入结构的名称" to a meaningful Chinese name such as "虚拟机本地卷缓存池" by updating the
title field so the document reflects the actual structure name.

In `@storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java`:
- Around line 643-662: The code persists VolumeVO inside the SQLBatchWithReturn
(methods in VolumeManagerImpl creating vo) but calls dbf.persist(cacheVO)
afterward, risking partial success; move the cacheVO persistence into the same
SQLBatchWithReturn transaction (inside the scripts() method alongside
dbf.getEntityManager().persist(finalVo)/flush()/refresh()) so both VolumeVO and
cacheVO are saved atomically, then return finalVo as before and remove the
standalone dbf.persist(cacheVO) call; ensure any reloads (dbf.reload(vo)) and
extension callbacks still run after the batch executes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: http://open.zstack.ai:20001/code-reviews/zstack-cloud.yaml (via .coderabbit.yaml)

Review profile: CHILL

Plan: Pro

Run ID: 7c3d7b1f-30fb-4a28-ae80-9f0cb7caede5

📥 Commits

Reviewing files that changed from the base of the PR and between 8b9a743 and b31ef5b.

⛔ Files ignored due to path filters (44)
  • conf/globalConfig/vmLocalVolumeCachePool.xml is excluded by !**/*.xml
  • conf/persistence.xml is excluded by !**/*.xml
  • conf/serviceConfig/host.xml is excluded by !**/*.xml
  • conf/serviceConfig/vmLocalVolumeCache.xml is excluded by !**/*.xml
  • conf/serviceConfig/vmLocalVolumeCachePool.xml is excluded by !**/*.xml
  • conf/springConfigXml/localVolumeCache.xml is excluded by !**/*.xml
  • conf/zstack.xml is excluded by !**/*.xml
  • sdk/src/main/java/SourceClassMap.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CacheTO.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ChangeVmLocalVolumeCachePoolStateAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ChangeVmLocalVolumeCachePoolStateResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateOvnControllerVmAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateVmInstanceAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DeleteVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DeleteVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DisableVolumeCacheAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DisableVolumeCacheResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/EnableVolumeCacheAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/EnableVolumeCacheResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ExtendVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ExtendVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/GetBlockDevicesAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/GetBlockDevicesResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/HostBlockDeviceStruct.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/HostInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/QueryVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/QueryVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ReconnectVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ReconnectVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/SyncVmLocalVolumeCachePoolCapacityAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/SyncVmLocalVolumeCachePoolCapacityResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/UpdateVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/UpdateVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheMode.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolState.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolStatus.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheState.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VolumeInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VolumeTO.java is excluded by !sdk/**
  • test/src/test/resources/springConfigXml/license.xml is excluded by !**/*.xml
📒 Files selected for processing (61)
  • compute/src/main/java/org/zstack/compute/host/HostBase.java
  • compute/src/main/java/org/zstack/compute/vm/InstantiateVmFromNewCreatedStruct.java
  • compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java
  • compute/src/main/java/org/zstack/compute/vm/VmAllocateVolumeFlow.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceManagerImpl.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceUtils.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstantiateOtherDiskFlow.java
  • conf/db/upgrade/V5.5.12__schema.sql
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEvent.java
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEventDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsg.java
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsgDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/GetBlockDevicesOnHostMsg.java
  • header/src/main/java/org/zstack/header/host/GetBlockDevicesOnHostReply.java
  • header/src/main/java/org/zstack/header/host/HostBlockDeviceStruct.java
  • header/src/main/java/org/zstack/header/host/HostBlockDeviceStructDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/HostInventory.java
  • header/src/main/java/org/zstack/header/host/HostVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/CachePoolMetadata.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheConstant.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventoryDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheMode.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheModeDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO_.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolState.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStateDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatus.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatusDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO_.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheState.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheStateDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO_.java
  • header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsgDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/vm/CreateVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/vm/InstantiateNewCreatedVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java
  • header/src/main/java/org/zstack/header/volume/VolumeAO.java
  • header/src/main/java/org/zstack/header/volume/VolumeInventory.java
  • plugin/kvm/src/main/java/org/zstack/kvm/CacheTO.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMApiInterceptor.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMAttachVolumeExtensionPoint.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMExtensionEmitter.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java
  • plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java
  • plugin/vhost/src/main/java/org/zstack/vhost/kvm/KvmVhostNodeServer.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeApiInterceptor.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeSystemTags.java
  • testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy
  • utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java
💤 Files with no reviewable changes (2)
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMApiInterceptor.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeApiInterceptor.java
✅ Files skipped from review due to trivial changes (2)
  • header/src/main/java/org/zstack/header/volume/VolumeAO.java
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsgDoc_zh_cn.groovy
🚧 Files skipped from review as they are similar to previous changes (21)
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheMode.java
  • header/src/main/java/org/zstack/header/host/HostBlockDeviceStructDoc_zh_cn.groovy
  • compute/src/main/java/org/zstack/compute/vm/VmInstantiateOtherDiskFlow.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventoryDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatus.java
  • header/src/main/java/org/zstack/header/volume/VolumeInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceManagerImpl.java
  • compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheStateDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheConstant.java
  • conf/db/upgrade/V5.5.12__schema.sql
  • header/src/main/java/org/zstack/header/localVolumeCache/CachePoolMetadata.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeSystemTags.java
  • header/src/main/java/org/zstack/header/host/HostVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStateDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO_.java
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsg.java
  • header/src/main/java/org/zstack/header/vm/InstantiateNewCreatedVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java

Comment on lines +7 to +11
title "在这里输入结构的名称"

field {
name "WriteBack"
desc ""

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

文档字段未填写完整。

title 仍为模板占位符 "在这里输入结构的名称"desc 字段为空。建议补充有意义的描述以便生成准确的 API 文档。

建议修改
 doc {

-	title "在这里输入结构的名称"
+	title "虚拟机本地卷缓存模式"

 	field {
 		name "WriteBack"
-		desc ""
+		desc "写回缓存模式"
 		type "VmLocalVolumeCacheMode"
 		since "5.5.6"
 	}
 }
📝 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
title "在这里输入结构的名称"
field {
name "WriteBack"
desc ""
title "虚拟机本地卷缓存模式"
field {
name "WriteBack"
desc "写回缓存模式"
type "VmLocalVolumeCacheMode"
since "5.5.6"
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheModeDoc_zh_cn.groovy`
around lines 7 - 11, Replace the placeholder title "在这里输入结构的名称" with a
meaningful Chinese title for the document and populate the empty desc for the
field named "WriteBack" with a concise Chinese description explaining the
WriteBack caching mode (its behavior, when to use it, and any important effects
or constraints); update the VmLocalVolumeCacheModeDoc_zh_cn.groovy entries for
title and the field{name "WriteBack"} so the API documentation is informative
and complete.


doc {

title "在这里输入结构的名称"

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

标题为占位符模板文本,需要填写实际内容。

title 字段当前值为 "在这里输入结构的名称",这是一个未填写的模板占位符,应替换为实际的结构名称描述。

📝 建议修复
-	title "在这里输入结构的名称"
+	title "虚拟机本地卷缓存池状态"
📝 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
title "在这里输入结构的名称"
title "虚拟机本地卷缓存池状态"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatusDoc_zh_cn.groovy`
at line 7, Replace the placeholder title string in
VmLocalVolumeCachePoolStatusDoc_zh_cn.groovy: change the `title "在这里输入结构的名称"`
entry to a meaningful Chinese title that describes the documented structure (use
the actual structure name or a concise descriptive phrase) so the `title` field
is no longer a template placeholder.

Comment on lines +332 to +378
column {
name "enableRootVolumeCache"
enclosedIn "params"
desc ""
location "body"
type "Boolean"
optional true
since "5.5.6"
}
column {
name "cacheMode"
enclosedIn "params"
desc ""
location "body"
type "String"
optional true
since "5.5.6"
values ("WriteBack")
}
column {
name "rootVolumeCachePoolUuid"
enclosedIn "params"
desc ""
location "body"
type "String"
optional true
since "5.5.6"
}
column {
name "rootVolumeCacheMode"
enclosedIn "params"
desc ""
location "body"
type "String"
optional true
since "5.5.6"
values ("WriteBack")
}
column {
name "dataDiskCacheConfigOnIndex"
enclosedIn "params"
desc ""
location "body"
type "Map"
optional true
since "5.5.6"
}

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

新增参数缺少描述,文档信息不完整

Line 335、344、354、363、373 的 desc 都是空值。建议补齐每个参数语义与约束(例如用途、取值影响、与其他参数的关系),避免 API 使用方误解。

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

In
`@header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsgDoc_zh_cn.groovy`
around lines 332 - 378, The new API parameters enableRootVolumeCache, cacheMode,
rootVolumeCachePoolUuid, rootVolumeCacheMode, and dataDiskCacheConfigOnIndex
have empty desc fields; update each column block to provide concise descriptions
explaining the parameter purpose, allowed values/constraints (e.g.,
cacheMode/rootVolumeCacheMode allowed values such as "WriteBack"), interactions
with other params (e.g., enableRootVolumeCache enabling related
rootVolumeCachePoolUuid and rootVolumeCacheMode), default behavior when omitted,
and any format expectations for the Map type dataDiskCacheConfigOnIndex so
callers understand semantics and validation rules.

Comment thread plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java
Comment on lines +616 to +620
String cacheModeTag = msg.getSystemTag(VolumeSystemTags.VOLUME_CACHE_MODE::isMatch);
String CachePoolTag = msg.getSystemTag(VolumeSystemTags.VOLUME_CACHE_POOL_UUID::isMatch);
if (cacheModeTag != null) {
cacheVO.setCacheMode(VmLocalVolumeCacheMode.valueOf(VolumeSystemTags.VOLUME_CACHE_MODE.getTokenByTag(cacheModeTag, VolumeSystemTags.CACHE_MODE_TOKEN)));
}

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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== 1) 查看缓存模式枚举定义 ==="
fd VmLocalVolumeCacheMode.java --exec sed -n '1,200p' {}

echo "=== 2) 查看 VOLUME_CACHE_MODE 标签定义与 token ==="
fd VolumeSystemTags.java --exec rg -n "VOLUME_CACHE_MODE|CACHE_MODE_TOKEN" {}

echo "=== 3) 查看是否存在上游对 cache mode 的统一校验 ==="
rg -n "VOLUME_CACHE_MODE|VOLUME_CACHE_ENABLE|VOLUME_CACHE_POOL_UUID"

Repository: MatheMatrix/zstack

Length of output: 2256


🏁 Script executed:

#!/bin/bash

echo "=== 1) 查看 getTokenByTag 方法定义 ==="
rg -n "def getTokenByTag|getTokenByTag" --type java -A 5

echo "=== 2) 查看 VolumeManagerImpl 的持久化代码 ==="
fd VolumeManagerImpl.java --exec sed -n '600,670p' {}

echo "=== 3) 查看 createVolume 方法的完整持久化逻辑 ==="
fd VolumeManagerImpl.java --exec sed -n '595,630p' {}

Repository: MatheMatrix/zstack

Length of output: 50374


🏁 Script executed:

#!/bin/bash

echo "=== 查看 VolumeManagerImpl createVolume 方法的持久化部分 ==="
fd VolumeManagerImpl.java --exec sed -n '600,670p' {}

echo "=== 检查代码中是否有对 cache mode 的输入验证 ==="
rg -n "WriteBack|cacheMode|CACHE_MODE" storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java

echo "=== 检查 PatternedSystemTag.getTokenByTag 的返回值范围 ==="
fd PatternedSystemTag.java --exec sed -n '75,90p' {}

Repository: MatheMatrix/zstack

Length of output: 4278


Line 619 存在多个关键问题

  1. Enum.valueOf 缺乏异常处理getTokenByTag() 可能返回 null(若 tokenName 不存在),导致 Enum.valueOf(null)NullPointerException。应添加显式校验或 try-catch,在异常情况下转换为业务错误。

  2. 缓存配置持久化原子性缺失:VolumeVO 在 SQLBatchWithReturn 内持久化(Line 648-654),而 cacheVO 在 SQLBatch 外单独持久化(Line 662-663),存在"卷已创建但缓存配置未落库"的部分成功窗口。建议将 cacheVO 持久化纳入同一事务。

  3. 变量命名风格:Line 617 String CachePoolTag 应遵循 lowerCamelCase,改为 cachePoolTag

🤖 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/VolumeManagerImpl.java`
around lines 616 - 620, Fix three issues in VolumeManagerImpl: (1) validate the
token returned by VolumeSystemTags.VOLUME_CACHE_MODE.getTokenByTag before
calling VmLocalVolumeCacheMode.valueOf (or wrap in try/catch) to convert
cacheModeTag into an enum and throw/return a clear API/business error when token
is null/invalid instead of allowing NullPointerException; (2) rename the local
variable CachePoolTag to cachePoolTag to follow lowerCamelCase style; (3) ensure
cacheVO persistence is done inside the same SQLBatchWithReturn transaction that
saves VolumeVO (move the cacheVO update/save into the same SQLBatchWithReturn
block where VolumeVO is persisted) so the volume creation and cache
configuration are committed atomically.

@MatheMatrix MatheMatrix force-pushed the sync/haidong.pang/feature/ZSTAC-81715@@3 branch from b31ef5b to e09ddd2 Compare March 16, 2026 06:35

@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: 4

Caution

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

⚠️ Outside diff range comments (2)
plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java (1)

68-90: ⚠️ Potential issue | 🟡 Minor

拷贝构造函数对 cache 使用浅拷贝,可能引入共享可变状态。

Line 89 直接赋值引用会让副本与原对象共享同一个 CacheTO。如果后续任一方修改 cache,会出现串改。建议改为深拷贝(或在 CacheTO 不可变时明确约束)。

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

In `@plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java` around lines 68 - 90,
The VolumeTO copy constructor currently assigns the cache field by reference
(this.cache = other.cache), causing shared mutable state between original and
copy; change VolumeTO(VolumeTO other) to perform a deep copy of cache (e.g.
this.cache = other.cache == null ? null : new CacheTO(other.cache) or use
CacheTO.clone()/copyOf if available) and, if CacheTO has no copy API, add a copy
constructor or clone method in CacheTO to enable safe deep-copying;
alternatively, document/ensure CacheTO is immutable before keeping the shallow
copy.
header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java (1)

74-75: ⚠️ Potential issue | 🟡 Minor

代码中包含中文注释,应使用英文。

根据编码规范,代码中不应包含中文,包括注释。第 74 行的 @summary 创建云主机 应改为英文。

🔧 建议修改
-* `@summary` 创建云主机
+* `@summary` Create a VM instance

As per coding guidelines: "代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写"

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

In `@header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java` around
lines 74 - 75, Replace the Chinese Javadoc summary in APICreateVmInstanceMsg
(the `@summary` line currently "创建云主机") with an English equivalent, e.g.,
"@summary Create a VM instance" (or "Create a virtual machine"); update the
Javadoc comment for the APICreateVmInstanceMsg class to use correct English and
ensure spelling/grammar are correct.
♻️ Duplicate comments (1)
storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java (1)

610-623: ⚠️ Potential issue | 🟠 Major

缓存配置创建存在一致性与健壮性缺口,建议一起修复。

当前实现有三个点需要一起处理:

  1. Enum.valueOf(...) 直接解析 token,遇到空值/非法值会抛运行时异常;
  2. CachePoolTag 局部变量命名不符合 lowerCamelCase;
  3. cacheVO 在事务块外单独持久化,存在“卷已创建但缓存配置未落库”的部分成功窗口。
建议修复示例(合并命名、校验、原子提交)
-        VmLocalVolumeCacheVO cacheVO = null;
+        VmLocalVolumeCacheVO cacheVO = null;
         if (msg.getSystemTag(VolumeSystemTags.VOLUME_CACHE_ENABLE::isMatch) != null) {
             cacheVO = new VmLocalVolumeCacheVO();
             cacheVO.setUuid(Platform.getUuid());
             cacheVO.setVolumeUuid(vo.getUuid());
             cacheVO.setState(VmLocalVolumeCacheState.Uninstantiated);
             String cacheModeTag = msg.getSystemTag(VolumeSystemTags.VOLUME_CACHE_MODE::isMatch);
-            String CachePoolTag = msg.getSystemTag(VolumeSystemTags.VOLUME_CACHE_POOL_UUID::isMatch);
+            String cachePoolTag = msg.getSystemTag(VolumeSystemTags.VOLUME_CACHE_POOL_UUID::isMatch);
             if (cacheModeTag != null) {
-                cacheVO.setCacheMode(VmLocalVolumeCacheMode.valueOf(VolumeSystemTags.VOLUME_CACHE_MODE.getTokenByTag(cacheModeTag, VolumeSystemTags.CACHE_MODE_TOKEN)));
+                String mode = VolumeSystemTags.VOLUME_CACHE_MODE.getTokenByTag(cacheModeTag, VolumeSystemTags.CACHE_MODE_TOKEN);
+                if (StringUtils.isBlank(mode)) {
+                    throw new OperationFailureException(operr("cacheMode in system tag is empty"));
+                }
+                try {
+                    cacheVO.setCacheMode(VmLocalVolumeCacheMode.valueOf(mode));
+                } catch (IllegalArgumentException e) {
+                    throw new OperationFailureException(operr("invalid cacheMode[%s] in system tag", mode));
+                }
             }
-            if (CachePoolTag != null) {
-                cacheVO.setPoolUuid(VolumeSystemTags.VOLUME_CACHE_POOL_UUID.getTokenByTag(CachePoolTag, VolumeSystemTags.CACHE_POOL_UUID_TOKEN));
+            if (cachePoolTag != null) {
+                cacheVO.setPoolUuid(VolumeSystemTags.VOLUME_CACHE_POOL_UUID.getTokenByTag(cachePoolTag, VolumeSystemTags.CACHE_POOL_UUID_TOKEN));
             }
         }

+        VmLocalVolumeCacheVO finalCacheVO = cacheVO;
         VolumeVO finalVo = vo;
         vo = new SQLBatchWithReturn<VolumeVO>() {
             `@Override`
             protected VolumeVO scripts() {
                 dbf.getEntityManager().persist(finalVo);
+                if (finalCacheVO != null) {
+                    dbf.getEntityManager().persist(finalCacheVO);
+                }
                 dbf.getEntityManager().flush();
                 dbf.getEntityManager().refresh(finalVo);
                 return finalVo;
             }
         }.execute();

-        if (cacheVO != null) {
-            dbf.persist(cacheVO);
-        }
#!/bin/bash
set -euo pipefail

# 1) 审核当前 createVolume 中缓存创建与持久化位置
fd VolumeManagerImpl.java --exec sed -n '608,666p' {}

# 2) 查看缓存模式枚举定义,确认允许值
fd VmLocalVolumeCacheMode.java --exec sed -n '1,120p' {}

# 3) 查看相关系统标签 token 定义
fd VolumeSystemTags.java --exec sed -n '50,90p' {}

As per coding guidelines: “方法名、参数名、成员变量和局部变量使用 lowerCamelCase 风格。”

Also applies to: 643-651, 660-662

🤖 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/VolumeManagerImpl.java`
around lines 610 - 623, The cache creation code for VmLocalVolumeCacheVO is
brittle and misnamed: rename CachePoolTag to cachePoolTag, validate tokens
before calling VmLocalVolumeCacheMode.valueOf (e.g., check the result of
VolumeSystemTags.VOLUME_CACHE_MODE.getTokenByTag and ensure it matches an enum
constant or handle invalid/empty values gracefully) to avoid runtime exceptions,
and move the creation/persistence of cacheVO (VmLocalVolumeCacheVO) into the
same transaction/block that persists the volume so cache and volume are
atomically committed; update references to
VolumeSystemTags.VOLUME_CACHE_POOL_UUID and VolumeSystemTags.VOLUME_CACHE_MODE
and add explicit error handling/logging for invalid tokens instead of letting
valueOf throw.
🧹 Nitpick comments (7)
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityInventory.java (1)

30-36: 建议使用 Stream API 简化集合转换。

根据编码规范,建议使用 Java Stream 表达式代替传统循环。

♻️ 建议的重构方案
 public static List<VmLocalVolumeCachePoolCapacityInventory> valueOf(Collection<VmLocalVolumeCachePoolCapacityVO> vos) {
-    List<VmLocalVolumeCachePoolCapacityInventory> invs = new ArrayList<>();
-    for (VmLocalVolumeCachePoolCapacityVO vo : vos) {
-        invs.add(valueOf(vo));
-    }
-    return invs;
+    return vos.stream()
+            .map(VmLocalVolumeCachePoolCapacityInventory::valueOf)
+            .collect(Collectors.toList());
 }

需要添加导入:

import java.util.stream.Collectors;

根据编码规范:"使用 Java Stream 或 Lambda 表达式代替冗长的循环与条件判断"。

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

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityInventory.java`
around lines 30 - 36, Replace the manual loop in
VmLocalVolumeCachePoolCapacityInventory.valueOf(Collection<VmLocalVolumeCachePoolCapacityVO>)
with a Stream-based transformation: stream the incoming vos, map each
VmLocalVolumeCachePoolCapacityVO to the existing valueOf(vo) call, and collect
into a List using Collectors.toList(); also add the required import for
java.util.stream.Collectors. Ensure you keep the method signature and return
type unchanged and reuse the existing valueOf(VmLocalVolumeCachePoolCapacityVO)
mapper.
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheConstant.java (1)

1-1: 包名使用了驼峰命名,与编码规范不符。

根据编码规范,包名应统一使用小写(如 localvolumecache),但代码库中已存在类似的驼峰命名包(如 observabilityServer)。建议与团队确认是否需要保持一致性,或作为后续统一重构的一部分。

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

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheConstant.java`
at line 1, The package declaration in VmLocalVolumeCacheConstant.java uses
camelCase (org.zstack.header.localVolumeCache) which violates the lowercase
package naming convention; change the package to all lowercase (e.g.,
org.zstack.header.localvolumecache) by updating the package declaration in
VmLocalVolumeCacheConstant and moving the file to the matching lowercase
directory, then update all references/imports/usages of
org.zstack.header.localVolumeCache across the codebase (imports, build files,
module descriptors, and any reflective string references) to the new package
name to keep compilation and runtime behavior consistent; coordinate with the
team if you intend to align with existing camelCase packages like
observabilityServer or defer a global refactor.
conf/db/upgrade/V5.5.12__schema.sql (1)

4-6: 若长度不可控,metadata/installPath 应改为 TEXT

Line 6metadataLine 33installPath 当前为 VARCHAR(2048)。如果这两个字段在业务上没有严格上限,建议改为 TEXT,避免后续截断风险。

As per coding guidelines "数据库记录中,如果字符串长度不可控,不要用 vchar,用 text 类型".

Also applies to: 33-33

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

In `@conf/db/upgrade/V5.5.12__schema.sql` around lines 4 - 6, Change the two
columns currently defined as `VARCHAR(2048)` to `TEXT`: replace the `metadata`
column definition (`metadata VARCHAR(2048) DEFAULT NULL`) and the `installPath`
column definition (`installPath VARCHAR(2048) DEFAULT NULL`) in the V5.5.12
schema migration with `TEXT` (keeping DEFAULT NULL semantics if desired) so
their lengths are unbounded and won’t be truncated.
header/src/main/java/org/zstack/header/host/HostInventory.java (1)

239-245: 可使用 Stream API 简化转换逻辑

当前使用传统 for 循环进行 VO 到 Inventory 的转换,可以使用 Stream API 使代码更简洁,并与项目中其他类似转换保持一致。

♻️ 建议的修改
         if (vo.getLocalVolumeCachePools() != null) {
-            Set<VmLocalVolumeCachePoolInventory> cachePoolInventories = new HashSet<>();
-            for (VmLocalVolumeCachePoolVO cachePoolVO : vo.getLocalVolumeCachePools()) {
-                cachePoolInventories.add(cachePoolVO.toInventory());
-            }
-            this.setLocalVolumeCachePools(cachePoolInventories);
+            this.setLocalVolumeCachePools(vo.getLocalVolumeCachePools().stream()
+                    .map(VmLocalVolumeCachePoolVO::toInventory)
+                    .collect(Collectors.toSet()));
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@header/src/main/java/org/zstack/header/host/HostInventory.java` around lines
239 - 245, Replace the explicit loop that converts vo.getLocalVolumeCachePools()
into a Set of VmLocalVolumeCachePoolInventory with a Stream-based mapping and
collect; inside HostInventory convert the collection from
VmLocalVolumeCachePoolVO (use VmLocalVolumeCachePoolVO::toInventory) to a Set
and pass it to setLocalVolumeCachePools, ensuring you still check for null
before streaming.
header/src/main/java/org/zstack/header/host/HostVO.java (1)

45-48: @OneToMany 使用 FetchType.EAGER 可能存在性能隐患

@OneToMany 关联使用 FetchType.EAGER 会在每次加载 HostVO 时同时加载所有关联的 VmLocalVolumeCachePoolVO 记录。如果一个主机关联多个缓存池,可能导致笛卡尔积问题或 N+1 查询。

建议考虑使用 FetchType.LAZY,并在需要时通过显式查询或 JOIN FETCH 加载关联数据。

♻️ 建议的修改
-    `@OneToMany`(fetch = FetchType.EAGER)
+    `@OneToMany`(fetch = FetchType.LAZY)
     `@NotFound`(action = NotFoundAction.IGNORE)
     `@JoinColumn`(name = "hostUuid", insertable = false, updatable = false)
     private Set<VmLocalVolumeCachePoolVO> localVolumeCachePools;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@header/src/main/java/org/zstack/header/host/HostVO.java` around lines 45 -
48, The `@OneToMany` mapping on HostVO for localVolumeCachePools currently uses
FetchType.EAGER which can cause performance and N+1 issues; change the fetch
mode to FetchType.LAZY on the localVolumeCachePools association (the
Set<VmLocalVolumeCachePoolVO> field) and update call sites that currently rely
on eager loading to explicitly load the association via repository/DAO queries
or use JOIN FETCH in JPQL/Criteria when needed (or provide a dedicated loader
method) to avoid accidentally pulling all VmLocalVolumeCachePoolVO records on
every HostVO load.
plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java (2)

3754-3754: 建议消除流程数据键的魔法字符串

"virtualDeviceInfoList" 建议提取为类内常量,避免后续重构时键名漂移导致隐性错误。

As per coding guidelines "避免使用魔法值(Magic Value):直接使用未经定义的数值或字符串应替换为常量或枚举。"

Also applies to: 3771-3771

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

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java` at line 3754, The flow
data key "virtualDeviceInfoList" should be extracted to a class-level constant
to avoid magic strings and future refactor breakage: add a private static final
String constant (e.g. VIRTUAL_DEVICE_INFO_LIST = "virtualDeviceInfoList") in the
KVMHost class and replace the direct string usages in the flowData.put calls
(and the other occurrence noted) with that constant; keep the constant
visibility appropriate to KVMHost and use the constant name in both places to
ensure consistency.

718-739: 建议在块设备查询前增加主机状态快速校验

当前会直接发起 agent HTTP 调用。建议先做 checkStatus()(或等价校验),让离线场景更快失败并返回更明确错误,而不是走到网络超时路径。

♻️ 建议修改
 private void handle(GetBlockDevicesOnHostMsg msg) {
+    checkStatus();
     GetBlockDevicesOnHostReply reply = new GetBlockDevicesOnHostReply();
     KVMAgentCommands.GetBlockDevicesCmd cmd = new KVMAgentCommands.GetBlockDevicesCmd();
     new Http<>(getBlockDevicesPath, cmd, KVMAgentCommands.GetBlockDevicesRsp.class)
             .call(new ReturnValueCompletion<KVMAgentCommands.GetBlockDevicesRsp>(msg) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java` around lines 718 - 739,
In handle(GetBlockDevicesOnHostMsg msg) add a pre-check of host availability by
invoking the existing checkStatus() (or equivalent host status method) before
constructing and sending the Http agent call; if checkStatus() indicates the
host is offline/unavailable, populate GetBlockDevicesOnHostReply with an
appropriate ErrorCode (or operr message) and return via bus.reply(msg, reply)
immediately to avoid the HTTP call and long timeouts, otherwise proceed with the
current KVMAgentCommands.GetBlockDevicesCmd / Http call flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java`:
- Around line 170-181: In getSpecifiedCachePoolUuids(VmInstanceSpec spec) avoid
adding the root volume cache pool UUID unless root caching is enabled and
prevent NPEs from data disk entries: check the root-cache-enabled flag (e.g.
spec.isRootVolumeCacheEnabled() or the appropriate boolean on VmInstanceSpec)
before adding spec.getRootVolumeCachePoolUuid(), and when iterating
dataDiskCacheConfigs guard each value with a null check (ensure
dataDiskCacheConfig != null) before calling
dataDiskCacheConfig.getCachePoolUuid() and adding it to poolUuids.

In
`@header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsgDoc_zh_cn.groovy`:
- Around line 8-10: The file defines placeholder API doc metadata: update the
category and desc fields in APIGetBlockDevicesMsgDoc_zh_cn.groovy (the
'category' and 'desc' entries) to real values: set 'category' to the appropriate
Chinese module/group name and replace the "在这里填写API描述" text with a concise
Chinese description of what the API does, its purpose, parameters/outputs
summary and any important notes so generated documentation is informative.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStateDoc_zh_cn.groovy`:
- Line 7: The title field in VmLocalVolumeCachePoolStateDoc_zh_cn.groovy
currently contains a placeholder ("在这里输入结构的名称"); replace that placeholder value
for the `title` field with a meaningful Chinese description of the structure
(e.g., a concise name or phrase that describes the VM local volume cache pool
state), ensuring the `title` string accurately reflects the purpose of the
document and matches other localization conventions used in this module.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheStateDoc_zh_cn.groovy`:
- Line 7: The `title` field in VmLocalVolumeCacheStateDoc_zh_cn.groovy currently
contains the placeholder "在这里输入结构的名称"; replace this placeholder with a
meaningful Chinese title that describes the document/structure (e.g.,
"本地虚拟机卷缓存状态" or similar), updating the `title` value to a concise, accurate
description of the structure so the generated doc shows a proper header.

---

Outside diff comments:
In `@header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java`:
- Around line 74-75: Replace the Chinese Javadoc summary in
APICreateVmInstanceMsg (the `@summary` line currently "创建云主机") with an English
equivalent, e.g., "@summary Create a VM instance" (or "Create a virtual
machine"); update the Javadoc comment for the APICreateVmInstanceMsg class to
use correct English and ensure spelling/grammar are correct.

In `@plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java`:
- Around line 68-90: The VolumeTO copy constructor currently assigns the cache
field by reference (this.cache = other.cache), causing shared mutable state
between original and copy; change VolumeTO(VolumeTO other) to perform a deep
copy of cache (e.g. this.cache = other.cache == null ? null : new
CacheTO(other.cache) or use CacheTO.clone()/copyOf if available) and, if CacheTO
has no copy API, add a copy constructor or clone method in CacheTO to enable
safe deep-copying; alternatively, document/ensure CacheTO is immutable before
keeping the shallow copy.

---

Duplicate comments:
In `@storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java`:
- Around line 610-623: The cache creation code for VmLocalVolumeCacheVO is
brittle and misnamed: rename CachePoolTag to cachePoolTag, validate tokens
before calling VmLocalVolumeCacheMode.valueOf (e.g., check the result of
VolumeSystemTags.VOLUME_CACHE_MODE.getTokenByTag and ensure it matches an enum
constant or handle invalid/empty values gracefully) to avoid runtime exceptions,
and move the creation/persistence of cacheVO (VmLocalVolumeCacheVO) into the
same transaction/block that persists the volume so cache and volume are
atomically committed; update references to
VolumeSystemTags.VOLUME_CACHE_POOL_UUID and VolumeSystemTags.VOLUME_CACHE_MODE
and add explicit error handling/logging for invalid tokens instead of letting
valueOf throw.

---

Nitpick comments:
In `@conf/db/upgrade/V5.5.12__schema.sql`:
- Around line 4-6: Change the two columns currently defined as `VARCHAR(2048)`
to `TEXT`: replace the `metadata` column definition (`metadata VARCHAR(2048)
DEFAULT NULL`) and the `installPath` column definition (`installPath
VARCHAR(2048) DEFAULT NULL`) in the V5.5.12 schema migration with `TEXT`
(keeping DEFAULT NULL semantics if desired) so their lengths are unbounded and
won’t be truncated.

In `@header/src/main/java/org/zstack/header/host/HostInventory.java`:
- Around line 239-245: Replace the explicit loop that converts
vo.getLocalVolumeCachePools() into a Set of VmLocalVolumeCachePoolInventory with
a Stream-based mapping and collect; inside HostInventory convert the collection
from VmLocalVolumeCachePoolVO (use VmLocalVolumeCachePoolVO::toInventory) to a
Set and pass it to setLocalVolumeCachePools, ensuring you still check for null
before streaming.

In `@header/src/main/java/org/zstack/header/host/HostVO.java`:
- Around line 45-48: The `@OneToMany` mapping on HostVO for localVolumeCachePools
currently uses FetchType.EAGER which can cause performance and N+1 issues;
change the fetch mode to FetchType.LAZY on the localVolumeCachePools association
(the Set<VmLocalVolumeCachePoolVO> field) and update call sites that currently
rely on eager loading to explicitly load the association via repository/DAO
queries or use JOIN FETCH in JPQL/Criteria when needed (or provide a dedicated
loader method) to avoid accidentally pulling all VmLocalVolumeCachePoolVO
records on every HostVO load.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheConstant.java`:
- Line 1: The package declaration in VmLocalVolumeCacheConstant.java uses
camelCase (org.zstack.header.localVolumeCache) which violates the lowercase
package naming convention; change the package to all lowercase (e.g.,
org.zstack.header.localvolumecache) by updating the package declaration in
VmLocalVolumeCacheConstant and moving the file to the matching lowercase
directory, then update all references/imports/usages of
org.zstack.header.localVolumeCache across the codebase (imports, build files,
module descriptors, and any reflective string references) to the new package
name to keep compilation and runtime behavior consistent; coordinate with the
team if you intend to align with existing camelCase packages like
observabilityServer or defer a global refactor.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityInventory.java`:
- Around line 30-36: Replace the manual loop in
VmLocalVolumeCachePoolCapacityInventory.valueOf(Collection<VmLocalVolumeCachePoolCapacityVO>)
with a Stream-based transformation: stream the incoming vos, map each
VmLocalVolumeCachePoolCapacityVO to the existing valueOf(vo) call, and collect
into a List using Collectors.toList(); also add the required import for
java.util.stream.Collectors. Ensure you keep the method signature and return
type unchanged and reuse the existing valueOf(VmLocalVolumeCachePoolCapacityVO)
mapper.

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java`:
- Line 3754: The flow data key "virtualDeviceInfoList" should be extracted to a
class-level constant to avoid magic strings and future refactor breakage: add a
private static final String constant (e.g. VIRTUAL_DEVICE_INFO_LIST =
"virtualDeviceInfoList") in the KVMHost class and replace the direct string
usages in the flowData.put calls (and the other occurrence noted) with that
constant; keep the constant visibility appropriate to KVMHost and use the
constant name in both places to ensure consistency.
- Around line 718-739: In handle(GetBlockDevicesOnHostMsg msg) add a pre-check
of host availability by invoking the existing checkStatus() (or equivalent host
status method) before constructing and sending the Http agent call; if
checkStatus() indicates the host is offline/unavailable, populate
GetBlockDevicesOnHostReply with an appropriate ErrorCode (or operr message) and
return via bus.reply(msg, reply) immediately to avoid the HTTP call and long
timeouts, otherwise proceed with the current KVMAgentCommands.GetBlockDevicesCmd
/ Http call flow.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: http://open.zstack.ai:20001/code-reviews/zstack-cloud.yaml (via .coderabbit.yaml)

Review profile: CHILL

Plan: Pro

Run ID: 887ea241-3f35-4415-8bf7-3a1d6c2a18fd

📥 Commits

Reviewing files that changed from the base of the PR and between b31ef5b and e09ddd2.

⛔ Files ignored due to path filters (44)
  • conf/globalConfig/vmLocalVolumeCachePool.xml is excluded by !**/*.xml
  • conf/persistence.xml is excluded by !**/*.xml
  • conf/serviceConfig/host.xml is excluded by !**/*.xml
  • conf/serviceConfig/vmLocalVolumeCache.xml is excluded by !**/*.xml
  • conf/serviceConfig/vmLocalVolumeCachePool.xml is excluded by !**/*.xml
  • conf/springConfigXml/localVolumeCache.xml is excluded by !**/*.xml
  • conf/zstack.xml is excluded by !**/*.xml
  • sdk/src/main/java/SourceClassMap.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CacheTO.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ChangeVmLocalVolumeCachePoolStateAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ChangeVmLocalVolumeCachePoolStateResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateOvnControllerVmAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateVmInstanceAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DeleteVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DeleteVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DisableVolumeCacheAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DisableVolumeCacheResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/EnableVolumeCacheAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/EnableVolumeCacheResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ExtendVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ExtendVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/GetBlockDevicesAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/GetBlockDevicesResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/HostBlockDeviceStruct.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/HostInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/QueryVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/QueryVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ReconnectVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ReconnectVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/SyncVmLocalVolumeCachePoolCapacityAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/SyncVmLocalVolumeCachePoolCapacityResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/UpdateVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/UpdateVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheMode.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolState.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolStatus.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheState.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VolumeInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VolumeTO.java is excluded by !sdk/**
  • test/src/test/resources/springConfigXml/license.xml is excluded by !**/*.xml
📒 Files selected for processing (61)
  • compute/src/main/java/org/zstack/compute/host/HostBase.java
  • compute/src/main/java/org/zstack/compute/vm/InstantiateVmFromNewCreatedStruct.java
  • compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java
  • compute/src/main/java/org/zstack/compute/vm/VmAllocateVolumeFlow.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceManagerImpl.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceUtils.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstantiateOtherDiskFlow.java
  • conf/db/upgrade/V5.5.12__schema.sql
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEvent.java
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEventDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsg.java
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsgDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/GetBlockDevicesOnHostMsg.java
  • header/src/main/java/org/zstack/header/host/GetBlockDevicesOnHostReply.java
  • header/src/main/java/org/zstack/header/host/HostBlockDeviceStruct.java
  • header/src/main/java/org/zstack/header/host/HostBlockDeviceStructDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/HostInventory.java
  • header/src/main/java/org/zstack/header/host/HostVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/CachePoolMetadata.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheConstant.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventoryDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheMode.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheModeDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO_.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolState.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStateDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatus.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatusDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO_.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheState.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheStateDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO_.java
  • header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsgDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/vm/CreateVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/vm/InstantiateNewCreatedVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java
  • header/src/main/java/org/zstack/header/volume/VolumeAO.java
  • header/src/main/java/org/zstack/header/volume/VolumeInventory.java
  • plugin/kvm/src/main/java/org/zstack/kvm/CacheTO.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMApiInterceptor.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMAttachVolumeExtensionPoint.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMExtensionEmitter.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java
  • plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java
  • plugin/vhost/src/main/java/org/zstack/vhost/kvm/KvmVhostNodeServer.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeApiInterceptor.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeSystemTags.java
  • testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy
  • utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java
💤 Files with no reviewable changes (2)
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMApiInterceptor.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeApiInterceptor.java
🚧 Files skipped from review as they are similar to previous changes (23)
  • plugin/vhost/src/main/java/org/zstack/vhost/kvm/KvmVhostNodeServer.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheModeDoc_zh_cn.groovy
  • compute/src/main/java/org/zstack/compute/vm/VmInstantiateOtherDiskFlow.java
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEventDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheMode.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMAttachVolumeExtensionPoint.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventoryDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/volume/VolumeAO.java
  • header/src/main/java/org/zstack/header/volume/VolumeInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolState.java
  • header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsgDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/HostBlockDeviceStruct.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceUtils.java
  • header/src/main/java/org/zstack/header/vm/CreateVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO.java
  • header/src/main/java/org/zstack/header/host/HostBlockDeviceStructDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO_.java
  • header/src/main/java/org/zstack/header/localVolumeCache/CachePoolMetadata.java
  • header/src/main/java/org/zstack/header/host/GetBlockDevicesOnHostMsg.java
  • header/src/main/java/org/zstack/header/host/GetBlockDevicesOnHostReply.java

Comment thread compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java Outdated
Comment on lines +8 to +10
category "未知类别"

desc """在这里填写API描述"""

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

文档元信息仍为占位值,建议在合并前补全。

categorydesc 目前还是模板占位内容,生成后的 API 文档可读性会明显受影响。

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

In
`@header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsgDoc_zh_cn.groovy`
around lines 8 - 10, The file defines placeholder API doc metadata: update the
category and desc fields in APIGetBlockDevicesMsgDoc_zh_cn.groovy (the
'category' and 'desc' entries) to real values: set 'category' to the appropriate
Chinese module/group name and replace the "在这里填写API描述" text with a concise
Chinese description of what the API does, its purpose, parameters/outputs
summary and any important notes so generated documentation is informative.


doc {

title "在这里输入结构的名称"

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

标题为占位符模板文本,需要填写实际内容。

title 字段当前值为 "在这里输入结构的名称",这是一个未填写的模板占位符,应替换为实际的结构名称描述。

📝 建议修复
-	title "在这里输入结构的名称"
+	title "虚拟机本地卷缓存池状态"
📝 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
title "在这里输入结构的名称"
title "虚拟机本地卷缓存池状态"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStateDoc_zh_cn.groovy`
at line 7, The title field in VmLocalVolumeCachePoolStateDoc_zh_cn.groovy
currently contains a placeholder ("在这里输入结构的名称"); replace that placeholder value
for the `title` field with a meaningful Chinese description of the structure
(e.g., a concise name or phrase that describes the VM local volume cache pool
state), ensuring the `title` string accurately reflects the purpose of the
document and matches other localization conventions used in this module.


doc {

title "在这里输入结构的名称"

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

标题为占位符模板文本,需要填写实际内容。

title 字段当前值为 "在这里输入结构的名称",这是一个未填写的模板占位符,应替换为实际的结构名称描述。

📝 建议修复
-	title "在这里输入结构的名称"
+	title "虚拟机本地卷缓存状态"
📝 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
title "在这里输入结构的名称"
title "虚拟机本地卷缓存状态"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheStateDoc_zh_cn.groovy`
at line 7, The `title` field in VmLocalVolumeCacheStateDoc_zh_cn.groovy
currently contains the placeholder "在这里输入结构的名称"; replace this placeholder with a
meaningful Chinese title that describes the document/structure (e.g.,
"本地虚拟机卷缓存状态" or similar), updating the `title` value to a concise, accurate
description of the structure so the generated doc shows a proper header.

@MatheMatrix MatheMatrix force-pushed the sync/haidong.pang/feature/ZSTAC-81715@@3 branch from e09ddd2 to e4aaeaf Compare March 16, 2026 07:25

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

Caution

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

⚠️ Outside diff range comments (1)
plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java (1)

149-151: ⚠️ Potential issue | 🟠 Major

withExtension=false 时会丢失 cache 字段赋值

Line 149-151 在扩展前直接返回,导致 Line 160-164 的 cache 映射永远不会执行;valueOfWithOutExtension(...) 返回的 VolumeTO 将缺少本地卷缓存信息。

🔧 建议修复
-        if (!withExtension) {
-            return to;
-        }
+        VmLocalVolumeCacheInventory cacheInv = vol.getLocalVolumeCache();
+        if (cacheInv != null) {
+            to.setCache(CacheTO.valueOf(cacheInv));
+        }
+
+        if (!withExtension) {
+            return to;
+        }

         if (exts == null) {
             prepareExts();
         }
         for (KVMConvertVolumeExtensionPoint ext : exts) {
             to = ext.convertVolumeIfNeed(host, vol, to);
         }
-
-        VmLocalVolumeCacheInventory cacheInv = vol.getLocalVolumeCache();
-        if (cacheInv != null){
-            CacheTO cacheTO = CacheTO.valueOf(cacheInv);
-            to.setCache(cacheTO);
-        }

Also applies to: 160-164

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

In `@plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java` around lines 149 -
151, 在 valueOfWithOutExtension(...) 中,当 withExtension 为 false 时当前直接返回 to 导致后续对
to.cache 的映射(原在 160-164 的逻辑)不会执行,从而丢失本地卷缓存信息;请在返回前确保对 VolumeTO 实例的 cache
字段完成赋值(将原本针对扩展路径的 cache 映射逻辑复用或抽取到统一位置),或将 cache 映射移动到 to
返回之前执行,涉及符号:withExtension、valueOfWithOutExtension、VolumeTO、cache。
♻️ Duplicate comments (5)
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheStateDoc_zh_cn.groovy (1)

7-7: ⚠️ Potential issue | 🟡 Minor

占位符标题仍未替换。

title 仍是模板占位符,生成文档会显示无意义标题,建议改为实际结构名。

📝 建议修复
-	title "在这里输入结构的名称"
+	title "虚拟机本地卷缓存状态"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheStateDoc_zh_cn.groovy`
at line 7, Replace the placeholder title string in
VmLocalVolumeCacheStateDoc_zh_cn.groovy with the actual structure name; update
the title call (title "在这里输入结构的名称") to a meaningful label such as the
structure's name (e.g., "VmLocalVolumeCacheState" or a localized Chinese
equivalent like "Vm 本地卷缓存状态") so generated docs show the correct heading for
VmLocalVolumeCacheState.
compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java (1)

170-181: ⚠️ Potential issue | 🟠 Major

root pool 约束仍然加得过早,而且 data-disk config 还缺空值保护。

根盘缓存关闭时,spec.getRootVolumeCachePoolUuid() 仍会被塞进 allocator tags,白白收窄可选 host;另外 dataDiskCacheConfigs 的 value 可能为 null,这里直接 getCachePoolUuid() 会 NPE。

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

In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java` around
lines 170 - 181, The method getSpecifiedCachePoolUuids adds a root cache UUID
too early and risks NPEs for data-disk configs; only include
spec.getRootVolumeCachePoolUuid() when the root-volume cache is actually enabled
(use the appropriate flag on VmInstanceSpec, e.g.,
isRootVolumeCacheEnabled()/isRootCacheEnabled() or similar) and add a null check
for each dataDiskCacheConfig before calling getCachePoolUuid() (i.e., skip null
values and only add non-null getCachePoolUuid() results from
APICreateVmInstanceMsg.VolumeCacheConfig).
compute/src/main/java/org/zstack/compute/vm/VmInstantiateOtherDiskFlow.java (1)

421-433: ⚠️ Potential issue | 🔴 Critical

不要在已附加现有卷后再新增会失败的缓存建档步骤。

Line 108-111 的分支会把 volumeInventory 指向已有数据卷。这里如果 initCacheRecord() 因枚举解析或落库失败而中断,外层 rollback() 仍会对这个 volumeInventoryDeleteVolumeMsg,把用户原有卷当成“本流程新建卷”删掉。至少要记录卷是否由当前流程创建,或把缓存记录创建前移到 attach 之前。

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

In `@compute/src/main/java/org/zstack/compute/vm/VmInstantiateOtherDiskFlow.java`
around lines 421 - 433, 当前流程在 VmInstantiateOtherDiskFlow
的缓存建档步骤(initCacheRecord())发生在 attach 之后且没有区分 volumeInventory
是否为本流程新建,会在建档失败时触发外层 rollback()
的删除逻辑误删用户已有卷。修复方法:在处理开启缓存的分支中使用明确的“本流程创建”标志(例如在创建新卷路径设置 createdByThisFlow=true
并传递到 volumeInventory),或者干脆将缓存记录创建 (initCacheRecord()) 前置到 attach 之前;另外在
rollback() 或删除逻辑里只对标记为 createdByThisFlow 的 volumeInventory 执行
DeleteVolumeMsg。确保修改涉及方法/变量包括 VmInstantiateOtherDiskFlow, initCacheRecord(),
volumeInventory, rollback()(或相关删除逻辑)并相应传递/检查 createdByThisFlow 标志以避免删除已有卷。
header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java (1)

241-243: ⚠️ Potential issue | 🟠 Major

磁盘级 cacheMode 缺少与顶层一致的取值约束

Line 241-243 和 Line 366-384 的 cacheMode 没有像顶层字段那样限制为 WriteBack,会导致同一 API 在不同输入路径上的校验强度不一致。

🔧 建议修复
     public static class DiskAO {
         private Boolean enableCache;
         private String cachePoolUuid;
+        `@APIParam`(required = false, validValues = {"WriteBack"})
         private String cacheMode;
@@
     public static class VolumeCacheConfig {
         private String cachePoolUuid;
+        `@APIParam`(required = false, validValues = {"WriteBack"})
         private String cacheMode;

Also applies to: 366-384

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

In `@header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java` around
lines 241 - 243, The disk-level cacheMode field (the private String cacheMode
declared alongside enableCache and cachePoolUuid and the same-named field in the
later block around the disk-specific settings) must enforce the same allowed
values as the top-level cacheMode (e.g., "WriteBack"); update the disk-level
declarations to use the same enum/type or add the same validation/annotation
used at the top level (or add a check in the class's validation method) so that
disk-level cacheMode is restricted to the identical set of values as the
top-level cacheMode.
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java (1)

22-36: ⚠️ Potential issue | 🟠 Major

@Queryable 关联列配置错误,容量查询关联会失效

Line 22-36 当前把 referencedColumnName 指向容量值字段(totalCapacity/availableCapacity/allocated/dirty),而不是关联键 uuid。这会导致关联语义错误,查询行为不稳定或直接失败。

🔧 建议修复
     `@Queryable`(mappingClass = VmLocalVolumeCachePoolCapacityInventory.class,
-            joinColumn = `@JoinColumn`(name = "uuid", referencedColumnName = "totalCapacity"))
+            joinColumn = `@JoinColumn`(name = "uuid", referencedColumnName = "uuid"))
     private long totalCapacity;

     `@Queryable`(mappingClass = VmLocalVolumeCachePoolCapacityInventory.class,
-            joinColumn = `@JoinColumn`(name = "uuid", referencedColumnName = "availableCapacity"))
+            joinColumn = `@JoinColumn`(name = "uuid", referencedColumnName = "uuid"))
     private long availableCapacity;

     `@Queryable`(mappingClass = VmLocalVolumeCachePoolCapacityInventory.class,
-            joinColumn = `@JoinColumn`(name = "uuid", referencedColumnName = "allocated"))
+            joinColumn = `@JoinColumn`(name = "uuid", referencedColumnName = "uuid"))
     private long allocated;

     `@Queryable`(mappingClass = VmLocalVolumeCachePoolCapacityInventory.class,
-            joinColumn = `@JoinColumn`(name = "uuid", referencedColumnName = "dirty"))
+            joinColumn = `@JoinColumn`(name = "uuid", referencedColumnName = "uuid"))
     private long dirty;

可先用下面脚本只读核对当前配置与目标类字段:

#!/bin/bash
set -euo pipefail

rg -n -C2 '@Queryable|@JoinColumn|totalCapacity|availableCapacity|allocated|dirty' \
  header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java

rg -n -C2 'class VmLocalVolumeCachePoolCapacityInventory|uuid|totalCapacity|availableCapacity|allocated|dirty' \
  header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityInventory.java
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java`
around lines 22 - 36, The `@Queryable` join is incorrectly referencing capacity
value fields instead of the capacity inventory's key; update the JoinColumn
referencedColumnName for the four fields (totalCapacity, availableCapacity,
allocated, dirty) so they reference the UUID key in the mapped class: change
referencedColumnName from totalCapacity/availableCapacity/allocated/dirty to
"uuid" for each `@Queryable` that uses mappingClass =
VmLocalVolumeCachePoolCapacityInventory to restore correct join semantics
between VmLocalVolumeCachePoolInventory and
VmLocalVolumeCachePoolCapacityInventory.
🧹 Nitpick comments (3)
utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java (1)

15875-15943: 建议为错误码添加语义化命名或文档说明

新增的 35 个错误码常量(10001-10035)使用了纯数字后缀,虽然符合现有文件的命名模式,但无法直观表达每个错误码的具体含义。根据编码规范中关于常量命名的要求("要求表达清楚,避免使用含糊或不准确的名称"),建议:

  1. 考虑使用语义化后缀,例如:ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_POOL_NOT_FOUND
  2. 或在相关的错误码定义处(如 ErrorCodeDoc 或对应的 .xml 文件)添加详细说明

这有助于后续维护和调试时快速理解错误含义。

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

In
`@utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java`
around lines 15875 - 15943, The 35 new constants in CloudOperationsErrorCode
(ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_10001 through
ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_10035) are numeric-only and lack semantic
meaning; rename each constant to a descriptive identifier (e.g.,
ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_POOL_NOT_FOUND) or add matching
documentation entries that map each numeric constant to a clear description
(update the ErrorCodeDoc/.xml or adjacent docs) so maintainers can immediately
understand each error; modify the constant names in CloudOperationsErrorCode and
ensure any references to these symbols in code/tests are updated to the new
names or that the documentation mapping is added and linked where error codes
are consumed.
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO.java (1)

28-31: 注释中的模块归属描述不准确,建议修正。

这里提到 VmLocalVolumeCachePoolVO in kvm plugin,会误导后续维护;建议改成与当前代码结构一致的表述。

📝 建议修复
-    /**
-     * Nullable: null means this cache entry has not been assigned to a pool yet.
-     * References VmLocalVolumeCachePoolVO in kvm plugin — no `@ForeignKey` to avoid cross-module dependency.
-     */
+    /**
+     * Nullable: null means this cache entry has not been assigned to a pool yet.
+     * Pool relation is intentionally kept as plain uuid (no DB foreign key) by current design.
+     */

As per coding guidelines 注释需要“仔细校对并随代码更新,确保内容正确”。

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

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO.java`
around lines 28 - 31, The class comment for VmLocalVolumeCacheVO incorrectly
states "VmLocalVolumeCachePoolVO in kvm plugin" which is misleading; update the
Javadoc for VmLocalVolumeCacheVO to accurately reflect the current module
ownership (e.g., reference VmLocalVolumeCachePoolVO in the localVolumeCache
module or simply "in the same module") and keep the note about avoiding
`@ForeignKey` to prevent cross-module dependency, so the comment matches the code
structure (update the comment block above the VmLocalVolumeCacheVO declaration).
plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java (1)

70-72: 复制构造函数对 cache 使用浅拷贝有共享可变对象风险

Line 91 直接复用 other.cache,后续任一实例修改 cache 都会影响另一个实例。建议做深拷贝以避免隐式副作用。

♻️ 建议修复
-        this.cache = other.cache;
+        if (other.cache != null) {
+            CacheTO copiedCache = new CacheTO();
+            BeanUtils.copyProperties(other.cache, copiedCache);
+            this.cache = copiedCache;
+        }

Also applies to: 91-91

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

In `@plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java` around lines 70 - 72,
The copy-constructor VolumeTO(VolumeTO other) currently assigns this.cache =
other.cache which causes shared mutable state; change it to create a new Map
instance and copy the entries (e.g., new HashMap<>(other.cache)) so the new
VolumeTO has its own cache; if cache values themselves are mutable, perform a
deeper copy of each entry’s value when copying to avoid implicit shared
mutations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java`:
- Around line 146-195: The current cache accounting in getTotalCacheSize and
getSpecifiedCachePoolUuids omits APICreateVmInstanceMsg.DiskAO entries that have
caching enabled; update getTotalCacheSize to iterate spec.getDiskAOs() and for
each DiskAO with enableCache/ isEnableCache() true add its size (use getSize()
or diskSize equivalent) to totalCacheSize, and update getSpecifiedCachePoolUuids
to include any DiskAO.getCachePoolUuid() when enableCache is true; ensure these
changes are referenced from addCacheSystemTags (which calls both methods) so
hosts are filtered by total requiredSize and poolUuid before scheduling.

In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateVolumeFlow.java`:
- Around line 134-145: The current block in VmAllocateVolumeFlow appends
volumeCache::* tags to the existing tags list (which already contains
vspec.getTags(), spec.getRootVolumeSystemTags(),
spec.getDataVolumeSystemTags()), causing duplicate/conflicting volumeCache
entries; before adding new tags, normalize by removing any existing tags that
start with the "volumeCache::" prefix from the tags collection and then add the
new "volumeCache::enable", "volumeCache::cacheMode::" + vspec.getCacheMode(),
and optional "volumeCache::poolUuid::" + vspec.getCachePoolUuid(); locate the
code around vspec.getEnableVolumeCache() / vspec.getCacheMode() in
VmAllocateVolumeFlow and perform the prefix-based removal on the tags list (or
alternatively throw an error if mixing existing volumeCache tags is
unacceptable).

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityInventory.java`:
- Around line 20-35: The two factory methods
valueOf(VmLocalVolumeCachePoolCapacityVO vo) and
valueOf(Collection<VmLocalVolumeCachePoolCapacityVO> vos) lack null checks and
can throw NPEs; add protection so valueOf(vo) immediately returns null if vo is
null, and change valueOf(vos) to return an empty List when vos is null and to
skip any null elements inside the loop (calling valueOf(vo) only for non-null
vo), keeping the same return types and using the existing method names to locate
the fixes.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy`:
- Around line 54-77: The ref blocks for "state", "status", and "caches" in
VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy currently set desc to the
literal string "null"; update each ref (the entries with name "state", "status",
and "caches" that reference VmLocalVolumeCachePoolState,
VmLocalVolumeCachePoolStatus, and VmLocalVolumeCacheInventory) to use an empty
string (desc "") or provide an actual descriptive text in Chinese instead of
"null" so the generated documentation does not render the word "null".
- Line 10: Replace the placeholder title string in
VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy ("在这里输入结构的名称") with the actual
structure name used by this inventory documentation (e.g.,
"VmLocalVolumeCachePoolInventory"); update the title line so the generated
documentation header is meaningful and matches the class/structure name
referenced in the file.

In `@header/src/main/java/org/zstack/header/volume/VolumeAO.java`:
- Around line 91-94: Change the localVolumeCache association from eager to lazy
loading: update the `@OneToOne`(fetch = FetchType.EAGER) on the localVolumeCache
field in VolumeAO (the private VmLocalVolumeCacheVO localVolumeCache) to use
FetchType.LAZY so the VmLocalVolumeCacheVO is not auto-fetched on every VolumeVO
query; ensure any code that needs it (e.g., VolumeInventory.valueOf())
explicitly initializes or fetches the association when converting to
VolumeInventory to avoid LazyInitializationException.

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java`:
- Around line 725-726: 当前分支直接用匿名 operr(...) 返回错误,需改为与本文件其余 KVM
错误路径一致,提供稳定的错误码并携带 host 上下文。请在 KVMHost 相关错误码集合中新增一个常量(例如
ORG_ZSTACK_KVM_OPERATION_ERROR 或类似命名),并在处理 rsp
不成功的地方用该稳定错误码构造错误(用与其它分支相同的错误构造函数/格式),同时把 host 相关信息(如 host.getUuid() 或
host.getManagementIp())拼接到错误详情里,最后用 reply.setError(...)
返回新的带码带上下文的错误;参考文件中其他失败分支的实现以保持一致性。

---

Outside diff comments:
In `@plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java`:
- Around line 149-151: 在 valueOfWithOutExtension(...) 中,当 withExtension 为 false
时当前直接返回 to 导致后续对 to.cache 的映射(原在 160-164 的逻辑)不会执行,从而丢失本地卷缓存信息;请在返回前确保对 VolumeTO
实例的 cache 字段完成赋值(将原本针对扩展路径的 cache 映射逻辑复用或抽取到统一位置),或将 cache 映射移动到 to
返回之前执行,涉及符号:withExtension、valueOfWithOutExtension、VolumeTO、cache。

---

Duplicate comments:
In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java`:
- Around line 170-181: The method getSpecifiedCachePoolUuids adds a root cache
UUID too early and risks NPEs for data-disk configs; only include
spec.getRootVolumeCachePoolUuid() when the root-volume cache is actually enabled
(use the appropriate flag on VmInstanceSpec, e.g.,
isRootVolumeCacheEnabled()/isRootCacheEnabled() or similar) and add a null check
for each dataDiskCacheConfig before calling getCachePoolUuid() (i.e., skip null
values and only add non-null getCachePoolUuid() results from
APICreateVmInstanceMsg.VolumeCacheConfig).

In `@compute/src/main/java/org/zstack/compute/vm/VmInstantiateOtherDiskFlow.java`:
- Around line 421-433: 当前流程在 VmInstantiateOtherDiskFlow
的缓存建档步骤(initCacheRecord())发生在 attach 之后且没有区分 volumeInventory
是否为本流程新建,会在建档失败时触发外层 rollback()
的删除逻辑误删用户已有卷。修复方法:在处理开启缓存的分支中使用明确的“本流程创建”标志(例如在创建新卷路径设置 createdByThisFlow=true
并传递到 volumeInventory),或者干脆将缓存记录创建 (initCacheRecord()) 前置到 attach 之前;另外在
rollback() 或删除逻辑里只对标记为 createdByThisFlow 的 volumeInventory 执行
DeleteVolumeMsg。确保修改涉及方法/变量包括 VmInstantiateOtherDiskFlow, initCacheRecord(),
volumeInventory, rollback()(或相关删除逻辑)并相应传递/检查 createdByThisFlow 标志以避免删除已有卷。

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java`:
- Around line 22-36: The `@Queryable` join is incorrectly referencing capacity
value fields instead of the capacity inventory's key; update the JoinColumn
referencedColumnName for the four fields (totalCapacity, availableCapacity,
allocated, dirty) so they reference the UUID key in the mapped class: change
referencedColumnName from totalCapacity/availableCapacity/allocated/dirty to
"uuid" for each `@Queryable` that uses mappingClass =
VmLocalVolumeCachePoolCapacityInventory to restore correct join semantics
between VmLocalVolumeCachePoolInventory and
VmLocalVolumeCachePoolCapacityInventory.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheStateDoc_zh_cn.groovy`:
- Line 7: Replace the placeholder title string in
VmLocalVolumeCacheStateDoc_zh_cn.groovy with the actual structure name; update
the title call (title "在这里输入结构的名称") to a meaningful label such as the
structure's name (e.g., "VmLocalVolumeCacheState" or a localized Chinese
equivalent like "Vm 本地卷缓存状态") so generated docs show the correct heading for
VmLocalVolumeCacheState.

In `@header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java`:
- Around line 241-243: The disk-level cacheMode field (the private String
cacheMode declared alongside enableCache and cachePoolUuid and the same-named
field in the later block around the disk-specific settings) must enforce the
same allowed values as the top-level cacheMode (e.g., "WriteBack"); update the
disk-level declarations to use the same enum/type or add the same
validation/annotation used at the top level (or add a check in the class's
validation method) so that disk-level cacheMode is restricted to the identical
set of values as the top-level cacheMode.

---

Nitpick comments:
In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO.java`:
- Around line 28-31: The class comment for VmLocalVolumeCacheVO incorrectly
states "VmLocalVolumeCachePoolVO in kvm plugin" which is misleading; update the
Javadoc for VmLocalVolumeCacheVO to accurately reflect the current module
ownership (e.g., reference VmLocalVolumeCachePoolVO in the localVolumeCache
module or simply "in the same module") and keep the note about avoiding
`@ForeignKey` to prevent cross-module dependency, so the comment matches the code
structure (update the comment block above the VmLocalVolumeCacheVO declaration).

In `@plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java`:
- Around line 70-72: The copy-constructor VolumeTO(VolumeTO other) currently
assigns this.cache = other.cache which causes shared mutable state; change it to
create a new Map instance and copy the entries (e.g., new
HashMap<>(other.cache)) so the new VolumeTO has its own cache; if cache values
themselves are mutable, perform a deeper copy of each entry’s value when copying
to avoid implicit shared mutations.

In
`@utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java`:
- Around line 15875-15943: The 35 new constants in CloudOperationsErrorCode
(ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_10001 through
ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_10035) are numeric-only and lack semantic
meaning; rename each constant to a descriptive identifier (e.g.,
ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_POOL_NOT_FOUND) or add matching
documentation entries that map each numeric constant to a clear description
(update the ErrorCodeDoc/.xml or adjacent docs) so maintainers can immediately
understand each error; modify the constant names in CloudOperationsErrorCode and
ensure any references to these symbols in code/tests are updated to the new
names or that the documentation mapping is added and linked where error codes
are consumed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: http://open.zstack.ai:20001/code-reviews/zstack-cloud.yaml (via .coderabbit.yaml)

Review profile: CHILL

Plan: Pro

Run ID: 66ea3d01-c778-4d2f-bdb0-89d7f704e3cd

📥 Commits

Reviewing files that changed from the base of the PR and between e09ddd2 and e4aaeaf.

⛔ Files ignored due to path filters (44)
  • conf/globalConfig/vmLocalVolumeCachePool.xml is excluded by !**/*.xml
  • conf/persistence.xml is excluded by !**/*.xml
  • conf/serviceConfig/host.xml is excluded by !**/*.xml
  • conf/serviceConfig/vmLocalVolumeCache.xml is excluded by !**/*.xml
  • conf/serviceConfig/vmLocalVolumeCachePool.xml is excluded by !**/*.xml
  • conf/springConfigXml/localVolumeCache.xml is excluded by !**/*.xml
  • conf/zstack.xml is excluded by !**/*.xml
  • sdk/src/main/java/SourceClassMap.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CacheTO.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ChangeVmLocalVolumeCachePoolStateAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ChangeVmLocalVolumeCachePoolStateResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateOvnControllerVmAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateVmInstanceAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DeleteVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DeleteVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DisableVolumeCacheAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DisableVolumeCacheResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/EnableVolumeCacheAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/EnableVolumeCacheResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ExtendVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ExtendVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/GetBlockDevicesAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/GetBlockDevicesResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/HostBlockDeviceStruct.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/HostInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/QueryVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/QueryVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ReconnectVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ReconnectVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/SyncVmLocalVolumeCachePoolCapacityAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/SyncVmLocalVolumeCachePoolCapacityResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/UpdateVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/UpdateVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheMode.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolState.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolStatus.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheState.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VolumeInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VolumeTO.java is excluded by !sdk/**
  • test/src/test/resources/springConfigXml/license.xml is excluded by !**/*.xml
📒 Files selected for processing (61)
  • compute/src/main/java/org/zstack/compute/host/HostBase.java
  • compute/src/main/java/org/zstack/compute/vm/InstantiateVmFromNewCreatedStruct.java
  • compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java
  • compute/src/main/java/org/zstack/compute/vm/VmAllocateVolumeFlow.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceManagerImpl.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceUtils.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstantiateOtherDiskFlow.java
  • conf/db/upgrade/V5.5.12__schema.sql
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEvent.java
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEventDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsg.java
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsgDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/GetBlockDevicesOnHostMsg.java
  • header/src/main/java/org/zstack/header/host/GetBlockDevicesOnHostReply.java
  • header/src/main/java/org/zstack/header/host/HostBlockDeviceStruct.java
  • header/src/main/java/org/zstack/header/host/HostBlockDeviceStructDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/HostInventory.java
  • header/src/main/java/org/zstack/header/host/HostVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/CachePoolMetadata.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheConstant.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventoryDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheMode.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheModeDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO_.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolState.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStateDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatus.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatusDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO_.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheState.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheStateDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO_.java
  • header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsgDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/vm/CreateVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/vm/InstantiateNewCreatedVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java
  • header/src/main/java/org/zstack/header/volume/VolumeAO.java
  • header/src/main/java/org/zstack/header/volume/VolumeInventory.java
  • plugin/kvm/src/main/java/org/zstack/kvm/CacheTO.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMApiInterceptor.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMAttachVolumeExtensionPoint.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMExtensionEmitter.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java
  • plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java
  • plugin/vhost/src/main/java/org/zstack/vhost/kvm/KvmVhostNodeServer.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeApiInterceptor.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeSystemTags.java
  • testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy
  • utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java
💤 Files with no reviewable changes (2)
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMApiInterceptor.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeApiInterceptor.java
✅ Files skipped from review due to trivial changes (1)
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO.java
🚧 Files skipped from review as they are similar to previous changes (23)
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMExtensionEmitter.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatus.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheState.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheModeDoc_zh_cn.groovy
  • storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatusDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsgDoc_zh_cn.groovy
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceManagerImpl.java
  • plugin/vhost/src/main/java/org/zstack/vhost/kvm/KvmVhostNodeServer.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO_.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheMode.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO_.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStateDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventoryDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEvent.java
  • header/src/main/java/org/zstack/header/host/HostBlockDeviceStruct.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventory.java
  • header/src/main/java/org/zstack/header/host/GetBlockDevicesOnHostMsg.java
  • plugin/kvm/src/main/java/org/zstack/kvm/CacheTO.java
  • header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsgDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsg.java
  • header/src/main/java/org/zstack/header/vm/InstantiateNewCreatedVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java

Comment thread compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java Outdated
Comment on lines +134 to +145
if (vspec.getEnableVolumeCache()) {
tags.add("volumeCache::enable");
if (vspec.getCacheMode() == null || vspec.getCacheMode().isEmpty()) {
throw new CloudRuntimeException(String.format(
"cacheMode cannot be empty when volume cache is enabled, vmUuid:%s, volumeType:%s",
spec.getVmInventory().getUuid(), vspec.getType()));
}
tags.add("volumeCache::cacheMode::" + vspec.getCacheMode());
if(vspec.getCachePoolUuid() != null) {
tags.add("volumeCache::poolUuid::" + vspec.getCachePoolUuid());
}
}

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 | 🟠 Major

先归一化已有的 volumeCache::* tags,再写入新值。

tags 前面已经合并了 vspec.getTags()spec.getRootVolumeSystemTags()spec.getDataVolumeSystemTags()。这里直接追加缓存 tags,会让调用方传入的旧 volumeCache::cacheMode::* / volumeCache::poolUuid::* 与新配置并存到同一个 volume 上,后续读取端拿到多个同类 tag 时就没有确定的生效值了。建议先移除同前缀 tag,或直接拒绝混用这两套入口。

💡 建议修改
             } else {
                 continue;
             }
+            tags.removeIf(tag -> tag.equals("volumeCache::enable")
+                    || tag.startsWith("volumeCache::cacheMode::")
+                    || tag.startsWith("volumeCache::poolUuid::"));
             if (vspec.getEnableVolumeCache()) {
                 tags.add("volumeCache::enable");
                 if (vspec.getCacheMode() == null || vspec.getCacheMode().isEmpty()) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateVolumeFlow.java` around
lines 134 - 145, The current block in VmAllocateVolumeFlow appends
volumeCache::* tags to the existing tags list (which already contains
vspec.getTags(), spec.getRootVolumeSystemTags(),
spec.getDataVolumeSystemTags()), causing duplicate/conflicting volumeCache
entries; before adding new tags, normalize by removing any existing tags that
start with the "volumeCache::" prefix from the tags collection and then add the
new "volumeCache::enable", "volumeCache::cacheMode::" + vspec.getCacheMode(),
and optional "volumeCache::poolUuid::" + vspec.getCachePoolUuid(); locate the
code around vspec.getEnableVolumeCache() / vspec.getCacheMode() in
VmAllocateVolumeFlow and perform the prefix-based removal on the tags list (or
alternatively throw an error if mixing existing volumeCache tags is
unacceptable).


doc {

title "在这里输入结构的名称"

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

标题仍是模板占位符,需替换为真实结构名称。

当前标题会让生成文档头部不可用。

📝 建议修复
-	title "在这里输入结构的名称"
+	title "虚拟机本地卷缓存池清单"
📝 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
title "在这里输入结构的名称"
title "虚拟机本地卷缓存池清单"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy`
at line 10, Replace the placeholder title string in
VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy ("在这里输入结构的名称") with the actual
structure name used by this inventory documentation (e.g.,
"VmLocalVolumeCachePoolInventory"); update the title line so the generated
documentation header is meaningful and matches the class/structure name
referenced in the file.

Comment on lines +54 to +77
ref {
name "state"
path "org.zstack.header.localVolumeCache.VmLocalVolumeCachePoolInventory.state"
desc "null"
type "VmLocalVolumeCachePoolState"
since "5.5.6"
clz VmLocalVolumeCachePoolState.class
}
ref {
name "status"
path "org.zstack.header.localVolumeCache.VmLocalVolumeCachePoolInventory.status"
desc "null"
type "VmLocalVolumeCachePoolStatus"
since "5.5.6"
clz VmLocalVolumeCachePoolStatus.class
}
ref {
name "caches"
path "org.zstack.header.localVolumeCache.VmLocalVolumeCachePoolInventory.caches"
desc "null"
type "Set"
since "5.5.6"
clz VmLocalVolumeCacheInventory.class
}

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

ref.desc 不应写成字符串 "null"

建议改为空字符串或实际说明,避免文档渲染出无效文本。

📝 建议修复
 	ref {
 		name "state"
 		path "org.zstack.header.localVolumeCache.VmLocalVolumeCachePoolInventory.state"
-		desc "null"
+		desc "缓存池状态"
 		type "VmLocalVolumeCachePoolState"
 		since "5.5.6"
 		clz VmLocalVolumeCachePoolState.class
 	}
 	ref {
 		name "status"
 		path "org.zstack.header.localVolumeCache.VmLocalVolumeCachePoolInventory.status"
-		desc "null"
+		desc "缓存池运行状态"
 		type "VmLocalVolumeCachePoolStatus"
 		since "5.5.6"
 		clz VmLocalVolumeCachePoolStatus.class
 	}
 	ref {
 		name "caches"
 		path "org.zstack.header.localVolumeCache.VmLocalVolumeCachePoolInventory.caches"
-		desc "null"
+		desc "缓存条目集合"
 		type "Set"
 		since "5.5.6"
 		clz VmLocalVolumeCacheInventory.class
 	}
📝 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
ref {
name "state"
path "org.zstack.header.localVolumeCache.VmLocalVolumeCachePoolInventory.state"
desc "null"
type "VmLocalVolumeCachePoolState"
since "5.5.6"
clz VmLocalVolumeCachePoolState.class
}
ref {
name "status"
path "org.zstack.header.localVolumeCache.VmLocalVolumeCachePoolInventory.status"
desc "null"
type "VmLocalVolumeCachePoolStatus"
since "5.5.6"
clz VmLocalVolumeCachePoolStatus.class
}
ref {
name "caches"
path "org.zstack.header.localVolumeCache.VmLocalVolumeCachePoolInventory.caches"
desc "null"
type "Set"
since "5.5.6"
clz VmLocalVolumeCacheInventory.class
}
ref {
name "state"
path "org.zstack.header.localVolumeCache.VmLocalVolumeCachePoolInventory.state"
desc "Cache pool state"
type "VmLocalVolumeCachePoolState"
since "5.5.6"
clz VmLocalVolumeCachePoolState.class
}
ref {
name "status"
path "org.zstack.header.localVolumeCache.VmLocalVolumeCachePoolInventory.status"
desc "Cache pool running status"
type "VmLocalVolumeCachePoolStatus"
since "5.5.6"
clz VmLocalVolumeCachePoolStatus.class
}
ref {
name "caches"
path "org.zstack.header.localVolumeCache.VmLocalVolumeCachePoolInventory.caches"
desc "Cache entries collection"
type "Set"
since "5.5.6"
clz VmLocalVolumeCacheInventory.class
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy`
around lines 54 - 77, The ref blocks for "state", "status", and "caches" in
VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy currently set desc to the
literal string "null"; update each ref (the entries with name "state", "status",
and "caches" that reference VmLocalVolumeCachePoolState,
VmLocalVolumeCachePoolStatus, and VmLocalVolumeCacheInventory) to use an empty
string (desc "") or provide an actual descriptive text in Chinese instead of
"null" so the generated documentation does not render the word "null".

Comment thread header/src/main/java/org/zstack/header/volume/VolumeAO.java Outdated
Comment on lines +725 to +726
if (!rsp.isSuccess()) {
reply.setError(operr("operation error, because:%s", rsp.getError()));

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

为这个新错误分支补一个稳定的 KVM 错误码。

这里直接返回匿名 operr(...),和本文件其他 KVM host 失败路径的 ORG_ZSTACK_KVM_xxxxx 约定不一致。新接口上线后,缺少稳定错误码会让告警聚合、问题定位和下游错误分类都更难;同时建议把 host 上下文也带进错误详情里。

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

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java` around lines 725 - 726,
当前分支直接用匿名 operr(...) 返回错误,需改为与本文件其余 KVM 错误路径一致,提供稳定的错误码并携带 host 上下文。请在 KVMHost
相关错误码集合中新增一个常量(例如 ORG_ZSTACK_KVM_OPERATION_ERROR 或类似命名),并在处理 rsp
不成功的地方用该稳定错误码构造错误(用与其它分支相同的错误构造函数/格式),同时把 host 相关信息(如 host.getUuid() 或
host.getManagementIp())拼接到错误详情里,最后用 reply.setError(...)
返回新的带码带上下文的错误;参考文件中其他失败分支的实现以保持一致性。

@MatheMatrix MatheMatrix force-pushed the sync/haidong.pang/feature/ZSTAC-81715@@3 branch from e4aaeaf to 8d45f70 Compare March 16, 2026 08:46

@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: 3

Caution

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

⚠️ Outside diff range comments (1)
header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java (1)

74-75: ⚠️ Potential issue | 🟡 Minor

代码注释中包含中文,应使用英文。

根据编码规范,代码中不应当包含中文,包括报错和注释等都应当使用正确的英文来写。

-* `@summary` 创建云主机
+* `@summary` Create a VM instance
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java` around
lines 74 - 75, The Javadoc `@summary` for the class APICreateVmInstanceMsg
contains Chinese text; update the comment to English by replacing the Chinese
summary with a clear English phrase such as "Create VM instance" or "Create
virtual machine" in the class-level Javadoc (the `@summary` tag) of
APICreateVmInstanceMsg so all source comments follow the project's English-only
comment policy.
♻️ Duplicate comments (7)
header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsgDoc_zh_cn.groovy (1)

8-10: ⚠️ Potential issue | 🟡 Minor

文档元信息仍是占位值,合并前建议补全。
categorydesc 目前是模板占位文本,会影响 API 文档可用性。

🛠️ 建议修改
-    category "未知类别"
+    category "主机"

-    desc """在这里填写API描述"""
+    desc """查询指定主机的块设备列表,返回可用于设备识别与后续缓存池相关操作的块设备信息。"""
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsgDoc_zh_cn.groovy`
around lines 8 - 10, Replace the placeholder metadata in
APIGetBlockDevicesMsgDoc_zh_cn.groovy: update the category value (the "category"
symbol) from "未知类别" to the appropriate API group name and replace the desc
multiline string (the "desc" symbol) with a concise, accurate Chinese
description of the API’s purpose, parameters and behavior; ensure the desc
explains what APIGetBlockDevicesMsg does, any important request fields and
returned data so the generated documentation is meaningful.
plugin/kvm/src/main/java/org/zstack/kvm/KVMExtensionEmitter.java (1)

395-399: ⚠️ Potential issue | 🔴 Critical

preBeforeAttachVolume 回调编排有致命问题:同一 completion 可能被多次触发。
当前写法把同一个 completion 传给所有扩展,任一扩展异步回调都会与其他扩展竞争,可能导致提前放行或重复 fail/success。建议改为串行链式执行,并保证最终只回调一次。

🛠️ 建议修改
+    private void doPreBeforeAttachVolume(Iterator<KVMAttachVolumeExtensionPoint> it,
+                                         KVMHostInventory host, VmInstanceInventory vm, VolumeInventory vol,
+                                         AttachDataVolumeCmd cmd, Map data, Completion completion) {
+        if (!it.hasNext()) {
+            completion.success();
+            return;
+        }
+
+        KVMAttachVolumeExtensionPoint ext = it.next();
+        ext.preBeforeAttachVolume(host, vm, vol, cmd, data, new Completion(completion) {
+            `@Override`
+            public void success() {
+                doPreBeforeAttachVolume(it, host, vm, vol, cmd, data, completion);
+            }
+
+            `@Override`
+            public void fail(ErrorCode errorCode) {
+                completion.fail(errorCode);
+            }
+        });
+    }
+
     public void preBeforeAttachVolume(KVMHostInventory host, VmInstanceInventory vm, VolumeInventory vol, AttachDataVolumeCmd cmd, Map data, Completion completion) {
-        for (KVMAttachVolumeExtensionPoint ext : attachVolumeExts) {
-            ext.preBeforeAttachVolume(host, vm, vol, cmd, data, completion);
-        }
+        doPreBeforeAttachVolume(attachVolumeExts.iterator(), host, vm, vol, cmd, data, completion);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMExtensionEmitter.java` around
lines 395 - 399, preBeforeAttachVolume currently passes the same Completion to
every KVMAttachVolumeExtensionPoint in attachVolumeExts so multiple async
extensions can call complete/fail repeatedly; change it to execute extensions
serially (chain them) so each ext is invoked one after another and only the
final chain invocation calls the original Completion once; implement by
iterating attachVolumeExts with an index-based invokeNext(extIndex, host, vm,
vol, cmd, data, chainCompletion) where each ext receives a new Completion that
on success calls invokeNext(index+1, ...) and on failure immediately calls the
original Completion.fail, and when index reaches end call original
Completion.success — ensure no other path calls the original Completion more
than once.
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityInventory.java (1)

20-35: ⚠️ Potential issue | 🟡 Minor

valueOf 转换缺少空值保护,存在 NPE 风险。
建议对单对象、集合和集合内元素都做空值保护,避免转换链路因脏输入直接失败。

🛠️ 建议修改
     public static VmLocalVolumeCachePoolCapacityInventory valueOf(VmLocalVolumeCachePoolCapacityVO vo) {
+        if (vo == null) {
+            return null;
+        }
         VmLocalVolumeCachePoolCapacityInventory inv = new VmLocalVolumeCachePoolCapacityInventory();
         inv.setUuid(vo.getUuid());
         inv.setTotalCapacity(vo.getTotalCapacity());
         inv.setAvailableCapacity(vo.getAvailableCapacity());
         inv.setAllocated(vo.getAllocated());
         inv.setDirty(vo.getDirty());
         return inv;
     }

     public static List<VmLocalVolumeCachePoolCapacityInventory> valueOf(Collection<VmLocalVolumeCachePoolCapacityVO> vos) {
-        List<VmLocalVolumeCachePoolCapacityInventory> invs = new ArrayList<>();
+        if (vos == null || vos.isEmpty()) {
+            return new ArrayList<>();
+        }
+        List<VmLocalVolumeCachePoolCapacityInventory> invs = new ArrayList<>(vos.size());
         for (VmLocalVolumeCachePoolCapacityVO vo : vos) {
-            invs.add(valueOf(vo));
+            VmLocalVolumeCachePoolCapacityInventory inv = valueOf(vo);
+            if (inv != null) {
+                invs.add(inv);
+            }
         }
         return invs;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityInventory.java`
around lines 20 - 35, The valueOf methods lack null checks and can NPE; update
VmLocalVolumeCachePoolCapacityInventory.valueOf(VmLocalVolumeCachePoolCapacityVO)
to return null (or throw a documented IllegalArgumentException) if the incoming
vo is null and guard access to vo.getXxx(), and update
VmLocalVolumeCachePoolCapacityInventory.valueOf(Collection<VmLocalVolumeCachePoolCapacityVO>)
to handle a null collection by returning an empty List and to skip or safely
handle null elements inside the loop (e.g., if vo==null continue or add
null/skip based on project convention) so the conversion chain won't fail on
dirty input.
storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java (1)

610-623: ⚠️ Potential issue | 🟠 Major

缓存模式解析与缓存元数据落库存在一致性风险

Line 619 直接 VmLocalVolumeCacheMode.valueOf(...),当 token 缺失/非法时会抛运行时异常;同时 cacheVO 在 SQLBatch 外持久化(Line 660-662),会出现“Volume 已创建但缓存配置未写入”的部分成功状态。另,CachePoolTag 命名不符合 lowerCamelCase。

🛠️ 建议修改
-        VmLocalVolumeCacheVO cacheVO = null;
+        VmLocalVolumeCacheVO cacheVO = null;
         if (msg.getSystemTag(VolumeSystemTags.VOLUME_CACHE_ENABLE::isMatch) != null) {
             cacheVO = new VmLocalVolumeCacheVO();
             cacheVO.setUuid(Platform.getUuid());
             cacheVO.setVolumeUuid(vo.getUuid());
             cacheVO.setState(VmLocalVolumeCacheState.Uninstantiated);
             String cacheModeTag = msg.getSystemTag(VolumeSystemTags.VOLUME_CACHE_MODE::isMatch);
-            String CachePoolTag = msg.getSystemTag(VolumeSystemTags.VOLUME_CACHE_POOL_UUID::isMatch);
+            String cachePoolTag = msg.getSystemTag(VolumeSystemTags.VOLUME_CACHE_POOL_UUID::isMatch);
             if (cacheModeTag != null) {
-                cacheVO.setCacheMode(VmLocalVolumeCacheMode.valueOf(VolumeSystemTags.VOLUME_CACHE_MODE.getTokenByTag(cacheModeTag, VolumeSystemTags.CACHE_MODE_TOKEN)));
+                String modeToken = VolumeSystemTags.VOLUME_CACHE_MODE.getTokenByTag(cacheModeTag, VolumeSystemTags.CACHE_MODE_TOKEN);
+                try {
+                    cacheVO.setCacheMode(VmLocalVolumeCacheMode.valueOf(modeToken));
+                } catch (Exception e) {
+                    throw new OperationFailureException(operr("invalid volume cache mode token[%s] from tag[%s]", modeToken, cacheModeTag));
+                }
             }
-            if (CachePoolTag != null) {
-                cacheVO.setPoolUuid(VolumeSystemTags.VOLUME_CACHE_POOL_UUID.getTokenByTag(CachePoolTag, VolumeSystemTags.CACHE_POOL_UUID_TOKEN));
+            if (cachePoolTag != null) {
+                cacheVO.setPoolUuid(VolumeSystemTags.VOLUME_CACHE_POOL_UUID.getTokenByTag(cachePoolTag, VolumeSystemTags.CACHE_POOL_UUID_TOKEN));
             }
         }
@@
-        VolumeVO finalVo = vo;
+        VolumeVO finalVo = vo;
+        VmLocalVolumeCacheVO finalCacheVO = cacheVO;
         vo = new SQLBatchWithReturn<VolumeVO>() {
             `@Override`
             protected VolumeVO scripts() {
                 dbf.getEntityManager().persist(finalVo);
+                if (finalCacheVO != null) {
+                    dbf.getEntityManager().persist(finalCacheVO);
+                }
                 dbf.getEntityManager().flush();
                 dbf.getEntityManager().refresh(finalVo);
                 return finalVo;
             }
         }.execute();
@@
-        if (cacheVO != null) {
-            dbf.persist(cacheVO);
-        }

可用以下只读脚本快速核验当前风险点是否仍存在:

#!/bin/bash
set -euo pipefail

fd VolumeManagerImpl.java --exec sed -n '600,670p' {}

rg -n "VmLocalVolumeCacheMode\\.valueOf|VOLUME_CACHE_MODE\\.getTokenByTag|CachePoolTag|dbf\\.persist\\(cacheVO\\)" \
  storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java

As per coding guidelines, “方法名、参数名、成员变量和局部变量使用 lowerCamelCase 风格”。

Also applies to: 643-662

🤖 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/VolumeManagerImpl.java`
around lines 610 - 623, The code currently calls
VmLocalVolumeCacheMode.valueOf(...) using
VolumeSystemTags.VOLUME_CACHE_MODE.getTokenByTag without validating the token,
and persists VmLocalVolumeCacheVO (cacheVO) outside the SQL batch, risking
runtime exceptions and partial commits; also rename CachePoolTag to cachePoolTag
to follow lowerCamelCase. Fix by validating/getting the token via
VolumeSystemTags.VOLUME_CACHE_MODE.getTokenByTag(...) and only calling
VmLocalVolumeCacheMode.valueOf if the token is non-null and matches an enum (or
wrap in a safe parse with controlled fallback), move creation/population and
dbf.persist(cacheVO) of VmLocalVolumeCacheVO into the same SQL batch/transaction
block that persists the volume to ensure atomicity, and rename the local
variable CachePoolTag to cachePoolTag (and update usages like
VolumeSystemTags.VOLUME_CACHE_POOL_UUID.getTokenByTag(...)) to follow naming
conventions.
header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsgDoc_zh_cn.groovy (1)

332-378: ⚠️ Potential issue | 🟡 Minor

新增缓存参数说明仍为空,文档信息不完整。

这与前一轮反馈一致:enableRootVolumeCachecacheModerootVolumeCachePoolUuidrootVolumeCacheModedataDiskCacheConfigOnIndexdesc 依旧为空,建议补齐用途与约束说明。

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

In
`@header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsgDoc_zh_cn.groovy`
around lines 332 - 378, The column descriptions for enableRootVolumeCache,
cacheMode, rootVolumeCachePoolUuid, rootVolumeCacheMode, and
dataDiskCacheConfigOnIndex are empty; update each column's desc to a clear,
concise explanation of purpose and constraints (e.g., enableRootVolumeCache:
whether to enable caching for the root volume; cacheMode/rootVolumeCacheMode:
allowed modes such as "WriteBack" and their effect on consistency;
rootVolumeCachePoolUuid: UUID of the cache pool to use when caching is enabled;
dataDiskCacheConfigOnIndex: map of disk index to cache configuration describing
expected keys/values), keeping notes about optionality and version (since
"5.5.6") and any value constraints so the API doc is complete and actionable.
compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java (2)

146-169: ⚠️ Potential issue | 🟠 Major

Host 预分配缓存约束仍未覆盖 DiskAO 缓存需求。

requiredSizepoolUuid 只统计了 root 与 dataDiskCacheConfigOnIndex,未纳入 spec.getDiskAOs() 中启用缓存的磁盘,可能导致调度阶段放行不满足缓存条件的主机。

建议补充统计
 private long getTotalCacheSize(VmInstanceSpec spec) {
     long totalCacheSize = 0;
@@
     Map<Integer, APICreateVmInstanceMsg.VolumeCacheConfig> dataDiskCacheConfigs = spec.getDataDiskCacheConfigOnIndex();
@@
     }
+
+    if (!CollectionUtils.isEmpty(spec.getDiskAOs())) {
+        for (APICreateVmInstanceMsg.DiskAO diskAO : spec.getDiskAOs()) {
+            if (diskAO != null && Boolean.TRUE.equals(diskAO.getEnableCache()) && diskAO.getSize() != null) {
+                totalCacheSize += diskAO.getSize();
+            }
+        }
+    }
     return totalCacheSize;
 }

 private List<String> getSpecifiedCachePoolUuids(VmInstanceSpec spec) {
@@
     if (dataDiskCacheConfigs != null && !dataDiskCacheConfigs.isEmpty()) {
@@
     }
+
+    if (!CollectionUtils.isEmpty(spec.getDiskAOs())) {
+        for (APICreateVmInstanceMsg.DiskAO diskAO : spec.getDiskAOs()) {
+            if (diskAO != null && Boolean.TRUE.equals(diskAO.getEnableCache())
+                    && diskAO.getCachePoolUuid() != null) {
+                poolUuids.add(diskAO.getCachePoolUuid());
+            }
+        }
+    }
     return poolUuids;
 }

Also applies to: 187-195

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

In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java` around
lines 146 - 169, getTotalCacheSize currently sums root and
dataDiskCacheConfigOnIndex sizes but ignores disk AOs from spec.getDiskAOs(), so
update getTotalCacheSize to also iterate spec.getDiskAOs() (e.g., for DiskAO
diskAo : spec.getDiskAOs()), detect entries that require caching (check the
DiskAO cache flag or method like isEnableCache()/getEnableCache()) and add their
disk size (diskAo.getSize() or getDiskSize()) to totalCacheSize; also replicate
the same addition logic in the other similar block referenced (lines ~187-195)
so requiredSize/poolUuid calculations include DiskAO cache requirements
consistently.

170-181: ⚠️ Potential issue | 🟠 Major

缓存池 UUID 收集条件仍不完整,且存在潜在 NPE。

当前仍会在未启用根盘缓存时收集 rootVolumeCachePoolUuid,并且对 dataDiskCacheConfigs 的 value 未做空判断,可能导致不必要的 host 约束或空指针。

建议修复
 private List<String> getSpecifiedCachePoolUuids(VmInstanceSpec spec) {
     List<String> poolUuids = new ArrayList<>();
-    if (spec.getRootVolumeCachePoolUuid() != null) {
+    if (Boolean.TRUE.equals(spec.getEnableRootVolumeCache())
+            && spec.getRootVolumeCachePoolUuid() != null) {
         poolUuids.add(spec.getRootVolumeCachePoolUuid());
     }
     Map<Integer, APICreateVmInstanceMsg.VolumeCacheConfig> dataDiskCacheConfigs = spec.getDataDiskCacheConfigOnIndex();
     if (dataDiskCacheConfigs != null && !dataDiskCacheConfigs.isEmpty()) {
         for (Integer index : dataDiskCacheConfigs.keySet()) {
             APICreateVmInstanceMsg.VolumeCacheConfig dataDiskCacheConfig = dataDiskCacheConfigs.get(index);
-            if (dataDiskCacheConfig.getCachePoolUuid() != null) {
+            if (dataDiskCacheConfig != null && dataDiskCacheConfig.getCachePoolUuid() != null) {
                 poolUuids.add(dataDiskCacheConfig.getCachePoolUuid());
             }
         }
     }
     return poolUuids;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java` around
lines 170 - 181, The method getSpecifiedCachePoolUuids collects cache pool UUIDs
even when root-volume caching isn't enabled and dereferences dataDiskCacheConfig
values without null checks; update getSpecifiedCachePoolUuids to: only add
spec.getRootVolumeCachePoolUuid() when the spec indicates root cache is enabled
(check the appropriate flag on VmInstanceSpec, e.g., isRootVolumeCacheEnabled()
or getRootVolumeCacheEnabled()), and iterate dataDiskCacheConfigs safely by
checking each APICreateVmInstanceMsg.VolumeCacheConfig value for null before
calling getCachePoolUuid(), and only add non-null cachePoolUuid values to
poolUuids to avoid unnecessary constraints and NPEs.
🧹 Nitpick comments (9)
utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java (1)

15875-15943: 建议:为错误码添加语义化命名或文档注释

新增的 35 个本地卷缓存错误码(1000110035)遵循了现有的命名模式,与文件中其他错误码保持一致。

但是,纯数字后缀的命名方式(如 _10001_10002)缺乏语义信息,在调用处难以直接理解其含义。建议考虑以下改进方案之一:

  1. 语义化命名:如 ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_POOL_NOT_FOUNDORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_CAPACITY_EXCEEDED
  2. 添加 Javadoc 注释:为每个常量说明其对应的错误场景

如果错误码的具体含义在其他配置文件或资源包中有定义,请忽略此建议。

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

In
`@utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java`
around lines 15875 - 15943, The new constants
ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_10001 through
ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_10035 in CloudOperationsErrorCode are
numeric-only and lack semantic meaning; update these by replacing or
supplementing them with descriptive names (e.g.,
ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_POOL_NOT_FOUND,
ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_CAPACITY_EXCEEDED) or add Javadoc comments
above each constant explaining the specific error scenario they represent;
locate the constants in class CloudOperationsErrorCode (symbols
ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_10001 ...
ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_10035) and either rename the constants to
semantic identifiers or add clear Javadoc lines for each to document the mapped
error condition (ensure any external usages or mappings are updated
accordingly).
header/src/main/java/org/zstack/header/vm/InstantiateNewCreatedVmInstanceMsg.java (1)

76-93: 建议对缓存字符串参数做 trim 归一化

cacheMode/rootVolumeCachePoolUuid/rootVolumeCacheMode 当前直接赋值。作为消息字段,建议在 setter 里统一 trim(或 trimToNull),避免用户输入前后空白导致后续匹配/校验异常。

♻️ 建议修改
 public void setCacheMode(String cacheMode) {
-    this.cacheMode = cacheMode;
+    this.cacheMode = cacheMode == null ? null : cacheMode.trim();
 }
 ...
 public void setRootVolumeCachePoolUuid(String rootVolumeCachePoolUuid) {
-    this.rootVolumeCachePoolUuid = rootVolumeCachePoolUuid;
+    this.rootVolumeCachePoolUuid = rootVolumeCachePoolUuid == null ? null : rootVolumeCachePoolUuid.trim();
 }
 ...
 public void setRootVolumeCacheMode(String rootVolumeCacheMode) {
-    this.rootVolumeCacheMode = rootVolumeCacheMode;
+    this.rootVolumeCacheMode = rootVolumeCacheMode == null ? null : rootVolumeCacheMode.trim();
 }

As per coding guidelines, “注意检查来自 Message 的参数是否做过 trim,用户可能在浏览器上复制粘贴的数据带有空格、换行符等”。

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

In
`@header/src/main/java/org/zstack/header/vm/InstantiateNewCreatedVmInstanceMsg.java`
around lines 76 - 93, The setters for cacheMode, rootVolumeCachePoolUuid and
rootVolumeCacheMode on InstantiateNewCreatedVmInstanceMsg currently assign
values directly; update setCacheMode, setRootVolumeCachePoolUuid and
setRootVolumeCacheMode to normalize inputs by trimming whitespace (or using
trim-to-null behavior) before assignment so leading/trailing spaces/newlines are
removed and empty results treated as null; keep null-safe handling (i.e., if
input is null leave as null or set to null after trim) to avoid downstream
matching/validation issues.
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy (1)

36-52: 部分字段缺少描述信息。

metadatatotalCapacityavailableCapacity 字段的 desc 为空字符串,建议补充描述以提升文档质量。

📝 建议修复
 	field {
 		name "metadata"
-		desc ""
+		desc "缓存池元数据"
 		type "LinkedHashMap"
 		since "5.5.6"
 	}
 	field {
 		name "totalCapacity"
-		desc ""
+		desc "总容量"
 		type "long"
 		since "5.5.6"
 	}
 	field {
 		name "availableCapacity"
-		desc ""
+		desc "可用容量"
 		type "long"
 		since "5.5.6"
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy`
around lines 36 - 52, The documentation fields metadata, totalCapacity and
availableCapacity have empty desc values; update the
VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy entries for the fields named
"metadata", "totalCapacity", and "availableCapacity" to provide meaningful
Chinese descriptions (e.g., explain that metadata is a key/value map of pool
properties, totalCapacity is the pool's total storage in bytes, and
availableCapacity is the currently free storage in bytes) so the API doc is
informative.
header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEventDoc_zh_cn.groovy (1)

8-21: 建议补齐标题与字段描述,避免生成文档出现空语义。

当前 title 仍为模板占位文本,且 blockDevices/success 描述信息不足,建议补全。

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

In
`@header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEventDoc_zh_cn.groovy`
around lines 8 - 21, The file APIGetBlockDevicesEventDoc_zh_cn.groovy has
placeholder and missing descriptions: replace the placeholder title with a
meaningful Chinese title for the event, and provide clear Chinese descriptions
for the ref/blockDevices
(org.zstack.header.host.APIGetBlockDevicesEvent.blockDevices, clz
HostBlockDeviceStruct) and the field "success" so generated docs are not empty;
update the title string and the desc values for the ref named "blockDevices" and
the field named "success" to concise descriptions matching their semantics.
plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java (1)

160-165: 代码格式问题。

  • Line 161: if 条件与 { 之间缺少空格
  • Line 165: returnto 之间有多余空格
♻️ 建议修复格式
         VmLocalVolumeCacheInventory cacheInv = vol.getLocalVolumeCache();
-        if (cacheInv != null){
+        if (cacheInv != null) {
             CacheTO cacheTO = CacheTO.valueOf(cacheInv);
             to.setCache(cacheTO);
         }
-        return  to;
+        return to;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java` around lines 160 -
165, In VolumeTO.java adjust whitespace to follow project style: in the block
that checks VmLocalVolumeCacheInventory (the if (cacheInv) block that uses
CacheTO.valueOf(cacheInv) and to.setCache(cacheTO)) add a space between the `if`
keyword/condition and the opening brace `{`, and remove the extra space between
`return` and the variable `to` in the final statement so it reads `return to;`.
header/src/main/java/org/zstack/header/host/HostBlockDeviceStruct.java (1)

6-6: 建议添加 serialVersionUID 以确保序列化兼容性。

实现 Serializable 接口的类建议显式声明 serialVersionUID,以避免在类结构变更时出现反序列化问题。

♻️ 建议添加
 public class HostBlockDeviceStruct implements Serializable {
+    private static final long serialVersionUID = 1L;
+
     private String name;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@header/src/main/java/org/zstack/header/host/HostBlockDeviceStruct.java` at
line 6, HostBlockDeviceStruct implements Serializable but lacks an explicit
serialVersionUID; add a private static final long serialVersionUID field (e.g.
1L or a generated UID) inside the HostBlockDeviceStruct class to ensure stable
serialization across versions and avoid InvalidClassException during
deserialization.
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO.java (1)

52-55: @OneToMany 使用 EAGER 抓取策略可能影响性能。

当加载 VmLocalVolumeCachePoolVO 时,会立即加载所有关联的 VmLocalVolumeCacheVO 实体。如果一个 pool 关联了大量 cache 条目,这可能导致性能问题。建议考虑使用 LAZY 抓取并在需要时显式加载。

♻️ 建议修改
-    `@OneToMany`(fetch = FetchType.EAGER)
+    `@OneToMany`(fetch = FetchType.LAZY)
     `@NotFound`(action = NotFoundAction.IGNORE)
     `@JoinColumn`(name = "poolUuid", insertable = false, updatable = false)
     private Set<VmLocalVolumeCacheVO> caches;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO.java`
around lines 52 - 55, 当前在 VmLocalVolumeCachePoolVO 的 caches 字段上使用
`@OneToMany`(fetch = FetchType.EAGER) 会在加载 VmLocalVolumeCachePoolVO 时强制一次性加载所有
VmLocalVolumeCacheVO,可能引起性能问题;请将该注解的抓取策略改为 FetchType.LAZY(即在
VmLocalVolumeCachePoolVO 类中把 caches 的 `@OneToMany`(fetch = FetchType.EAGER) 改为
`@OneToMany`(fetch = FetchType.LAZY)),并在需要访问关联缓存的地方改用显式加载(例如通过 repository/DAO
方法、JPQL with JOIN FETCH 或在事务内调用访问器以触发懒加载)以确保行为正确且不会导致 N+1 或懒加载异常。
plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java (1)

3754-3754: 建议将 flowData 的字符串 key 常量化。
"virtualDeviceInfoList" 作为跨步骤共享 key,建议提取为 private static final 常量,减少后续重构时的拼写风险。

Also applies to: 3771-3772

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

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java` at line 3754, Extract
the repeated string key "virtualDeviceInfoList" used when putting/getting values
into the flowData Map into a single private static final constant in the KVMHost
class (e.g., VIRTUAL_DEVICE_INFO_LIST_KEY) and replace all literal occurrences
(including the uses around flowData.put("virtualDeviceInfoList", ...) and the
related accesses at the other occurrences noted) with that constant to avoid
typos and ease refactoring.
header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java (1)

20-20: 建议为 LinkedHashMap 添加泛型参数以增强类型安全。

-private LinkedHashMap metadata;
+private LinkedHashMap<String, Object> metadata;

同样地,setMetadatagetMetadata 方法的签名也应相应更新。

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

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java`
at line 20, The field metadata in VmLocalVolumeCachePoolInventory is declared as
a raw LinkedHashMap; update it to use generic types (e.g., LinkedHashMap<String,
Object> or the appropriate key/value types for your metadata) and update the
signatures of getMetadata and setMetadata to use the same generic type to
restore compile-time type safety and avoid unchecked warnings; ensure any usages
or deserialization handling of metadata are adjusted to the chosen generic
types.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateVolumeFlow.java`:
- Around line 136-144: Trim cacheMode and cachePoolUuid before validating and
adding system tags: in VmAllocateVolumeFlow where you check vspec.getCacheMode()
and vspec.getCachePoolUuid(), call trim() (safely handling null) on the values,
then validate using isEmpty() on the trimmed string (and throw the
CloudRuntimeException using the trimmed value for context) and use the trimmed
strings when calling tags.add("volumeCache::cacheMode::" + ...) and
tags.add("volumeCache::poolUuid::" + ...); this prevents whitespace-only inputs
from being treated as valid tags.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventoryDoc_zh_cn.groovy`:
- Around line 9-49: The doc file contains placeholder and empty
descriptions—replace the title placeholder and all empty/"null" desc values with
meaningful Chinese text: update the title to a concise structure name, fill
field descs for uuid, volumeUuid, poolUuid, installPath with clear semantic
descriptions, and replace the ref descs for cacheMode and state with brief
explanations of those enums; ensure you modify the symbols title, field { name
"uuid" }, field { name "poolUuid" }, field { name "installPath" }, ref { name
"cacheMode" } and ref { name "state" } so generated docs are informative.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheStateDoc_zh_cn.groovy`:
- Around line 9-14: The documentation defines a non-existent enum value "None"
for VmLocalVolumeCacheState; remove the field block that names "None" from
VmLocalVolumeCacheStateDoc_zh_cn.groovy, or alternatively replace it with the
actual enum values ("Uninstantiated" and/or "Instantiated") so the doc matches
org.zstack.header.localVolumeCache.VmLocalVolumeCacheState; locate the field
block with name "None" and delete or update it accordingly.

---

Outside diff comments:
In `@header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java`:
- Around line 74-75: The Javadoc `@summary` for the class APICreateVmInstanceMsg
contains Chinese text; update the comment to English by replacing the Chinese
summary with a clear English phrase such as "Create VM instance" or "Create
virtual machine" in the class-level Javadoc (the `@summary` tag) of
APICreateVmInstanceMsg so all source comments follow the project's English-only
comment policy.

---

Duplicate comments:
In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java`:
- Around line 146-169: getTotalCacheSize currently sums root and
dataDiskCacheConfigOnIndex sizes but ignores disk AOs from spec.getDiskAOs(), so
update getTotalCacheSize to also iterate spec.getDiskAOs() (e.g., for DiskAO
diskAo : spec.getDiskAOs()), detect entries that require caching (check the
DiskAO cache flag or method like isEnableCache()/getEnableCache()) and add their
disk size (diskAo.getSize() or getDiskSize()) to totalCacheSize; also replicate
the same addition logic in the other similar block referenced (lines ~187-195)
so requiredSize/poolUuid calculations include DiskAO cache requirements
consistently.
- Around line 170-181: The method getSpecifiedCachePoolUuids collects cache pool
UUIDs even when root-volume caching isn't enabled and dereferences
dataDiskCacheConfig values without null checks; update
getSpecifiedCachePoolUuids to: only add spec.getRootVolumeCachePoolUuid() when
the spec indicates root cache is enabled (check the appropriate flag on
VmInstanceSpec, e.g., isRootVolumeCacheEnabled() or
getRootVolumeCacheEnabled()), and iterate dataDiskCacheConfigs safely by
checking each APICreateVmInstanceMsg.VolumeCacheConfig value for null before
calling getCachePoolUuid(), and only add non-null cachePoolUuid values to
poolUuids to avoid unnecessary constraints and NPEs.

In
`@header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsgDoc_zh_cn.groovy`:
- Around line 8-10: Replace the placeholder metadata in
APIGetBlockDevicesMsgDoc_zh_cn.groovy: update the category value (the "category"
symbol) from "未知类别" to the appropriate API group name and replace the desc
multiline string (the "desc" symbol) with a concise, accurate Chinese
description of the API’s purpose, parameters and behavior; ensure the desc
explains what APIGetBlockDevicesMsg does, any important request fields and
returned data so the generated documentation is meaningful.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityInventory.java`:
- Around line 20-35: The valueOf methods lack null checks and can NPE; update
VmLocalVolumeCachePoolCapacityInventory.valueOf(VmLocalVolumeCachePoolCapacityVO)
to return null (or throw a documented IllegalArgumentException) if the incoming
vo is null and guard access to vo.getXxx(), and update
VmLocalVolumeCachePoolCapacityInventory.valueOf(Collection<VmLocalVolumeCachePoolCapacityVO>)
to handle a null collection by returning an empty List and to skip or safely
handle null elements inside the loop (e.g., if vo==null continue or add
null/skip based on project convention) so the conversion chain won't fail on
dirty input.

In
`@header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsgDoc_zh_cn.groovy`:
- Around line 332-378: The column descriptions for enableRootVolumeCache,
cacheMode, rootVolumeCachePoolUuid, rootVolumeCacheMode, and
dataDiskCacheConfigOnIndex are empty; update each column's desc to a clear,
concise explanation of purpose and constraints (e.g., enableRootVolumeCache:
whether to enable caching for the root volume; cacheMode/rootVolumeCacheMode:
allowed modes such as "WriteBack" and their effect on consistency;
rootVolumeCachePoolUuid: UUID of the cache pool to use when caching is enabled;
dataDiskCacheConfigOnIndex: map of disk index to cache configuration describing
expected keys/values), keeping notes about optionality and version (since
"5.5.6") and any value constraints so the API doc is complete and actionable.

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMExtensionEmitter.java`:
- Around line 395-399: preBeforeAttachVolume currently passes the same
Completion to every KVMAttachVolumeExtensionPoint in attachVolumeExts so
multiple async extensions can call complete/fail repeatedly; change it to
execute extensions serially (chain them) so each ext is invoked one after
another and only the final chain invocation calls the original Completion once;
implement by iterating attachVolumeExts with an index-based invokeNext(extIndex,
host, vm, vol, cmd, data, chainCompletion) where each ext receives a new
Completion that on success calls invokeNext(index+1, ...) and on failure
immediately calls the original Completion.fail, and when index reaches end call
original Completion.success — ensure no other path calls the original Completion
more than once.

In `@storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java`:
- Around line 610-623: The code currently calls
VmLocalVolumeCacheMode.valueOf(...) using
VolumeSystemTags.VOLUME_CACHE_MODE.getTokenByTag without validating the token,
and persists VmLocalVolumeCacheVO (cacheVO) outside the SQL batch, risking
runtime exceptions and partial commits; also rename CachePoolTag to cachePoolTag
to follow lowerCamelCase. Fix by validating/getting the token via
VolumeSystemTags.VOLUME_CACHE_MODE.getTokenByTag(...) and only calling
VmLocalVolumeCacheMode.valueOf if the token is non-null and matches an enum (or
wrap in a safe parse with controlled fallback), move creation/population and
dbf.persist(cacheVO) of VmLocalVolumeCacheVO into the same SQL batch/transaction
block that persists the volume to ensure atomicity, and rename the local
variable CachePoolTag to cachePoolTag (and update usages like
VolumeSystemTags.VOLUME_CACHE_POOL_UUID.getTokenByTag(...)) to follow naming
conventions.

---

Nitpick comments:
In
`@header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEventDoc_zh_cn.groovy`:
- Around line 8-21: The file APIGetBlockDevicesEventDoc_zh_cn.groovy has
placeholder and missing descriptions: replace the placeholder title with a
meaningful Chinese title for the event, and provide clear Chinese descriptions
for the ref/blockDevices
(org.zstack.header.host.APIGetBlockDevicesEvent.blockDevices, clz
HostBlockDeviceStruct) and the field "success" so generated docs are not empty;
update the title string and the desc values for the ref named "blockDevices" and
the field named "success" to concise descriptions matching their semantics.

In `@header/src/main/java/org/zstack/header/host/HostBlockDeviceStruct.java`:
- Line 6: HostBlockDeviceStruct implements Serializable but lacks an explicit
serialVersionUID; add a private static final long serialVersionUID field (e.g.
1L or a generated UID) inside the HostBlockDeviceStruct class to ensure stable
serialization across versions and avoid InvalidClassException during
deserialization.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java`:
- Line 20: The field metadata in VmLocalVolumeCachePoolInventory is declared as
a raw LinkedHashMap; update it to use generic types (e.g., LinkedHashMap<String,
Object> or the appropriate key/value types for your metadata) and update the
signatures of getMetadata and setMetadata to use the same generic type to
restore compile-time type safety and avoid unchecked warnings; ensure any usages
or deserialization handling of metadata are adjusted to the chosen generic
types.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy`:
- Around line 36-52: The documentation fields metadata, totalCapacity and
availableCapacity have empty desc values; update the
VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy entries for the fields named
"metadata", "totalCapacity", and "availableCapacity" to provide meaningful
Chinese descriptions (e.g., explain that metadata is a key/value map of pool
properties, totalCapacity is the pool's total storage in bytes, and
availableCapacity is the currently free storage in bytes) so the API doc is
informative.

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO.java`:
- Around line 52-55: 当前在 VmLocalVolumeCachePoolVO 的 caches 字段上使用
`@OneToMany`(fetch = FetchType.EAGER) 会在加载 VmLocalVolumeCachePoolVO 时强制一次性加载所有
VmLocalVolumeCacheVO,可能引起性能问题;请将该注解的抓取策略改为 FetchType.LAZY(即在
VmLocalVolumeCachePoolVO 类中把 caches 的 `@OneToMany`(fetch = FetchType.EAGER) 改为
`@OneToMany`(fetch = FetchType.LAZY)),并在需要访问关联缓存的地方改用显式加载(例如通过 repository/DAO
方法、JPQL with JOIN FETCH 或在事务内调用访问器以触发懒加载)以确保行为正确且不会导致 N+1 或懒加载异常。

In
`@header/src/main/java/org/zstack/header/vm/InstantiateNewCreatedVmInstanceMsg.java`:
- Around line 76-93: The setters for cacheMode, rootVolumeCachePoolUuid and
rootVolumeCacheMode on InstantiateNewCreatedVmInstanceMsg currently assign
values directly; update setCacheMode, setRootVolumeCachePoolUuid and
setRootVolumeCacheMode to normalize inputs by trimming whitespace (or using
trim-to-null behavior) before assignment so leading/trailing spaces/newlines are
removed and empty results treated as null; keep null-safe handling (i.e., if
input is null leave as null or set to null after trim) to avoid downstream
matching/validation issues.

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java`:
- Line 3754: Extract the repeated string key "virtualDeviceInfoList" used when
putting/getting values into the flowData Map into a single private static final
constant in the KVMHost class (e.g., VIRTUAL_DEVICE_INFO_LIST_KEY) and replace
all literal occurrences (including the uses around
flowData.put("virtualDeviceInfoList", ...) and the related accesses at the other
occurrences noted) with that constant to avoid typos and ease refactoring.

In `@plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java`:
- Around line 160-165: In VolumeTO.java adjust whitespace to follow project
style: in the block that checks VmLocalVolumeCacheInventory (the if (cacheInv)
block that uses CacheTO.valueOf(cacheInv) and to.setCache(cacheTO)) add a space
between the `if` keyword/condition and the opening brace `{`, and remove the
extra space between `return` and the variable `to` in the final statement so it
reads `return to;`.

In
`@utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java`:
- Around line 15875-15943: The new constants
ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_10001 through
ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_10035 in CloudOperationsErrorCode are
numeric-only and lack semantic meaning; update these by replacing or
supplementing them with descriptive names (e.g.,
ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_POOL_NOT_FOUND,
ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_CAPACITY_EXCEEDED) or add Javadoc comments
above each constant explaining the specific error scenario they represent;
locate the constants in class CloudOperationsErrorCode (symbols
ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_10001 ...
ORG_ZSTACK_STORAGE_LOCALVOLUMECACHE_10035) and either rename the constants to
semantic identifiers or add clear Javadoc lines for each to document the mapped
error condition (ensure any external usages or mappings are updated
accordingly).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: http://open.zstack.ai:20001/code-reviews/zstack-cloud.yaml (via .coderabbit.yaml)

Review profile: CHILL

Plan: Pro

Run ID: 43c0638c-dab9-465a-958a-e13db2607331

📥 Commits

Reviewing files that changed from the base of the PR and between e4aaeaf and 8d45f70.

⛔ Files ignored due to path filters (44)
  • conf/globalConfig/vmLocalVolumeCachePool.xml is excluded by !**/*.xml
  • conf/persistence.xml is excluded by !**/*.xml
  • conf/serviceConfig/host.xml is excluded by !**/*.xml
  • conf/serviceConfig/vmLocalVolumeCache.xml is excluded by !**/*.xml
  • conf/serviceConfig/vmLocalVolumeCachePool.xml is excluded by !**/*.xml
  • conf/springConfigXml/localVolumeCache.xml is excluded by !**/*.xml
  • conf/zstack.xml is excluded by !**/*.xml
  • sdk/src/main/java/SourceClassMap.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CacheTO.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ChangeVmLocalVolumeCachePoolStateAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ChangeVmLocalVolumeCachePoolStateResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateOvnControllerVmAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateVmInstanceAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/CreateVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DeleteVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DeleteVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DisableVolumeCacheAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/DisableVolumeCacheResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/EnableVolumeCacheAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/EnableVolumeCacheResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ExtendVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ExtendVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/GetBlockDevicesAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/GetBlockDevicesResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/HostBlockDeviceStruct.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/HostInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/QueryVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/QueryVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ReconnectVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/ReconnectVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/SyncVmLocalVolumeCachePoolCapacityAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/SyncVmLocalVolumeCachePoolCapacityResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/UpdateVmLocalVolumeCachePoolAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/UpdateVmLocalVolumeCachePoolResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheMode.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolState.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolStatus.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheState.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VolumeInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/VolumeTO.java is excluded by !sdk/**
  • test/src/test/resources/springConfigXml/license.xml is excluded by !**/*.xml
📒 Files selected for processing (61)
  • compute/src/main/java/org/zstack/compute/host/HostBase.java
  • compute/src/main/java/org/zstack/compute/vm/InstantiateVmFromNewCreatedStruct.java
  • compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java
  • compute/src/main/java/org/zstack/compute/vm/VmAllocateVolumeFlow.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceManagerImpl.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstanceUtils.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstantiateOtherDiskFlow.java
  • conf/db/upgrade/V5.5.12__schema.sql
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEvent.java
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesEventDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsg.java
  • header/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsgDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/GetBlockDevicesOnHostMsg.java
  • header/src/main/java/org/zstack/header/host/GetBlockDevicesOnHostReply.java
  • header/src/main/java/org/zstack/header/host/HostBlockDeviceStruct.java
  • header/src/main/java/org/zstack/header/host/HostBlockDeviceStructDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/HostInventory.java
  • header/src/main/java/org/zstack/header/host/HostVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/CachePoolMetadata.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheConstant.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventoryDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheMode.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheModeDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO_.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolState.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStateDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatus.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatusDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO_.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheState.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheStateDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO_.java
  • header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsgDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/vm/CreateVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/vm/InstantiateNewCreatedVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java
  • header/src/main/java/org/zstack/header/volume/VolumeAO.java
  • header/src/main/java/org/zstack/header/volume/VolumeInventory.java
  • plugin/kvm/src/main/java/org/zstack/kvm/CacheTO.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMApiInterceptor.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMAttachVolumeExtensionPoint.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMExtensionEmitter.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java
  • plugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.java
  • plugin/vhost/src/main/java/org/zstack/vhost/kvm/KvmVhostNodeServer.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeApiInterceptor.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeSystemTags.java
  • testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy
  • utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java
💤 Files with no reviewable changes (2)
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMApiInterceptor.java
  • storage/src/main/java/org/zstack/storage/volume/VolumeApiInterceptor.java
🚧 Files skipped from review as they are similar to previous changes (15)
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStateDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatusDoc_zh_cn.groovy
  • header/src/main/java/org/zstack/header/host/HostInventory.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheState.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolState.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheModeDoc_zh_cn.groovy
  • plugin/vhost/src/main/java/org/zstack/vhost/kvm/KvmVhostNodeServer.java
  • header/src/main/java/org/zstack/header/host/HostBlockDeviceStructDoc_zh_cn.groovy
  • conf/db/upgrade/V5.5.12__schema.sql
  • header/src/main/java/org/zstack/header/localVolumeCache/CachePoolMetadata.java
  • plugin/kvm/src/main/java/org/zstack/kvm/CacheTO.java
  • header/src/main/java/org/zstack/header/vm/CreateVmInstanceMsg.java
  • header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO.java
  • compute/src/main/java/org/zstack/compute/vm/VmInstantiateOtherDiskFlow.java
  • header/src/main/java/org/zstack/header/volume/VolumeAO.java

Comment on lines +136 to +144
if (vspec.getCacheMode() == null || vspec.getCacheMode().isEmpty()) {
throw new CloudRuntimeException(String.format(
"cacheMode cannot be empty when volume cache is enabled, vmUuid:%s, volumeType:%s",
spec.getVmInventory().getUuid(), vspec.getType()));
}
tags.add("volumeCache::cacheMode::" + vspec.getCacheMode());
if(vspec.getCachePoolUuid() != null) {
tags.add("volumeCache::poolUuid::" + vspec.getCachePoolUuid());
}

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 | 🟠 Major

建议对 cacheMode/cachePoolUuid 做 trim 后再校验与落标签。

Line 136 当前仅做 null/isEmpty,像 " " 这种值会被当成有效输入写入 system tag,后续解析容易出现隐性错误。建议先 trim(),再做空值判断并写入标签。

💡 建议修改
             if (vspec.getEnableVolumeCache()) {
                 tags.add("volumeCache::enable");
-                if (vspec.getCacheMode() == null || vspec.getCacheMode().isEmpty()) {
+                String cacheMode = vspec.getCacheMode() == null ? null : vspec.getCacheMode().trim();
+                if (cacheMode == null || cacheMode.isEmpty()) {
                     throw new CloudRuntimeException(String.format(
                             "cacheMode cannot be empty when volume cache is enabled, vmUuid:%s, volumeType:%s",
                             spec.getVmInventory().getUuid(), vspec.getType()));
                 }
-                tags.add("volumeCache::cacheMode::" + vspec.getCacheMode());
-                if(vspec.getCachePoolUuid() != null) {
-                    tags.add("volumeCache::poolUuid::" + vspec.getCachePoolUuid());
+                tags.add("volumeCache::cacheMode::" + cacheMode);
+                String cachePoolUuid = vspec.getCachePoolUuid() == null ? null : vspec.getCachePoolUuid().trim();
+                if (cachePoolUuid != null && !cachePoolUuid.isEmpty()) {
+                    tags.add("volumeCache::poolUuid::" + cachePoolUuid);
                 }
             }

As per coding guidelines "注意检查来自 Message 的参数是否做过 trim,用户可能在浏览器上复制粘贴的数据带有空格、换行符等".

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

In `@compute/src/main/java/org/zstack/compute/vm/VmAllocateVolumeFlow.java` around
lines 136 - 144, Trim cacheMode and cachePoolUuid before validating and adding
system tags: in VmAllocateVolumeFlow where you check vspec.getCacheMode() and
vspec.getCachePoolUuid(), call trim() (safely handling null) on the values, then
validate using isEmpty() on the trimmed string (and throw the
CloudRuntimeException using the trimmed value for context) and use the trimmed
strings when calling tags.add("volumeCache::cacheMode::" + ...) and
tags.add("volumeCache::poolUuid::" + ...); this prevents whitespace-only inputs
from being treated as valid tags.

Comment on lines +9 to +49
title "在这里输入结构的名称"

field {
name "uuid"
desc "资源的UUID,唯一标示该资源"
type "String"
since "5.5.6"
}
field {
name "volumeUuid"
desc "云盘UUID"
type "String"
since "5.5.6"
}
field {
name "poolUuid"
desc ""
type "String"
since "5.5.6"
}
field {
name "installPath"
desc ""
type "String"
since "5.5.6"
}
ref {
name "cacheMode"
path "org.zstack.header.localVolumeCache.VmLocalVolumeCacheInventory.cacheMode"
desc "null"
type "VmLocalVolumeCacheMode"
since "5.5.6"
clz VmLocalVolumeCacheMode.class
}
ref {
name "state"
path "org.zstack.header.localVolumeCache.VmLocalVolumeCacheInventory.state"
desc "null"
type "VmLocalVolumeCacheState"
since "5.5.6"
clz VmLocalVolumeCacheState.class

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

文档仍含占位符和空描述,建议补齐语义。

title 仍是模板占位文本,且部分 desc 为空或为 "null";建议补充为明确描述,避免生成文档后信息缺失。

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

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventoryDoc_zh_cn.groovy`
around lines 9 - 49, The doc file contains placeholder and empty
descriptions—replace the title placeholder and all empty/"null" desc values with
meaningful Chinese text: update the title to a concise structure name, fill
field descs for uuid, volumeUuid, poolUuid, installPath with clear semantic
descriptions, and replace the ref descs for cacheMode and state with brief
explanations of those enums; ensure you modify the symbols title, field { name
"uuid" }, field { name "poolUuid" }, field { name "installPath" }, ref { name
"cacheMode" } and ref { name "state" } so generated docs are informative.

Comment on lines +9 to +14
field {
name "None"
desc ""
type "VmLocalVolumeCacheState"
since "5.5.6"
}

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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: 验证 VmLocalVolumeCacheState 枚举中是否包含 None 值

# 查找枚举定义文件
fd "VmLocalVolumeCacheState.java" --type f --exec cat {}

# 使用 ast-grep 搜索枚举定义
ast-grep --pattern 'enum VmLocalVolumeCacheState {
  $$$
}'

Repository: MatheMatrix/zstack

Length of output: 288


删除文档中不存在的 None 状态定义

org.zstack.header.localVolumeCache.VmLocalVolumeCacheState 枚举中不包含 None 值,仅包含 UninstantiatedInstantiated。当前文档定义的 None 状态与实际枚举定义不一致。请移除该字段或将 None 添加到枚举定义中。

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

In
`@header/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheStateDoc_zh_cn.groovy`
around lines 9 - 14, The documentation defines a non-existent enum value "None"
for VmLocalVolumeCacheState; remove the field block that names "None" from
VmLocalVolumeCacheStateDoc_zh_cn.groovy, or alternatively replace it with the
actual enum values ("Uninstantiated" and/or "Instantiated") so the doc matches
org.zstack.header.localVolumeCache.VmLocalVolumeCacheState; locate the field
block with name "None" and delete or update it accordingly.

@MatheMatrix MatheMatrix force-pushed the sync/haidong.pang/feature/ZSTAC-81715@@3 branch 4 times, most recently from 8e93767 to 4a09f6e Compare March 16, 2026 12:58
@zstack-robot-2

Copy link
Copy Markdown
Collaborator

Comment from yaohua.wu:

Review: MR !9343 — ZSTAC-81715 (zstack repo)

Feature: 计算节点 IO 缓存本地化(VM local volume cache)— 核心框架层变更

本次 Review 覆盖 zstack 仓库 105 个文件变更,重点检查 host 调度、volume 分配、KVM 扩展点等核心路径。


🔴 Critical

1. [VmAllocateHostFlow.java] DiskAO 缓存需求未参与 Host 调度

getTotalCacheSize() 只统计了 enableRootVolumeCachedataDiskCacheConfigOnIndex,但 VmInstantiateOtherDiskFlow 已支持对 APICreateVmInstanceMsg.DiskAO 设置 enableCache=true。当用户通过 DiskAO 方式创建 VM 并启用缓存时:

  • addCacheSystemTags() 不会产生 volumeCache::requiredSize:: 标签
  • Host 调度不会过滤缓存池容量
  • 可能选中无缓存池或容量不足的 host,导致后续 volume 创建/缓存实例化失败

建议:在 getTotalCacheSize() 中追加 DiskAO 缓存大小的统计:

if (spec.getDiskAOs() != null) {
    for (APICreateVmInstanceMsg.DiskAO diskAO : spec.getDiskAOs()) {
        if (Boolean.TRUE.equals(diskAO.getEnableCache()) && diskAO.getDiskSize() > 0) {
            totalCacheSize += diskAO.getDiskSize();
        }
    }
}

同理 getSpecifiedCachePoolUuids() 也需要从 DiskAO 中提取 cachePoolUuid

2. [VmInstantiateOtherDiskFlow.java:262] initCacheRecord()diskAO.getCacheMode() 为 null 时会抛异常

cacheVO.setCacheMode(VmLocalVolumeCacheMode.valueOf(diskAO.getCacheMode()));

当 DiskAO 未显式设置 cacheMode 时(用户只传 enableCache=true),getCacheMode() 为 null,VmLocalVolumeCacheMode.valueOf(null) 会抛 NullPointerException

建议:添加默认值:

String cacheMode = diskAO.getCacheMode() != null ? diskAO.getCacheMode() : VmLocalVolumeCacheMode.WriteBack.toString();
cacheVO.setCacheMode(VmLocalVolumeCacheMode.valueOf(cacheMode));

🟡 Warning

1. [VmAllocateVolumeFlow.java:214-223] 缓存 system tag 未去重

如果调用方已经在 vspec.getTags() / spec.getRootVolumeSystemTags() 中传入了 volumeCache::* 标签,当前代码会直接追加新标签,导致同一个 volume 上存在多个同前缀 tag(如两个 volumeCache::cacheMode:: tag)。后续读取端拿到多个同类 tag 时取值不确定。

建议:在追加 volumeCache::* 标签前,先移除 tags 中已存在的同前缀条目。

2. [VmAllocateVolumeFlow.java:187-194] Data disk cache config index 映射假设 VolumeSpec 顺序与 DataDiskOffering 顺序一致

List<VolumeSpec> dataVolumeSpecs = spec.getVolumeSpecs().stream()
        .filter(s -> s.getType().equals(VolumeType.Data.toString())).collect(Collectors.toList());
IntStream.range(0, dataVolumeSpecs.size()).forEach(index -> {
    APICreateVmInstanceMsg.VolumeCacheConfig config = dataDiskCacheConfigs.get(index);

dataDiskCacheConfigOnIndex 的 key 是用户传入的 dataDiskOffering 索引,但 dataVolumeSpecs 是从 spec.getVolumeSpecs() 过滤出来的 Data 类型。如果中间有其他 flow 修改了 VolumeSpecs 的顺序或数量,索引映射会错位。建议添加注释说明索引契约,或使用更安全的关联方式(如 volumeUuid)。

3. [KVMHost.java] attachDataVolume FlowChain 重构后缺少 error 日志

原来的 inline 回调中有明确的 operr() 日志包含 vol.uuid / vm.name / host.ip,重构为 FlowChain 后 done(err) 路径仅设置 reply.setError(err) 但没有附带 host/vm 上下文。出问题时运维定位困难。

4. [VolumeTO.java:103-106] valueOfWithOutExtension 命名不规范

方法名 valueOfWithOutExtension 应为 valueOfWithoutExtensionWithout 是一个词)。此外该方法直接调用 valueOf(vol, host, platform, false)withExtension=false 已是默认行为,存在冗余方法的可能。

5. [APICreateVmInstanceMsg.java] cacheModerootVolumeCacheMode 语义重叠

APICreateVmInstanceMsg 同时有 cacheModerootVolumeCacheMode 两个字段,都标注 validValues = {"WriteBack"},但在 InstantiateVmFromNewCreatedStruct 中两者都被独立传递。cacheMode 的使用场景不明确,可能导致调用方困惑。建议明确文档说明或合并。


🟢 Suggestion

1. [CacheTO.java] valueOf() 缺少空值保护

建议增加 if (inv == null) return null; 防御。

2. [VmInstanceSpec.VolumeSpec] enableVolumeCache 默认值为 false(primitive boolean)

由于使用了 primitive boolean,不设值时默认 false,而其他字段如 cachePoolUuidString 默认 null。建议改为 Boolean 以统一 null 语义("未配置" vs "明确关闭")。

3. [V5.5.12__schema.sql] 建议 review 外键约束和索引

新增的 VmLocalVolumeCachePoolVOVmLocalVolumeCacheVO 表应确认:

  • volumeUuid 外键是否设置 ON DELETE CASCADE(volume 删除时自动清理缓存记录)
  • poolUuid + hostUuid 上是否有合适索引(调度查询性能)

4. [HostBlockDeviceStruct.java] equals()/hashCode() 契约问题

已有 robot 指出:当 wwid 一方为 null 时 equals() 回退到 path+hctl 比较,但 hashCode() 使用 wwid 计算,违反 equals/hashCode 契约。建议修复。


Verdict: REVISION_REQUIRED

主要阻塞项:Critical #1(DiskAO 缓存未参与调度)和 Critical #2(initCacheRecord NPE)需要修复。


🤖 Robot Reviewer

@MatheMatrix MatheMatrix force-pushed the sync/haidong.pang/feature/ZSTAC-81715@@3 branch 3 times, most recently from 40ea978 to 493b5b7 Compare March 17, 2026 03:54
@MatheMatrix

Copy link
Copy Markdown
Owner Author

Comment from yaohua.wu:

Review: MR !9343 — ZSTAC-81715 (zstack)

Feature: 计算节点 IO 缓存本地化 — VM Local Volume Cache

🔴 Critical

  1. [HostVO.java] FetchType.EAGER on @OneToMany localVolumeCachePools — 全局性能炸弹

    @OneToMany(fetch = FetchType.EAGER)
    @JoinColumn(name = "hostUuid", insertable = false, updatable = false)
    private Set<VmLocalVolumeCachePoolVO> localVolumeCachePools;

    每次加载 HostVO(包括 host 列表查询、分配器查询等)都会 EAGER 加载该 host 的所有 CachePoolVO,而 CachePoolVO 自身又有 @OneToOne(EAGER) 的 capacity。百台以上集群中,这会导致管理面全面性能退化。

    建议: 改为 FetchType.LAZY,缓存池信息通过 @ExpandedQuery 或独立查询获取。

  2. [VolumeAO.java] FetchType.EAGER on @OneToOne localVolumeCache — 所有 volume 查询受影响

    @OneToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "uuid", referencedColumnName = "volumeUuid", insertable = false, updatable = false)
    private VmLocalVolumeCacheVO localVolumeCache;

    VolumeAO 是所有 volume 操作的基类实体,绝大多数 volume 不会有 local cache,但每次 volume 查询都会额外 JOIN VmLocalVolumeCacheVO 表。大规模部署(上千 volume)影响显著。

    建议: 改为 FetchType.LAZYisLocalCached() 改用 Q.New(VmLocalVolumeCacheVO.class).eq(...).isExists() 做针对性查询。

🟡 Warning

  1. [VmAllocateHostFlow.java:~145] DiskAO.getEnableCache() NPE 风险

    totalCacheSize = diskAOs.stream()
        .filter(APICreateVmInstanceMsg.DiskAO::getEnableCache)  // enableCache 是 Boolean(可 null),auto-unbox 会 NPE
        .mapToLong(APICreateVmInstanceMsg.DiskAO::getSize)
        .sum();

    enableCache 字段类型是 Boolean(boxed),null 时 auto-unbox 到 boolean 会抛 NPE。同一 MR 中其他地方已正确使用 Boolean.TRUE.equals(...) 模式。

    建议: .filter(d -> Boolean.TRUE.equals(d.getEnableCache()))

  2. [VmLocalVolumeCacheVO.java] poolUuid 缺少 @ForeignKey 注解

    SQL schema 定义了 FOREIGN KEY (poolUuid) REFERENCES VmLocalVolumeCachePoolVO(uuid) ON DELETE SET NULL,但 JPA 实体缺少 @ForeignKey 注解,ZStack 实体图框架(级联删除、GC)无法追踪此关系。

    建议: 添加 @ForeignKey(parentEntityClass = VmLocalVolumeCachePoolVO.class, onDeleteAction = ForeignKey.ReferenceOption.SET_NULL)

  3. [VmLocalVolumeCacheStateDoc_zh_cn.groovy] 文档列出不存在的 None 状态

    groovy doc 中列出 None 状态,但 VmLocalVolumeCacheState 枚举只有 UninstantiatedInstantiated。API 文档会产生误导。

    建议: 删除 None field,或在枚举中添加(如果确实需要)。

  4. [VolumeTO.java:~103-110] BeanUtils.copyPropertiesHostInventory 构造 KVMHostInventory — 字段丢失

    KVMHostInventory kvmHostInventory = new KVMHostInventory();
    BeanUtils.copyProperties(host, kvmHostInventory);

    KVMHostInventory 有 KVM 特有字段(如 managementIp),从 HostInventory 拷贝不会填充这些字段。下游 extension point 期望完整的 KVMHostInventory 可能 NPE。

    建议: 在调用处直接传 KVMHostInventory,或从 DB 加载:KVMHostInventory.valueOf(dbf.findByUuid(host.getUuid(), KVMHostVO.class))

  5. [VmLocalVolumeCacheMode.java] 只有 WriteBack 一种模式

    缓存模式只有 WriteBack,缺少 WriteThrough。多处代码硬编码 WriteBack 作为默认值。如果后续需要支持 WriteThrough,修改面很大。

    建议: 如果 WriteThrough 在规划中,建议现在就集中管理默认值(而非散落在 4+ 个类中)。如有意推迟,添加注释说明。

🟢 Suggestion

  1. [APICreateVmInstanceMsg.java] cacheModerootVolumeCacheMode 语义模糊 — 两个字段都是 validValues = {"WriteBack"}cacheMode 在 volume 创建流程中未被使用(只有 rootVolumeCacheMode 生效),疑似冗余。建议删除或明确文档区分。

  2. [VmInstantiateOtherDiskFlow.java] initCacheRecord() 使用 NoRollbackFlow — flow 失败时 cache 记录不回滚,volumeUuid 上的 UNIQUE KEY 会阻塞重试。建议实现 rollback handler 或确保幂等性。

  3. [多文件] 代码风格:if 后缺空格if(vspec.getCachePoolUuid() != null) 等处缺少空格,与项目风格不一致。

  4. *[所有 Doc_zh_cn.groovy] API 文档模板未填写 — title 仍为 "在这里输入结构的名称",desc 为空,category 为 "未知类别"。发布前需补全。

Verdict: REVISION_REQUIRED

两个 CRITICAL(EAGER fetch)会影响整个管理面性能,必须在合并前修复为 LAZY。


🤖 Robot Reviewer

@MatheMatrix MatheMatrix force-pushed the sync/haidong.pang/feature/ZSTAC-81715@@3 branch 5 times, most recently from 91816d4 to f2c26f8 Compare March 17, 2026 09:29
@ZStack-Robot

Copy link
Copy Markdown
Collaborator

@coderabbitai Comment on test/src/test/resources/springConfigXml/license.xml from @yaohua.wu on GitLab:

这个是为啥删了

@coderabbitai

coderabbitai Bot commented Mar 18, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if the file exists in the current branch
fd "license.xml" test/

# Check git log for this file
git log --oneline --diff-filter=D -- "test/src/test/resources/springConfigXml/license.xml" 2>/dev/null | head -5

# Search for references to license.xml in the codebase
rg -n "license\.xml" --type xml --type java --type groovy -C2

# Check the git diff for this specific file in the PR
git show HEAD -- "test/src/test/resources/springConfigXml/license.xml" 2>/dev/null | head -50

# Look for other spring config XML files to understand the pattern
fd "*.xml" test/src/test/resources/springConfigXml/ 2>/dev/null | head -20

Length of output: 1411


🏁 Script executed:

#!/bin/bash
# Check if MockLicenseManagerImpl still exists
fd "MockLicenseManagerImpl.java" 

# Search for any other place that registers MockLicenseManagerImpl or references license.xml
rg -n "MockLicenseManagerImpl" -C3

# Check if there's another license spring config XML elsewhere
fd "license.xml"

# Check if this file is referenced in any test suite XML files
rg -n "license\.xml" --type xml -C3

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Search for any applicationContext or test suite XML that referenced license.xml
rg -rn "license" test/src/test/resources/ --include="*.xml" -C3

# Look for LicenseManager bean references in test XMLs
rg -rn "LicenseManager" test/ --include="*.xml" -C3

# Check if there's a MockLicenseManager class under a different name
rg -rn "MockLicense" test/ -C2

# List remaining spring config XMLs to understand what was kept
fd "*.xml" test/src/test/resources/springConfigXml/ | sort

Length of output: 1011


🏁 Script executed:

#!/bin/bash
# Find all spring config XMLs in test resources
fd "*.xml" test/src/test/resources/ 2>/dev/null | sort | head -30

# Search for LicenseManager references in test resources
rg -n "LicenseManager" test/ -C3 2>/dev/null | head -30

# Search for license references in test resources
rg -n "license" test/ -g "*.xml" -C3 2>/dev/null | head -30

# Check what was in springConfigXml directory (now deleted)
git show HEAD:test/src/test/resources/springConfigXml/ 2>/dev/null || true
git diff HEAD~1 HEAD --name-only 2>/dev/null | grep -i "springConfigXml" | head -20

# Check if MockLicenseManagerImpl existed before this commit
git show HEAD~1:test/src/test/java/org/zstack/test/mevoco/MockLicenseManagerImpl.java 2>/dev/null | head -5

Length of output: 1540


@ZStack-Robot(转达 @yaohua.wu

经过排查,test/src/test/resources/springConfigXml/license.xml 的删除是合理的清理行为,具体原因如下:

  1. 该文件对应的实现类已不存在license.xml 中注册的 MockLicenseManagerImpl 类(org.zstack.test.mevoco.MockLicenseManagerImpl)在当前代码库中已不再存在,是一个孤立的 Spring 配置文件(orphaned config)。

  2. 此次 PR 是 GitLab MR !9343 的同步:删除动作是随 GitLab 上游同步带过来的,并非 vm local volume cache 功能本身的变更,而是对此前已移除的 MockLicenseManagerImpl 留下的遗留配置进行清理。

  3. 无其他引用:仓库中已无任何代码或配置文件引用 license.xmlLicenseManager bean,删除后不会影响任何测试。

结论:该文件删除是正常的历史遗留配置清理,可以接受。若需确认 MockLicenseManagerImpl 是在哪个历史 commit 中被移除的,可查看 GitLab 的提交历史。


🧠 Learnings used
Learnt from: zstack-robot-2
Repo: MatheMatrix/zstack PR: 3431
File: network/src/main/java/org/zstack/network/l3/zstack ipam.md:15-19
Timestamp: 2026-03-06T12:57:49.180Z
Learning: In the MatheMatrix/zstack repository, Chinese-language internal documentation markdown files (e.g., `network/src/main/java/org/zstack/network/l3/zstack ipam.md`) are intentionally written in Chinese for the Chinese-speaking development team. These files are exempt from the coding guideline that prohibits Chinese text (`代码里不应当有有中文`), which applies only to source code such as error messages and code comments, not to dedicated Chinese documentation files. Do not flag Chinese content in such documentation markdown files during review.

Learnt from: zstack-robot-2
Repo: MatheMatrix/zstack PR: 2435
File: storage/src/main/java/org/zstack/storage/snapshot/group/VolumeSnapshotGroupBase.java:47-47
Timestamp: 2025-08-14T06:56:19.585Z
Learning: 在VolumeSnapshotGroupBase.java中,VmInstanceResourceMetadataManager的注入和SKIP_RESOURCE_ROLLBACK标记虽然在当前版本中未被使用,但这些导入在大型重构PR中是有意为之的,用于保持代码一致性或为后续功能做准备。

Learnt from: MatheMatrix
Repo: MatheMatrix/zstack PR: 2418
File: compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java:3873-3885
Timestamp: 2025-08-12T05:52:18.323Z
Learning: 在 compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java 的 setNoIpAllocationStaticIp() 流程中,静态 IP 标签删除已统一在 change-ip-in-database 步骤对 voRemoveSet 循环处理:对收集到的旧 IPv4/IPv6 UsedIpVO 均调用 ipOperator.deleteStaticIpByVmUuidAndL3Uuid(vmUuid, l3Uuid, IPv6NetworkUtils.ipv6AddessToTagValue(ip)) 清理对应的 static-ip 标签。因此无需在分支里分别删除 IPv4/IPv6 的标签。

Learnt from: zstack-robot-1
Repo: MatheMatrix/zstack PR: 2360
File: network/src/main/java/org/zstack/network/l3/L3BasicNetwork.java:449-490
Timestamp: 2025-08-04T04:48:19.103Z
Learning: ZStack项目在cherry-pick操作中,即使发现了性能优化机会(如IP地址批量保存的内存优化),也严格遵循不做额外修改的政策,优先保证cherry-pick的完整性和一致性。

Learnt from: ZStack-Robot
Repo: MatheMatrix/zstack PR: 2489
File: storage/src/main/java/org/zstack/storage/snapshot/VolumeTree.java:471-545
Timestamp: 2025-08-22T05:36:40.467Z
Learning: In ZStack项目中,对于核心逻辑相关的代码,团队倾向于暂时不进行重构,即使代码复杂度较高,优先保证核心功能的稳定性。

Learnt from: ZStack-Robot
Repo: MatheMatrix/zstack PR: 2325
File: compute/src/main/java/org/zstack/compute/vm/VmMigrateCallExtensionFlow.java:29-29
Timestamp: 2025-07-24T05:53:10.246Z
Learning: 在ZStack项目的重构过程中,团队采用务实的优先级策略:优先完成影响运行时功能的代码重构,对于注释中的旧引用等非功能性问题可以延后处理,因为这些不会影响系统的实际运行。

Learnt from: zstack-robot-1
Repo: MatheMatrix/zstack PR: 2360
File: compute/src/main/java/org/zstack/compute/vm/StaticIpOperator.java:364-501
Timestamp: 2025-08-04T03:19:07.454Z
Learning: In ZStack project, developers may prefer to keep related validation logic together in a single method (like validateStaticIpTagsInApiMessage with 130+ lines) when the architecture is clear and there's no code reuse, rather than splitting into smaller methods based purely on single responsibility principle.

Learnt from: ZStack-Robot
Repo: MatheMatrix/zstack PR: 2419
File: plugin/sdnController/src/main/java/org/zstack/sdnController/SdnControllerBase.java:986-1067
Timestamp: 2025-08-13T02:31:40.023Z
Learning: 在ZStack项目中,团队倾向于保持统一的业务流程方法不进行拆分,即使方法较长也会维持在单个方法中以保持业务逻辑的完整性。

Learnt from: zstack-robot-2
Repo: MatheMatrix/zstack PR: 2496
File: plugin/sharedMountPointPrimaryStorage/src/main/java/org/zstack/storage/primary/smp/KvmBackend.java:2545-2566
Timestamp: 2025-08-24T07:35:42.793Z
Learning: 在 ZStack 代码库中,当响应类包含数值字段(如 size、actualSize)时,优先使用原始类型(long)而不是包装类型(Long),以避免 NPE 风险和不必要的装箱/拆箱操作。如果 Agent 端可能不设置该字段,应在 Agent 端确保设置默认值,而不是在使用端做 null 检查。

@NotFound(action = NotFoundAction.IGNORE)
@JoinColumn(name = "uuid", referencedColumnName = "volumeUuid", insertable = false, updatable = false)
private VmLocalVolumeCacheVO localVolumeCache;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Comment from yaohua.wu:

这里不适合自动关联查询,本身你的取名语义就带有Vm属性,Volume本身应该是Vm的子资源,VolumeCache的逻辑统一处理就行了,没有就skip,不需要跟随VolumeAO出来

private String rootVolumeCacheMode;

@APIParam(required = false)
private Map<Integer, VolumeCacheConfig> dataDiskCacheConfigOnIndex;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Comment from yaohua.wu:

这上面几个是有systemTag的吧,可以放到rootVolumeSystemTags和dataVolumeSystemTagsOnIndex里

@MatheMatrix MatheMatrix force-pushed the sync/haidong.pang/feature/ZSTAC-81715@@3 branch from f2c26f8 to 8092ad2 Compare March 18, 2026 03:46

public static SystemTag FAST_REVERT = new SystemTag("fast::revert", VolumeVO.class);

public static PatternedSystemTag VOLUME_CACHE_ENABLE = new PatternedSystemTag("volumeCache::enable", VolumeVO.class);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Comment from yaohua.wu:

EphemeralSystemTag

String LOCAL_VOLUME_CACHE_GET_CACHE_CAPACITY_PATH = "/localvolumecache/getcapacity";

String KVM_ATTACH_VOLUME_CACHE_PATH = "/vm/volume/cache/attach";
String KVM_DETACH_VOLUME_CACHE_PATH = "/vm/volume/cache/detach";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Comment from yaohua.wu:

localCache相关的放到premium的localVolumeCache模块里去

@MatheMatrix MatheMatrix force-pushed the sync/haidong.pang/feature/ZSTAC-81715@@3 branch 5 times, most recently from 61bec1a to 0f3a5f4 Compare March 19, 2026 09:45
Related: ZSTAC-81715

Change-Id: I6d747a786b73746a6f756a6c72787479726e6765
@MatheMatrix MatheMatrix force-pushed the sync/haidong.pang/feature/ZSTAC-81715@@3 branch from 0f3a5f4 to ab160bd Compare March 19, 2026 10:01
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