Skip to content

<feature>[longjob]: standardize LongJob progress detail format#3450

Open
zstack-robot-1 wants to merge 2 commits into5.5.12from
sync/ye.zou/fix/ZSTAC-82318
Open

<feature>[longjob]: standardize LongJob progress detail format#3450
zstack-robot-1 wants to merge 2 commits into5.5.12from
sync/ye.zou/fix/ZSTAC-82318

Conversation

@zstack-robot-1
Copy link
Collaborator

Root Cause

TaskProgressVO.opaque 是自由格式 String,各 agent 的进度详情格式各异:

  • VM 迁移: {remain, total, speed, remaining_migration_time}
  • AI 模型下载: {data: JSON({state, progress:{...}, state_reason})} (双层嵌套)
  • 大多数 LongJob: 不传 detail (null)

UI 无法用统一逻辑解析,导致进度展示困难。

Solution

新增 LongJobProgressDetail 标准化 POJO + LongJobProgressDetailBuilder 适配层:

  • ProgressReportService.inventory()LongJobProgressNotificationMessage.progressUpdated() 中自动将旧格式 opaque 转换为统一结构
  • 通过 TaskProgressInventory.progressDetail 新字段返回
  • DB schema 不变,旧字段 (content/opaque) 保留,向后兼容
  • SDK 同步更新

格式适配

旧格式 适配方式
VM 迁移 {remain,total,speed} processedBytes=total-remain, totalBytes, speedBytesPerSecond, estimatedRemainingSeconds
AI 下载 {data:JSON} 解析嵌套 JSON,映射所有字段,state_reason 支持 String 和 Map
未知格式 放入 extra 字段,不丢弃数据

Testing

  • JDK8 编译验证: header + core + sdk 通过
  • 3 轮 code review 迭代,所有 Critical/Major 修复后 APPROVE

未完成 (Phase 2, 后续迭代)

  • Agent 端改为直接上报标准格式 (kvmagent vm_plugin.py + aios progress_utils.py)
  • 当前 Java 适配层独立可工作,无需 Agent 端改动

Resolves: ZSTAC-82318

sync from gitlab !9308

Add LongJobProgressDetail POJO and LongJobProgressDetailBuilder to normalize opaque progress data into unified typed structure. Three opaque formats parsed: VM migration, AI download, unknown. DB schema unchanged. Agent-side migration deferred to Phase 2.

Resolves: ZSTAC-82318

Change-Id: I70d60ff5e6c8f659f55770e2fbbe56781b238fd5
@coderabbitai
Copy link

coderabbitai bot commented Mar 10, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent 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: b9866522-c4e8-4246-aba2-54a0deeff402

📥 Commits

Reviewing files that changed from the base of the PR and between c0bfab9 and cb554df.

