Skip to content

<fix>[storage]: filter non-preferred backup storage types before sorting#3395

Open
zstack-robot-2 wants to merge 1 commit into5.5.12from
sync/yaohua.wu/bugfix/ZSTAC-71706
Open

<fix>[storage]: filter non-preferred backup storage types before sorting#3395
zstack-robot-2 wants to merge 1 commit into5.5.12from
sync/yaohua.wu/bugfix/ZSTAC-71706

Conversation

@zstack-robot-2
Copy link
Collaborator

ZSTAC-71706

indexOf() returns -1 for non-preferred backup storage types, causing them to sort before preferred ones. Fix: filter by preferBsTypes first, then sort. Added dedicated error code ORG_ZSTACK_STORAGE_ADDON_PRIMARY_10015 with i18n entries in all 10 locales.

Related: ZSTAC-71706

sync from gitlab !9243

indexOf() returns -1 for non-preferred types, causing them
to sort before preferred ones. Filter by preferBsTypes first
and return error when no matching backup storage available.

Use dedicated error code ORG_ZSTACK_STORAGE_ADDON_PRIMARY_10015
for the new "no matching backup storage" condition, with i18n
entries in all 10 locales.

Related: ZSTAC-71706
Change-Id: Ia3af38cc50e69132a1c769180792363495c1080f
@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

总体概览

外部主存储备份存储选择逻辑重构,引入专用的过滤排序方法以优先处理首选类型,并添加相应的错误处理和验证测试。同时补充新的错误代码常量。

变更清单

分组 / 文件 变更概要
存储适配器主逻辑重构
storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java
将原有的直接排序替换为先过滤后排序的两步流程,添加过滤失败时的错误提前返回逻辑,并引入新的静态辅助方法 filterAndSortByPreferredTypes 来封装过滤排序逻辑。
过滤排序功能测试
test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/ExternalPrimaryStorageSelectBackupStorageCase.groovy
新增 Groovy 测试类,涵盖非首选类型过滤排除、多首选类型按偏好序列排序、空结果处理、单一匹配等多个测试场景,以验证 filterAndSortByPreferredTypes 的功能正确性。
错误代码常量扩展
utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java
新增静态常量 ORG_ZSTACK_STORAGE_ADDON_PRIMARY_10015 用于存储适配器主存储模块的错误标识。

代码审查工作量估算

🎯 2 (简单) | ⏱️ ~12 分钟

小兔倒腾储存库,
过滤排序逐个梳,
优先类型有序列,
错误提前早预拦,
测试覆盖样样全,
代码清晰真不赖~🐰✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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]: 格式,完整且简洁地描述了主要改动:过滤非首选备份存储类型后再排序。
Description check ✅ Passed PR描述清晰关联到变更集,说明了问题根源(indexOf返回-1导致排序错误)和解决方案(先过滤后排序),提及相关JIRA票和错误码。

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch sync/yaohua.wu/bugfix/ZSTAC-71706

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.0)
utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java

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

@MatheMatrix
Copy link
Owner

Comment from yaohua.wu:

Review: MR !9243 — ZSTAC-71706

含VCenter的环境中ZBS存储上创建的虚机全量克隆失败

业务背景

ExternalPrimaryStorage.handle(SelectBackupStorageMsg) 中使用 List.indexOf() 排序备份存储,对不在 preferBsTypes 中的类型返回 -1,导致非首选类型(如 VCenterBackupStorage)被错误排到最前面。后续操作向 VCenter BS 发送不支持的消息,克隆失败。

Critical

Warning

  1. [ExternalPrimaryStorageSelectBackupStorageCase.groovy: 全文] CRLF 行尾 — 新增的测试文件使用了 \r\n(Windows 风格)行尾,与项目其余代码(LF)不一致。建议在提交前转换为 LF:sed -i 's/\r$//' <file>。虽然不影响功能,但可能导致 diff 噪音和跨平台编辑问题。

Suggestion

  1. [ExternalPrimaryStorage.java:516] 错误消息中 List.toString() 格式preferBsTypes.toString() 会输出 [ImageStoreBackupStorage](带方括号)。建议用 String.join(", ", preferBsTypes) 使消息更清晰,输出为 ImageStoreBackupStorage 而非 [ImageStoreBackupStorage]

  2. [ExternalPrimaryStorage.java:524] 方法可见性filterAndSortByPreferredTypes 为 package-visible static 方法,注释说明了 // package-visible for testing,这是合理的设计。但如果后续有其他模块需要复用此逻辑,可考虑提升到 utility 类。当前无需修改。

  3. [ExternalPrimaryStorageSelectBackupStorageCase.groovy] 集成测试覆盖 — 当前测试直接调用 static 方法验证 filter+sort 逻辑,覆盖充分(6 个场景)。如果条件允许,可补充一个通过 SelectBackupStorageMsg 消息驱动的集成测试,验证完整消息处理链路(包括 SQL 查询返回空列表时的行为)。优先级低,当前单元测试已足够验证核心逻辑。

