<feature>[vm_local_volume_cache]: vm local volume cache imp#3484
<feature>[vm_local_volume_cache]: vm local volume cache imp#3484MatheMatrix wants to merge 1 commit intofeature-5.5.6-local-cachefrom
Conversation
Related: ZSTAC-81715 Change-Id: I6d747a786b73746a6f756a6c72787479726e6765
概述该变更引入虚拟机本地卷缓存功能,包含数据库表定义、缓存配置API消息、VM创建流程的缓存字段传播、KVM代理命令、卷分配流程增强以及相应的库存和错误代码定义。 变更
代码审查工作量评估🎯 4 (复杂) | ⏱️ ~60 分钟 诗歌
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
📝 Coding Plan
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ast-grep (0.41.1)compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.javaplugin/kvm/src/main/java/org/zstack/kvm/KVMHost.javautils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.javaComment |
There was a problem hiding this comment.
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文档元信息仍为占位内容
category、desc(以及请求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新增缓存参数缺少说明文本
enableRootVolumeCache、cacheMode、rootVolumeCachePoolUuid、rootVolumeCacheMode、dataDiskCacheConfigOnIndex的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/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 持久化在事务外执行
cacheVO在SQLBatchWithReturn执行完成后单独持久化,如果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: 小格式问题: 缺少空格和多余空格代码逻辑正确,但有两处小的格式问题:
- 第 149 行:
if (cacheInv != null){在{前缺少空格- 第 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
⛔ Files ignored due to path filters (44)
conf/globalConfig/vmLocalVolumeCachePool.xmlis excluded by!**/*.xmlconf/persistence.xmlis excluded by!**/*.xmlconf/serviceConfig/host.xmlis excluded by!**/*.xmlconf/serviceConfig/vmLocalVolumeCache.xmlis excluded by!**/*.xmlconf/serviceConfig/vmLocalVolumeCachePool.xmlis excluded by!**/*.xmlconf/springConfigXml/localVolumeCache.xmlis excluded by!**/*.xmlconf/zstack.xmlis excluded by!**/*.xmlsdk/src/main/java/SourceClassMap.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/CacheTO.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/ChangeVmLocalVolumeCachePoolStateAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/ChangeVmLocalVolumeCachePoolStateResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/CreateOvnControllerVmAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/CreateVmInstanceAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/CreateVmLocalVolumeCachePoolAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/CreateVmLocalVolumeCachePoolResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DeleteVmLocalVolumeCachePoolAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DeleteVmLocalVolumeCachePoolResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DisableVolumeCacheAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DisableVolumeCacheResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/EnableVolumeCacheAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/EnableVolumeCacheResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/ExtendVmLocalVolumeCachePoolAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/ExtendVmLocalVolumeCachePoolResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/GetBlockDevicesAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/GetBlockDevicesResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/HostBlockDeviceStruct.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/HostInventory.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/QueryVmLocalVolumeCachePoolAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/QueryVmLocalVolumeCachePoolResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/ReconnectVmLocalVolumeCachePoolAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/ReconnectVmLocalVolumeCachePoolResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/SyncVmLocalVolumeCachePoolCapacityAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/SyncVmLocalVolumeCachePoolCapacityResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/UpdateVmLocalVolumeCachePoolAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/UpdateVmLocalVolumeCachePoolResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheInventory.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheMode.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolInventory.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolState.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCachePoolStatus.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/VmLocalVolumeCacheState.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/VolumeInventory.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/VolumeTO.javais excluded by!sdk/**test/src/test/resources/springConfigXml/license.xmlis excluded by!**/*.xml
📒 Files selected for processing (64)
compute/src/main/java/org/zstack/compute/host/HostBase.javacompute/src/main/java/org/zstack/compute/vm/InstantiateVmFromNewCreatedStruct.javacompute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.javacompute/src/main/java/org/zstack/compute/vm/VmAllocateVolumeFlow.javacompute/src/main/java/org/zstack/compute/vm/VmInstanceBase.javacompute/src/main/java/org/zstack/compute/vm/VmInstanceManagerImpl.javacompute/src/main/java/org/zstack/compute/vm/VmInstanceUtils.javacompute/src/main/java/org/zstack/compute/vm/VmInstantiateOtherDiskFlow.javaconf/db/upgrade/V5.5.12__schema.sqlheader/src/main/java/org/zstack/header/host/APIGetBlockDevicesEvent.javaheader/src/main/java/org/zstack/header/host/APIGetBlockDevicesEventDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsg.javaheader/src/main/java/org/zstack/header/host/APIGetBlockDevicesMsgDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/host/GetBlockDevicesOnHostMsg.javaheader/src/main/java/org/zstack/header/host/GetBlockDevicesOnHostReply.javaheader/src/main/java/org/zstack/header/host/HostBlockDeviceStruct.javaheader/src/main/java/org/zstack/header/host/HostBlockDeviceStructDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/host/HostInventory.javaheader/src/main/java/org/zstack/header/host/HostVO.javaheader/src/main/java/org/zstack/header/localVolumeCache/CachePoolMetadata.javaheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheConstant.javaheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventory.javaheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheInventoryDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheMode.javaheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheModeDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityInventory.javaheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO.javaheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolCapacityVO_.javaheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventory.javaheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolInventoryDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolState.javaheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStateDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatus.javaheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolStatusDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO.javaheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCachePoolVO_.javaheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheState.javaheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheStateDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO.javaheader/src/main/java/org/zstack/header/localVolumeCache/VmLocalVolumeCacheVO_.javaheader/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsg.javaheader/src/main/java/org/zstack/header/vm/APICreateVmInstanceMsgDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/vm/CreateVmInstanceMsg.javaheader/src/main/java/org/zstack/header/vm/InstantiateNewCreatedVmInstanceMsg.javaheader/src/main/java/org/zstack/header/vm/VmInstanceSpec.javaheader/src/main/java/org/zstack/header/volume/VolumeAO.javaheader/src/main/java/org/zstack/header/volume/VolumeInventory.javaplugin/cbd/src/main/java/org/zstack/cbd/kvm/KvmCbdNodeServer.javaplugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageFactory.javaplugin/iscsi/src/main/java/org/zstack/iscsi/kvm/KvmIscsiNodeServer.javaplugin/kvm/src/main/java/org/zstack/kvm/CacheTO.javaplugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.javaplugin/kvm/src/main/java/org/zstack/kvm/KVMApiInterceptor.javaplugin/kvm/src/main/java/org/zstack/kvm/KVMAttachVolumeExtensionPoint.javaplugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.javaplugin/kvm/src/main/java/org/zstack/kvm/KVMExtensionEmitter.javaplugin/kvm/src/main/java/org/zstack/kvm/KVMHost.javaplugin/kvm/src/main/java/org/zstack/kvm/VolumeTO.javaplugin/vhost/src/main/java/org/zstack/vhost/kvm/KvmVhostNodeServer.javastorage/src/main/java/org/zstack/storage/volume/VolumeApiInterceptor.javastorage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.javastorage/src/main/java/org/zstack/storage/volume/VolumeSystemTags.javatestlib/src/main/java/org/zstack/testlib/ApiHelper.groovyutils/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
| if (dataDiskCacheConfigs != null && !dataDiskCacheConfigs.isEmpty()) { | ||
| for (Integer index : dataDiskCacheConfigs.keySet()) { | ||
| APICreateVmInstanceMsg.VolumeCacheConfig dataDiskCacheConfig = dataDiskCacheConfigs.get(index); | ||
| totalCacheSize += spec.getDataDiskOfferings().get(index).getDiskSize(); | ||
| } | ||
|
|
||
| } | ||
| return totalCacheSize; |
There was a problem hiding this comment.
潜在的 IndexOutOfBoundsException 风险
代码使用 dataDiskCacheConfigs.keySet() 中的 index 直接访问 spec.getDataDiskOfferings().get(index),但没有验证 index 是否在 DataDiskOfferings 列表的有效范围内。如果用户配置的 cache 索引超出了实际数据盘数量,将抛出 IndexOutOfBoundsException。
此外,第 161 行声明的变量 dataDiskCacheConfig 未被使用,属于无用代码。
🛡️ 建议添加边界检查并移除无用变量
Map<Integer, APICreateVmInstanceMsg.VolumeCacheConfig> dataDiskCacheConfigs = spec.getDataDiskCacheConfigOnIndex();
if (dataDiskCacheConfigs != null && !dataDiskCacheConfigs.isEmpty()) {
+ List<DiskOfferingInventory> dataDisks = spec.getDataDiskOfferings();
for (Integer index : dataDiskCacheConfigs.keySet()) {
- APICreateVmInstanceMsg.VolumeCacheConfig dataDiskCacheConfig = dataDiskCacheConfigs.get(index);
- totalCacheSize += spec.getDataDiskOfferings().get(index).getDiskSize();
+ if (index >= 0 && index < dataDisks.size()) {
+ totalCacheSize += dataDisks.get(index).getDiskSize();
+ }
}
-
}📝 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.
| if (dataDiskCacheConfigs != null && !dataDiskCacheConfigs.isEmpty()) { | |
| for (Integer index : dataDiskCacheConfigs.keySet()) { | |
| APICreateVmInstanceMsg.VolumeCacheConfig dataDiskCacheConfig = dataDiskCacheConfigs.get(index); | |
| totalCacheSize += spec.getDataDiskOfferings().get(index).getDiskSize(); | |
| } | |
| } | |
| return totalCacheSize; | |
| if (dataDiskCacheConfigs != null && !dataDiskCacheConfigs.isEmpty()) { | |
| List<DiskOfferingInventory> dataDisks = spec.getDataDiskOfferings(); | |
| for (Integer index : dataDiskCacheConfigs.keySet()) { | |
| if (index >= 0 && index < dataDisks.size()) { | |
| totalCacheSize += dataDisks.get(index).getDiskSize(); | |
| } | |
| } | |
| } | |
| return totalCacheSize; |
🤖 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 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.
| if (!spec.getDataDiskCacheConfigOnIndex().isEmpty() && !CollectionUtils.isEmpty(spec.getDataDiskOfferings())) { | ||
| 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 = spec.getDataDiskCacheConfigOnIndex().get(index); | ||
| if (config != null) { | ||
| dataVolumeSpecs.get(index).setEnableVolumeCache(true); | ||
| dataVolumeSpecs.get(index).setCachePoolUuid(config.getCachePoolUuid()); | ||
| dataVolumeSpecs.get(index).setCacheMode(config.getCacheMode()); | ||
| } | ||
| }); | ||
| } |
There was a problem hiding this comment.
给 dataDiskCacheConfigOnIndex 加空值保护。
Line 76 直接对新 map 调 isEmpty(),和 Lines 65-75 对同类可选参数的空安全处理不一致。调用方不传这个字段时,这里会在 volume 分配前直接 NPE。
💡 建议修改
- if (!spec.getDataDiskCacheConfigOnIndex().isEmpty() && !CollectionUtils.isEmpty(spec.getDataDiskOfferings())) {
+ Map<Integer, APICreateVmInstanceMsg.VolumeCacheConfig> dataDiskCacheConfigs = spec.getDataDiskCacheConfigOnIndex();
+ if (dataDiskCacheConfigs != null && !dataDiskCacheConfigs.isEmpty()
+ && !CollectionUtils.isEmpty(spec.getDataDiskOfferings())) {
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 = spec.getDataDiskCacheConfigOnIndex().get(index);
+ APICreateVmInstanceMsg.VolumeCacheConfig config = dataDiskCacheConfigs.get(index);
if (config != null) {
dataVolumeSpecs.get(index).setEnableVolumeCache(true);
dataVolumeSpecs.get(index).setCachePoolUuid(config.getCachePoolUuid());🤖 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 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.
| vspec.setEnableVolumeCache(spec.getEnableRootVolumeCache()); | ||
| vspec.setCachePoolUuid(spec.getRootVolumeCachePoolUuid()); | ||
| vspec.setCacheMode(spec.getRootVolumeCacheMode()); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
fd 'VmInstanceSpec\.java$' | while read -r f; do
echo "== $f =="
rg -n -C3 'Boolean\s+enableRootVolumeCache|getEnableRootVolumeCache|setEnableVolumeCache\(' "$f"
doneRepository: MatheMatrix/zstack
Length of output: 1027
🏁 Script executed:
#!/bin/bash
# Find VmAllocateVolumeFlow.java and check lines around 106-108
fd 'VmAllocateVolumeFlow\.java$' | while read -r f; do
echo "== $f =="
sed -n '100,115p' "$f"
echo ""
echo "== Checking VolumeSpec setter methods =="
done
# Also find VolumeSpec to check all setter parameter types
echo "=== VolumeSpec class ==="
fd 'VolumeSpec\.java$' | while read -r f; do
echo "== $f =="
rg -n -A1 'setCachePoolUuid|setCacheMode|setEnableVolumeCache' "$f"
doneRepository: MatheMatrix/zstack
Length of output: 1130
🏁 Script executed:
#!/bin/bash
# Find VolumeSpec more broadly
fd 'VolumeSpec' --type f | grep -E '\.java$' | head -20
# Search for setCachePoolUuid and setCacheMode definitions
echo "=== Searching for setCachePoolUuid and setCacheMode ==="
rg -n 'setCachePoolUuid|setCacheMode' --type java -A2 | head -50Repository: MatheMatrix/zstack
Length of output: 4345
🏁 Script executed:
#!/bin/bash
# Search for getRootVolumeCachePoolUuid and getRootVolumeCacheMode getters in VmInstanceSpec
rg -n 'getRootVolumeCachePoolUuid|getRootVolumeCacheMode|rootVolumeCachePoolUuid|rootVolumeCacheMode' header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java -A2Repository: MatheMatrix/zstack
Length of output: 788
避免在根盘默认路径上对 Boolean 自动拆箱。
Line 106 中,spec.getEnableRootVolumeCache() 返回 Boolean(装箱类型),但 vspec.setEnableVolumeCache() 期望 boolean(原始类型)。若值为 null,此行会抛出 NullPointerException。应显式归一化为 false:
- vspec.setEnableVolumeCache(spec.getEnableRootVolumeCache());
+ vspec.setEnableVolumeCache(Boolean.TRUE.equals(spec.getEnableRootVolumeCache()));Line 107 与 108 的 setCachePoolUuid() 和 setCacheMode() 接收 String 类型,无此风险。
🤖 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 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.
| if (vspec.getEnableVolumeCache()) { | ||
| tags.add("volumeCache::enable"); | ||
| tags.add("volumeCache::cacheMode::" + vspec.getCacheMode()); | ||
| if(vspec.getCachePoolUuid() != null) { | ||
| tags.add("volumeCache::poolUuid::" + vspec.getCachePoolUuid()); | ||
| } | ||
| } |
There was a problem hiding this comment.
不要把未配置的 cacheMode 持久化成坏 tag。
Line 134 对 cacheMode 没有空值保护。当前分支已经对 cachePoolUuid 做了空判,但这里会把缺省值写成 volumeCache::cacheMode::null,后续读取方拿到的是持久化坏数据,而不是“未配置”。
💡 建议修改
if (vspec.getEnableVolumeCache()) {
tags.add("volumeCache::enable");
- tags.add("volumeCache::cacheMode::" + vspec.getCacheMode());
+ 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());
}🤖 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 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".
| private VmLocalVolumeCacheVO initCacheRecord() { | ||
| VmLocalVolumeCacheVO existing = Q.New(VmLocalVolumeCacheVO.class) | ||
| .eq(VmLocalVolumeCacheVO_.volumeUuid, volumeInventory.getUuid()) | ||
| .find(); | ||
| if (existing != null) { | ||
| return existing; | ||
| } | ||
| VmLocalVolumeCacheVO cacheVO = new VmLocalVolumeCacheVO(); | ||
| cacheVO.setUuid(Platform.getUuid()); | ||
| cacheVO.setVolumeUuid(volumeInventory.getUuid()); | ||
| cacheVO.setState(VmLocalVolumeCacheState.Uninstantiated); | ||
| cacheVO.setCacheMode(VmLocalVolumeCacheMode.valueOf(diskAO.getCacheMode())); | ||
| cacheVO.setPoolUuid(diskAO.getCachePoolUuid()); | ||
| dbf.persist(cacheVO); | ||
| return cacheVO; | ||
| } |
There was a problem hiding this comment.
缓存记录创建缺少回滚清理,失败路径会遗留脏数据。
initCacheRecord() 会落库,但放在 NoRollbackFlow 中;后续任一环节失败触发外层回滚时,卷可能被删除而缓存记录仍保留,形成孤儿数据。
🧹 建议修复(记录“本次是否新建”,并在 rollback 清理)
@@
- VolumeInventory volumeInventory;
+ VolumeInventory volumeInventory;
+ String createdCacheRecordUuid;
@@
private VmLocalVolumeCacheVO initCacheRecord() {
VmLocalVolumeCacheVO existing = Q.New(VmLocalVolumeCacheVO.class)
.eq(VmLocalVolumeCacheVO_.volumeUuid, volumeInventory.getUuid())
.find();
if (existing != null) {
return existing;
}
VmLocalVolumeCacheVO cacheVO = new VmLocalVolumeCacheVO();
cacheVO.setUuid(Platform.getUuid());
@@
cacheVO.setPoolUuid(diskAO.getCachePoolUuid());
dbf.persist(cacheVO);
+ createdCacheRecordUuid = cacheVO.getUuid();
return cacheVO;
}// 建议补充到 rollback(...) 开头(位于当前变更段之外)
if (createdCacheRecordUuid != null) {
VmLocalVolumeCacheVO cacheVO = Q.New(VmLocalVolumeCacheVO.class)
.eq(VmLocalVolumeCacheVO_.uuid, createdCacheRecordUuid)
.find();
if (cacheVO != null) {
dbf.remove(cacheVO);
}
}Also applies to: 421-436
🤖 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 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).
| private Boolean enableCache; | ||
| private String cachePoolUuid; | ||
| private String cacheMode; |
There was a problem hiding this comment.
补齐磁盘级 cacheMode 的约束,避免非法值下沉
Line 243 和 Line 368 的 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 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.
| public static CacheTO valueOf(VmLocalVolumeCacheInventory inv) { | ||
| CacheTO to = new CacheTO(); | ||
| to.setCacheUuid(inv.getUuid()); | ||
| to.setPoolUuid(inv.getPoolUuid()); | ||
| to.setInstallPath(inv.getInstallPath()); | ||
| to.setCacheMode(inv.getCacheMode().name()); | ||
| return to; | ||
| } |
There was a problem hiding this comment.
潜在的空指针异常风险。
在 valueOf 方法中,第 48 行调用 inv.getCacheMode().name() 时,如果 getCacheMode() 返回 null,将抛出 NullPointerException。
🔧 建议修复
public static CacheTO valueOf(VmLocalVolumeCacheInventory inv) {
CacheTO to = new CacheTO();
to.setCacheUuid(inv.getUuid());
to.setPoolUuid(inv.getPoolUuid());
to.setInstallPath(inv.getInstallPath());
- to.setCacheMode(inv.getCacheMode().name());
+ to.setCacheMode(inv.getCacheMode() != null ? inv.getCacheMode().name() : null);
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/CacheTO.java` around lines 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.
| * Created by frank on 4/24/2015. | ||
| */ | ||
| public interface KVMAttachVolumeExtensionPoint extends KVMConvertVolumeExtensionPoint { | ||
| void preBeforeAttachVolume(KVMHostInventory host, VmInstanceInventory vm, VolumeInventory volume, AttachDataVolumeCmd cmd, Map data, Completion completion); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "[1] 查找所有实现 KVMAttachVolumeExtensionPoint 的类"
impl_files=$(rg -nP --type=java '\bimplements\b[^{;]*\bKVMAttachVolumeExtensionPoint\b' | cut -d: -f1 | sort -u)
printf '%s\n' "$impl_files"
echo
echo "[2] 检查每个实现类是否显式实现 preBeforeAttachVolume"
for f in $impl_files; do
if ! rg -nP '\bpreBeforeAttachVolume\s*\(' "$f" >/dev/null; then
echo "MISSING: $f"
fi
doneRepository: MatheMatrix/zstack
Length of output: 105
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "[1] 查找接口定义文件"
find . -name "KVMAttachVolumeExtensionPoint.java" -type f
echo
echo "[2] 尝试更宽松的搜索方式找实现类"
rg -t java "KVMAttachVolumeExtensionPoint" --no-heading -A 2
echo
echo "[3] 搜索该类在哪些文件中被引用"
rg -t java "KVMAttachVolumeExtensionPoint" -lRepository: MatheMatrix/zstack
Length of output: 5245
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "[1] 检查 KvmVhostNodeServer 中的 preBeforeAttachVolume 实现"
rg -A 10 "preBeforeAttachVolume" plugin/vhost/src/main/java/org/zstack/vhost/kvm/KvmVhostNodeServer.java || echo "NOT FOUND in KvmVhostNodeServer"
echo
echo "[2] 检查 KvmIscsiNodeServer 中的 preBeforeAttachVolume 实现"
rg -A 10 "preBeforeAttachVolume" plugin/iscsi/src/main/java/org/zstack/iscsi/kvm/KvmIscsiNodeServer.java || echo "NOT FOUND in KvmIscsiNodeServer"
echo
echo "[3] 检查 KvmCbdNodeServer 中的 preBeforeAttachVolume 实现"
rg -A 10 "preBeforeAttachVolume" plugin/cbd/src/main/java/org/zstack/cbd/kvm/KvmCbdNodeServer.java || echo "NOT FOUND in KvmCbdNodeServer"
echo
echo "[4] 检查 CephPrimaryStorageFactory 中的 preBeforeAttachVolume 实现"
rg -A 10 "preBeforeAttachVolume" plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageFactory.java || echo "NOT FOUND in CephPrimaryStorageFactory"Repository: MatheMatrix/zstack
Length of output: 2812
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "[1] 查看 KVMAttachVolumeExtensionPoint 接口的完整内容"
cat plugin/kvm/src/main/java/org/zstack/kvm/KVMAttachVolumeExtensionPoint.java
echo
echo "[2] 查看其父接口 KVMConvertVolumeExtensionPoint"
cat plugin/kvm/src/main/java/org/zstack/kvm/KVMConvertVolumeExtensionPoint.javaRepository: MatheMatrix/zstack
Length of output: 1373
接口新增抽象方法破坏向后兼容性,建议改为 default 实现。
直接新增抽象方法将强制所有实现类(包括仓外插件)同步修改,否则会编译失败。为保持向后兼容性,应提供默认实现。
建议修改
- void preBeforeAttachVolume(KVMHostInventory host, VmInstanceInventory vm, VolumeInventory volume, AttachDataVolumeCmd cmd, Map data, Completion completion);
+ default void preBeforeAttachVolume(KVMHostInventory host, VmInstanceInventory vm, VolumeInventory volume, AttachDataVolumeCmd cmd, Map data, Completion completion) {
+ completion.success();
+ }🤖 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/KVMAttachVolumeExtensionPoint.java`
at 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.
| 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); | ||
| } | ||
| } |
There was a problem hiding this comment.
preBeforeAttachVolume 的异步编排有严重问题:同一 completion 被多次并发回调。
Line 395-399 目前会把同一个 completion 直接传给所有扩展,容易出现重复 success/fail、提前放行或状态机错乱。应改为串行链式执行,最终只回调一次。
🐛 建议修复
- 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);
- }
- }
+ 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) {
+ 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 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.
| FlowChain chain = FlowChainBuilder.newSimpleFlowChain(); | ||
| chain.setName(String.format("attach-volume-%s", msg.getInventory().getUuid())); | ||
| chain.then(new ShareFlow() { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '== FlowChainBuilder definitions =='
fd 'FlowChainBuilder.java$' | while read -r f; do
echo "--- $f ---"
sed -n '1,240p' "$f"
done
echo
echo '== ShareFlow definitions =='
fd 'ShareFlow.java$' | while read -r f; do
echo "--- $f ---"
sed -n '1,260p' "$f"
done
echo
echo '== SimpleFlowChain definitions =='
fd 'SimpleFlowChain.java$' | while read -r f; do
echo "--- $f ---"
sed -n '1,260p' "$f"
done
echo
echo '== ShareFlow usages in the repository =='
rg -n -C2 --type java 'newShareFlowChain\(\)|newSimpleFlowChain\(\)|new ShareFlow\('Repository: MatheMatrix/zstack
Length of output: 50375
将此处的 ShareFlow 改为 newShareFlowChain()
本文件其他 ShareFlow 用法都采用 FlowChainBuilder.newShareFlowChain(),但这里使用了 newSimpleFlowChain()。根据 ShareFlow 的实现,该类在构造时需要设置一个 ShareFlowChain 实例,其 setup() 方法中调用的 flow()、done()、error() 等方法都依赖于 ShareFlowChain 的接口。使用 SimpleFlowChain 会导致类型不匹配,无法正确设置 flow 回调或处理完成/错误逻辑。
♻️ 建议修改
- FlowChain chain = FlowChainBuilder.newSimpleFlowChain();
+ FlowChain chain = FlowChainBuilder.newShareFlowChain();📝 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.
| FlowChain chain = FlowChainBuilder.newSimpleFlowChain(); | |
| chain.setName(String.format("attach-volume-%s", msg.getInventory().getUuid())); | |
| chain.then(new ShareFlow() { | |
| FlowChain chain = FlowChainBuilder.newShareFlowChain(); | |
| chain.setName(String.format("attach-volume-%s", msg.getInventory().getUuid())); | |
| chain.then(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 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.
Related: ZSTAC-81715
Change-Id: I6d747a786b73746a6f756a6c72787479726e6765
sync from gitlab !9343