⛔ Files ignored due to path filters (3)
  • sdk/src/main/java/SourceClassMap.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/LongJobProgressDetail.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/TaskProgressInventory.java is excluded by !sdk/**
📒 Files selected for processing (6)
  • core/src/main/java/org/zstack/core/progress/ProgressReportService.java
  • header/src/main/java/org/zstack/header/core/progress/APIGetTaskProgressReply.java
  • header/src/main/java/org/zstack/header/core/progress/LongJobProgressDetail.java
  • header/src/main/java/org/zstack/header/core/progress/LongJobProgressDetailBuilder.java
  • header/src/main/java/org/zstack/header/core/progress/TaskProgressInventory.java
  • header/src/main/java/org/zstack/header/longjob/LongJobProgressNotificationMessage.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • header/src/main/java/org/zstack/header/core/progress/APIGetTaskProgressReply.java
  • core/src/main/java/org/zstack/core/progress/ProgressReportService.java

Walkthrough

本次变更新增并传播了标准化的长任务进度详情:添加 LongJobProgressDetailLongJobProgressDetailBuilder,在 TaskProgressInventoryProgressReportServiceLongJobProgressNotificationMessage 与 API 示例中填充或传递该 progressDetail(由 TaskProgressVO.opaque 解析)。

Changes

Cohort / File(s) Summary
新增进度详情数据模型
header/src/main/java/org/zstack/header/core/progress/LongJobProgressDetail.java
新增 POJO,声明进度相关字段(percent、stage、state、stateReason、processedBytes、totalBytes、processedItems、totalItems、speedBytesPerSecond、estimatedRemainingSeconds、extra)及其 getter/setter。
新增进度详情构建器
header/src/main/java/org/zstack/header/core/progress/LongJobProgressDetailBuilder.java
新增解析器,从 TaskProgressVO.opaque 解析多种格式(AI 下载样式、虚机迁移样式、未知格式)并映射到 LongJobProgressDetail;暴露 fromTaskProgressVO(...),包含容错与类型转换逻辑。
库存与 API 示例更新
header/src/main/java/org/zstack/header/core/progress/TaskProgressInventory.java, header/src/main/java/org/zstack/header/core/progress/APIGetTaskProgressReply.java
TaskProgressInventory 中新增 progressDetail 字段及访问器;在 API 示例中构建并设置 LongJobProgressDetail 示例数据。
上报与通知流改动
core/src/main/java/org/zstack/core/progress/ProgressReportService.java, header/src/main/java/org/zstack/header/longjob/LongJobProgressNotificationMessage.java
在上报处理处调用构建器将 TaskProgressVO 的 opaque 解析为 LongJobProgressDetail,将结果关联到 TaskProgressInventory 并在通知消息中新增并传播 progressDetail 字段。

Sequence Diagram(s)

sequenceDiagram
participant Service as ProgressReportService
participant Builder as LongJobProgressDetailBuilder
participant Inventory as TaskProgressInventory
participant Notifier as LongJobProgressNotificationMessage
participant API as APIGetTaskProgressReply

Service->>Builder: fromTaskProgressVO(vo)
Builder-->>Service: LongJobProgressDetail
Service->>Inventory: inventory.setProgressDetail(detail)
Service->>Notifier: include progressDetail in notification
Notifier->>API: reply/example includes progressDetail
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 新细节钻进灰色匣,
解析不透明把数拆,
百分与字节并排坐,
阶段、速度共诉话,
兔儿欢跃报进展。

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.26% 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 标题完整描述了主要变更:引入标准化的 LongJob 进度详情格式,遵循 [scope]: description 格式,长度为 62 字符,符合 72 字符限制。
Description check ✅ Passed 描述详细说明了变更的根本原因、解决方案、格式适配规则和测试情况,与代码变更高度相关,涵盖了新增的标准化类和适配层的功能。

✏️ 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/ye.zou/fix/ZSTAC-82318
📝 Coding Plan
  • Generate coding plan for human review comments

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

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.

🧹 Nitpick comments (2)
header/src/main/java/org/zstack/header/longjob/LongJobProgressNotificationMessage.java (1)

100-102: 考虑数据一致性风险

progressDetail 同时存储在 TaskProgressInventoryLongJobProgressNotificationMessage 两处。当前实现中两者指向同一个对象引用,但如果后续代码单独修改其中一个,可能导致数据不一致。

建议考虑以下方案之一:

  1. 仅在 taskProgress 中保留 progressDetail,移除 message 级别的冗余字段
  2. 在 Javadoc 中明确说明 msg.progressDetailtaskProgress.progressDetail 的便捷访问器
🤖 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/longjob/LongJobProgressNotificationMessage.java`
around lines 100 - 102, The message stores progressDetail redundantly which
risks inconsistency; remove the redundant
LongJobProgressNotificationMessage.progressDetail field and stop assigning it
(replace the lines setting msg.progressDetail with relying on
msg.taskProgress.getProgressDetail()), update usages to read progress via
taskProgress.getProgressDetail(), and add a Javadoc on
LongJobProgressNotificationMessage (or TaskProgressInventory accessor)
explaining progressDetail should be accessed through taskProgress to make the
invariant clear.
header/src/main/java/org/zstack/header/core/progress/LongJobProgressDetailBuilder.java (1)

38-43: 日志中避免记录完整的 opaque 数据

trace 日志中直接拼接 vo.getOpaque() 可能导致:

  1. 日志过长(opaque 可能包含大量数据)
  2. 潜在的敏感信息泄露

建议截断或摘要显示:

♻️ 建议的修改
         } catch (Exception e) {
-            logger.trace("LongJobProgressDetailBuilder: opaque is not a JSON object, skipping: " + vo.getOpaque(), e);
+            logger.trace("LongJobProgressDetailBuilder: opaque is not a JSON object, skipping (length={})", 
+                vo.getOpaque() != null ? vo.getOpaque().length() : 0, e);
             return 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/core/progress/LongJobProgressDetailBuilder.java`
around lines 38 - 43, The trace log in LongJobProgressDetailBuilder currently
concatenates vo.getOpaque() which can be huge or contain sensitive data; change
the logger.trace call in the catch block that handles
JSONObjectUtil.toObject(...) to avoid logging the full opaque: log a truncated
snippet (e.g. first N chars) or a digest (hash) plus the opaque length and
context instead of vo.getOpaque(), and keep the original exception parameter;
update the message produced by logger.trace(...) accordingly so it references
vo.getUuid() or other non-sensitive identifier along with the truncated/hashed
opaque preview.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@header/src/main/java/org/zstack/header/core/progress/LongJobProgressDetailBuilder.java`:
- Around line 38-43: The trace log in LongJobProgressDetailBuilder currently
concatenates vo.getOpaque() which can be huge or contain sensitive data; change
the logger.trace call in the catch block that handles
JSONObjectUtil.toObject(...) to avoid logging the full opaque: log a truncated
snippet (e.g. first N chars) or a digest (hash) plus the opaque length and
context instead of vo.getOpaque(), and keep the original exception parameter;
update the message produced by logger.trace(...) accordingly so it references
vo.getUuid() or other non-sensitive identifier along with the truncated/hashed
opaque preview.

In
`@header/src/main/java/org/zstack/header/longjob/LongJobProgressNotificationMessage.java`:
- Around line 100-102: The message stores progressDetail redundantly which risks
inconsistency; remove the redundant
LongJobProgressNotificationMessage.progressDetail field and stop assigning it
(replace the lines setting msg.progressDetail with relying on
msg.taskProgress.getProgressDetail()), update usages to read progress via
taskProgress.getProgressDetail(), and add a Javadoc on
LongJobProgressNotificationMessage (or TaskProgressInventory accessor)
explaining progressDetail should be accessed through taskProgress to make the
invariant clear.

ℹ️ 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: 5509151d-a09d-4893-967a-bfd468be5045

📥 Commits

Reviewing files that changed from the base of the PR and between 3a6ad0f and 241090b.

⛔ Files ignored due to path filters (2)
  • sdk/src/main/java/org/zstack/sdk/LongJobProgressDetail.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/TaskProgressInventory.java is excluded by !sdk/**
📒 Files selected for processing (6)
  • core/src/main/java/org/zstack/core/progress/ProgressReportService.java
  • header/src/main/java/org/zstack/header/core/progress/APIGetTaskProgressReply.java
  • header/src/main/java/org/zstack/header/core/progress/LongJobProgressDetail.java
  • header/src/main/java/org/zstack/header/core/progress/LongJobProgressDetailBuilder.java
  • header/src/main/java/org/zstack/header/core/progress/TaskProgressInventory.java
  • header/src/main/java/org/zstack/header/longjob/LongJobProgressNotificationMessage.java

@MatheMatrix MatheMatrix force-pushed the sync/ye.zou/fix/ZSTAC-82318 branch from 241090b to c0bfab9 Compare March 13, 2026 03:40
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: 3

🤖 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/core/progress/LongJobProgressDetailBuilder.java`:
- Around line 198-210: LongJobProgressDetailBuilder currently only copies
unmapped keys from the progress map into detail.setExtra, which omits unknown
top-level fields in the inner object and non-"data" keys from the raw outer
object; update the builder to merge and preserve all unknown fields by
collecting leftover keys from progress, any unknown keys present in the inner
map, and any keys from the raw map except "data" into a single extra map before
calling detail.setExtra (do this for both the progress->extra block and the
similar block handling lines 213-221), ensuring existing mapped keys (percent,
downloaded_bytes, etc.) are excluded so we don't overwrite mapped fields.
- Around line 157-160: The current code in LongJobProgressDetailBuilder takes
the reported Number percent, rounds it and assigns it directly via
detail.setPercent, which can pass through out-of-range values; modify the logic
that handles the local variable percent (from progress.get("percent")) so after
computing int p = (int) Math.round(percent.doubleValue()) you clamp p to the
0..100 range (e.g., p = Math.max(0, Math.min(100, p))) before calling
detail.setPercent(p) to prevent negative or >100 values reaching the front end.

In
`@header/src/main/java/org/zstack/header/core/progress/TaskProgressInventory.java`:
- Around line 21-22: TaskProgressInventory's field comment for progressDetail
(type LongJobProgressDetail) is inaccurate: "unrecognized" currently does not
yield null but returns a detail with extra; update the Javadoc for the private
LongJobProgressDetail progressDetail to state that it is null only when opaque
is absent or when parsing of opaque fails, and that unrecognized opaque values
produce a detail containing extra information instead of null.

ℹ️ 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: 2d84d1b8-e63a-4910-8340-e6224e126920

📥 Commits

Reviewing files that changed from the base of the PR and between 241090b and c0bfab9.

⛔ Files ignored due to path filters (3)
  • sdk/src/main/java/SourceClassMap.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/LongJobProgressDetail.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/TaskProgressInventory.java is excluded by !sdk/**
📒 Files selected for processing (6)
  • core/src/main/java/org/zstack/core/progress/ProgressReportService.java
  • header/src/main/java/org/zstack/header/core/progress/APIGetTaskProgressReply.java
  • header/src/main/java/org/zstack/header/core/progress/LongJobProgressDetail.java
  • header/src/main/java/org/zstack/header/core/progress/LongJobProgressDetailBuilder.java
  • header/src/main/java/org/zstack/header/core/progress/TaskProgressInventory.java
  • header/src/main/java/org/zstack/header/longjob/LongJobProgressNotificationMessage.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • header/src/main/java/org/zstack/header/core/progress/APIGetTaskProgressReply.java
  • header/src/main/java/org/zstack/header/core/progress/LongJobProgressDetail.java

Comment on lines +21 to +22
/** Typed progress detail parsed from opaque. Null when opaque is absent or unrecognized. */
private LongJobProgressDetail progressDetail;
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

字段注释与实际行为不一致,建议修正文案

当前实现下,“unrecognized” 并不会返回 null,而是返回带 extra 的 detail;注释建议改为仅在 opaque 缺失或解析失败时为 null

建议修复
-    /** Typed progress detail parsed from opaque. Null when opaque is absent or unrecognized. */
+    /** Typed progress detail parsed from opaque. Null when opaque is absent or parsing fails. */
🤖 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/core/progress/TaskProgressInventory.java`
around lines 21 - 22, TaskProgressInventory's field comment for progressDetail
(type LongJobProgressDetail) is inaccurate: "unrecognized" currently does not
yield null but returns a detail with extra; update the Javadoc for the private
LongJobProgressDetail progressDetail to state that it is null only when opaque
is absent or when parsing of opaque fails, and that unrecognized opaque values
produce a detail containing extra information instead of null.

Resolves: ZSTAC-82318

Change-Id: I8931c4207547b836b522c9b5cea2db807a032d5c
@MatheMatrix MatheMatrix force-pushed the sync/ye.zou/fix/ZSTAC-82318 branch from c0bfab9 to cb554df Compare March 13, 2026 06: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.

2 participants