亮点

  • 修复策略选择合理:采用 filter+sort(方案 B)而非仅修正 comparator(方案 A),更加严格——非首选类型被彻底排除,避免了 fallback 到不兼容的 BS 类型。
  • 附带修复了原有的空列表风险:原代码 availableBs.get(0) 在 SQL 返回空列表时会抛 IndexOutOfBoundsException,新代码通过 filter 后的空检查统一处理了此边界条件。
  • i18n 覆盖完整:新增错误码 ORG_ZSTACK_STORAGE_ADDON_PRIMARY_10015 的 10 个语言翻译齐全。
  • 测试覆盖充分:6 个测试场景覆盖了 bug 复现、修复验证、多类型排序、空结果、全首选、单首选等关键路径。

Verdict: APPROVED

修复正确地解决了根因问题,方案稳健,测试充分。仅有 CRLF 行尾问题建议修复后合并。


🤖 Robot Reviewer

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java`:
- Around line 527-534: The method filterAndSortByPreferredTypes is
package-private but is used from a test in a different package; make it public
(change its signature to public static List<BackupStorageVO>
filterAndSortByPreferredTypes(...)) and add a short javadoc noting it’s exposed
for tests/tools, or alternatively move the
ExternalPrimaryStorageSelectBackupStorageCase test into the same package as
ExternalPrimaryStorage (org.zstack.storage.addon.primary) so package-private
visibility is valid.

ℹ️ Review info

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

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2442f28 and 0ecb2e3.

⛔ Files ignored due to path filters (10)
  • conf/i18n/globalErrorCodeMapping/global-error-de-DE.json is excluded by !**/*.json
  • conf/i18n/globalErrorCodeMapping/global-error-en_US.json is excluded by !**/*.json
  • conf/i18n/globalErrorCodeMapping/global-error-fr-FR.json is excluded by !**/*.json
  • conf/i18n/globalErrorCodeMapping/global-error-id-ID.json is excluded by !**/*.json
  • conf/i18n/globalErrorCodeMapping/global-error-ja-JP.json is excluded by !**/*.json
  • conf/i18n/globalErrorCodeMapping/global-error-ko-KR.json is excluded by !**/*.json
  • conf/i18n/globalErrorCodeMapping/global-error-ru-RU.json is excluded by !**/*.json
  • conf/i18n/globalErrorCodeMapping/global-error-th-TH.json is excluded by !**/*.json
  • conf/i18n/globalErrorCodeMapping/global-error-zh_CN.json is excluded by !**/*.json
  • conf/i18n/globalErrorCodeMapping/global-error-zh_TW.json is excluded by !**/*.json
📒 Files selected for processing (3)
  • storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java
  • test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/ExternalPrimaryStorageSelectBackupStorageCase.groovy
  • utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java

Comment on lines +527 to +534
// package-visible for testing
static List<BackupStorageVO> filterAndSortByPreferredTypes(
List<BackupStorageVO> candidates, List<String> preferBsTypes) {
return candidates.stream()
.filter(bs -> preferBsTypes.contains(bs.getType()))
.sorted(Comparator.comparingInt(o -> preferBsTypes.indexOf(o.getType())))
.collect(Collectors.toList());
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== ExternalPrimaryStorage.java package + method signature =="
sed -n '1,5p;525,535p' storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java

echo
echo "== Test package + call site =="
sed -n '1,8p;60,66p' test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/ExternalPrimaryStorageSelectBackupStorageCase.groovy

echo
echo "== All call sites =="
rg -n --type=java --type=groovy 'filterAndSortByPreferredTypes\s*\('

Repository: MatheMatrix/zstack

Length of output: 3335


filterAndSortByPreferredTypes 改为 public,或将测试类移到同包。

方法 filterAndSortByPreferredTypes 目前是包级可见(无访问修饰符),但测试类在不同包 org.zstack.test.integration.storage.primary.addon 中调用它。根据 Java 可见性规则,包级可见方法不应被不同包中的类访问。

虽然 Groovy 的运行时反射机制可能允许此类访问,但这违反了 Java 封装原则,且易导致维护问题。建议:

  • 改为 public static(添加注释说明仅供测试和工具方法使用),或
  • 将 ExternalPrimaryStorageSelectBackupStorageCase 测试类移至 org.zstack.storage.addon.primary.test 或同包位置。
🤖 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/addon/primary/ExternalPrimaryStorage.java`
around lines 527 - 534, The method filterAndSortByPreferredTypes is
package-private but is used from a test in a different package; make it public
(change its signature to public static List<BackupStorageVO>
filterAndSortByPreferredTypes(...)) and add a short javadoc noting it’s exposed
for tests/tools, or alternatively move the
ExternalPrimaryStorageSelectBackupStorageCase test into the same package as
ExternalPrimaryStorage (org.zstack.storage.addon.primary) so package-private
visibility is valid.

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