Skip to content

<refactor>[pciDevice]: Flyway migrations for GPU scope, BM2 phantom host, MdevDeviceVO subtable#3394

Open
MatheMatrix wants to merge 4 commits into5.5.12from
sync/zstackio/fix/ZSTAC-68709@@2
Open

<refactor>[pciDevice]: Flyway migrations for GPU scope, BM2 phantom host, MdevDeviceVO subtable#3394
MatheMatrix wants to merge 4 commits into5.5.12from
sync/zstackio/fix/ZSTAC-68709@@2

Conversation

@MatheMatrix
Copy link
Owner

Summary

  • V6.0.0.1: Add scope and chassisUuid columns to GpuDeviceVO for unified device scope tracking
  • V6.0.0.2: Migrate BareMetal2 GPU devices to unified GpuDeviceVO via phantom hosts (conditional, safe for non-BM2 deployments)
  • V6.0.0.3: Convert MdevDeviceVO from standalone to PciDeviceVO subtable with AccountResourceRefVO migration

Related MR

  • Premium: zstackio/premium!13043

Test plan

  • Verify V6.0.0.1 adds columns without data loss
  • Verify V6.0.0.2 skips cleanly on non-BM2 deployments
  • Verify V6.0.0.3 preserves all MdevDeviceVO data in new subtable structure
  • Verify AccountResourceRefVO references are correctly migrated

Resolves: ZSTAC-68709

🤖 Generated with Claude Code

sync from gitlab !9242

AlanJager and others added 2 commits February 26, 2026 15:40
…om host, MdevDeviceVO subtable

Resolves: ZSTAC-68709

Change-Id: Ib0c9bbcad669d8e0c311f9f3d2d1c83953a689b1
- M1: Wrap V6.0.0.2 BM2 GPU migration in conditional procedure that
      checks for BareMetal2ChassisGpuDeviceVO table existence (safe for
      non-BM2 deployments)
- M5: Add cleanup of old MdevDeviceVO AccountResourceRefVO entries in
      V6.0.0.3 to prevent duplicate resource references

Resolves: ZSTAC-68709

Change-Id: I6d8c934cdc580eef26e01e40122e4f989da84136

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

Walkthrough

新增三条数据库升级脚本:为 GpuDeviceVO 添加 scope 与 chassisUuid 并建索引;将 BareMetal2 的 PCI/GPU 数据迁移至 PciDeviceVO/GpuDeviceVO(通过 phantom host);将 MdevDeviceVO 迁移为 PciDeviceVO 的子表并迁移相关引用与数据。

Changes

Cohort / File(s) Summary
GPU 设备扩展
conf/db/upgrade/V6.0.0.1__gpu_add_scope.sql
添加存储过程以在不存在时新增列 scope(VARCHAR(32) NOT NULL DEFAULT 'VM')和 chassisUuid(VARCHAR(32) NULL),根据 PciDeviceVO.virtStatus 更新 GpuDeviceVO.scopeCONTAINER,并创建 idxGpuDeviceVOScope 索引,执行后删除过程。
BareMetal2 GPU 迁移
conf/db/upgrade/V6.0.0.2__bm2_gpu_migrate.sql
新增并执行存储过程 bm2_gpu_migrate():仅在 BareMetal2 表存在时运行。为有 GPU 的 BM2 chassis 创建 phantom HostEO,迁移 BareMetal2ChassisPciDeviceVO -> PciDeviceVOBareMetal2ChassisGpuDeviceVO -> GpuDeviceVO,并以 _migrated 标记已迁移行;使用 NOT EXISTS 防重入。
Mdev 设备子表化
conf/db/upgrade/V6.0.0.3__mdev_subtable.sql
新增存储过程 migrate_mdev_subtable:将独立表 MdevDeviceVO 的共享字段插入 PciDeviceVO(保留 uuid),重命名旧表为 MdevDeviceVO_old,创建 MdevDeviceVO 子表并迁移 mdev 特有字段,迁移并去重 AccountResourceRefVO 的引用,保留旧表供回滚验证;过程具幂等性。

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 我是小兔子,轻踏迁移桥,
为 GPU 添域名,为机箱留线索;
BareMetal2 的显卡,幻主机引路跑,
Mdev 变子表,旧表留作照应笑,
数据归位稳稳,草丛里我蹦跳。


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Title check ❌ Error PR标题超过72字符限制(95字符),违反了[scope]: 格式的字符长度要求。 缩短标题至72字符以内,例如:'[pciDevice]: Flyway GPU scope, BM2, MdevDeviceVO migrations'(62字符)。
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed PR描述清晰详细地说明了三个数据库迁移的内容、目的和测试计划,与变更集高度相关。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch sync/zstackio/fix/ZSTAC-68709@@2

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.

Actionable comments posted: 7

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

Inline comments:
In `@conf/db/upgrade/V6.0.0.1__gpu_add_scope.sql`:
- Around line 3-13: Wrap all table and column identifiers in backticks to follow
the naming convention and avoid reserved-word conflicts: update ALTER TABLE
statements for GpuDeviceVO to use `GpuDeviceVO`, `scope`, `chassisUuid`; update
the UPDATE ... JOIN block to use `GpuDeviceVO`, `PciDeviceVO`, `g`.`uuid`,
`p`.`uuid`, and `virtStatus` (e.g., `p`.`virtStatus`) and the WHERE string
comparison; and update the CREATE INDEX to use `idxGpuDeviceVOScope` and
`GpuDeviceVO`/`scope` in backticks. Keep string defaults and literals (e.g.,
'VM', 'HAMI_VIRTUALIZED') unchanged. Ensure every bare identifier in the ALTER,
UPDATE, JOIN, WHERE and CREATE INDEX statements is wrapped with backticks.
- Line 13: 在 V6.0.0.1 的迁移脚本中直接执行 CREATE INDEX idxGpuDeviceVOScope ON GpuDeviceVO
(scope) 缺少幂等性检查;请改为先检测索引是否已存在(例如查询 pg_class/pg_indexes 或
information_schema)再创建,从而避免重复运行失败;在该脚本里以 idxGpuDeviceVOScope 和 表名 GpuDeviceVO
为判断条件,若不存在则执行创建索引的语句,否则跳过创建。
- Around line 3-4: The ALTER TABLE statements are not idempotent; wrap them in a
conditional that checks information_schema.COLUMNS for table_name='GpuDeviceVO'
and column_name='scope' / 'chassisUuid' and only run ALTER TABLE GpuDeviceVO ADD
COLUMN ... when the column does not already exist (you can implement this as a
stored procedure or anonymous block that queries information_schema and performs
ALTER TABLE for 'scope' and 'chassisUuid' accordingly); ensure the new columns
use the same definitions (VARCHAR(32) DEFAULT 'VM' NOT NULL for scope,
VARCHAR(32) DEFAULT NULL for chassisUuid) and that each addition is guarded by
its own existence-check to make the migration safely re-runnable.

In `@conf/db/upgrade/V6.0.0.2__bm2_gpu_migrate.sql`:
- Around line 12-82: 给所有 SQL 标识符加反引号以避免保留字冲突:在脚本中把表名
HostEO、BareMetal2ChassisVO、BareMetal2ChassisPciDeviceVO、BareMetal2ChassisGpuDeviceVO、PciDeviceVO、GpuDeviceVO
以及所有列名(例如
uuid、name、description、zoneUuid、clusterUuid、managementIp、hypervisorType、state、status、createDate、lastOpDate、type、virtStatus、vendorId、deviceId、subvendorId、subdeviceId、pciDeviceAddress、iommuGroup、vendor、device、serialNumber、memory、power、isDriverLoaded、scope、chassisUuid、_migrated
等)都用反引号包裹;在所有 SELECT/INSERT/WHERE/JOIN/ALTER/UPDATE 子句内统一替换未加引号的标识符(例如在 INSERT
INTO HostEO、INSERT INTO PciDeviceVO、INSERT INTO GpuDeviceVO、ALTER TABLE
BareMetal2ChassisPciDeviceVO、以及子查询中引用的所有列)为带反引号的形式以保证兼容 MySQL 8.0/GreatSQL。

In `@conf/db/upgrade/V6.0.0.3__mdev_subtable.sql`:
- Around line 72-75: Step 6 currently deletes AccountResourceRefVO rows for
resourceType 'MdevDeviceVO' unconditionally; wrap the Step 5 INSERT and this
DELETE in a single transaction (or implement a stored procedure) so the DELETE
only commits if the INSERT into AccountResourceRefVO (from Step 5) succeeds;
specifically, enclose the Step 5 INSERT and the DELETE FROM AccountResourceRefVO
... WHERE resourceType = 'MdevDeviceVO' AND resourceUuid IN (SELECT uuid FROM
MdevDeviceVO) in BEGIN/COMMIT with ROLLBACK on error (or move delete into the
same stored procedure and check the INSERT result before performing the DELETE)
to ensure atomicity.
- Around line 4-76: The SQL uses unquoted identifiers which can conflict with
MySQL reserved words; update every table and column reference to use backticks
(e.g., `PciDeviceVO`, `MdevDeviceVO`, `AccountResourceRefVO`, and column names
like `type`, `state`, `status`, `name`, `description`, `uuid`,
`mdevDeviceAddress`, `mdevSpecUuid`, `mttyUuid`, etc.) across all statements
(INSERT ... SELECT, JOIN, WHERE, RENAME TABLE, CREATE TABLE, DELETE) so all
identifiers are wrapped in backticks and foreign key clauses and literals remain
unchanged.
- Around line 31-52: 脚本缺乏幂等性——在对 MdevDeviceVO 做 RENAME、CREATE、INSERT
操作时会在重复执行时失败;请将逻辑改为存储过程并在执行前通过 information_schema 检查表/约束是否存在(检查
MdevDeviceVO、MdevDeviceVO_old、PciDeviceVO、MdevDeviceSpecVO、MttyDeviceVO),仅在旧表存在且新表不存在时执行
RENAME TABLE MdevDeviceVO TO MdevDeviceVO_old,并仅在新表不存在时执行 CREATE TABLE
MdevDeviceVO,最后在插入数据时使用条件插入(例如基于 NOT EXISTS 或 INSERT IGNORE 的等效逻辑)以避免主键冲突;参考
V6.0.0.2 的存储过程实现模式来组织这些检查与 DDL/DML 操作。

ℹ️ 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 c5e4f49 and bd225b0.

📒 Files selected for processing (3)
  • conf/db/upgrade/V6.0.0.1__gpu_add_scope.sql
  • conf/db/upgrade/V6.0.0.2__bm2_gpu_migrate.sql
  • conf/db/upgrade/V6.0.0.3__mdev_subtable.sql

Comment on lines 31 to 52
-- Step 2: Rename old MdevDeviceVO table
RENAME TABLE MdevDeviceVO TO MdevDeviceVO_old;

-- Step 3: Create new MdevDeviceVO as subtable of PciDeviceVO
CREATE TABLE MdevDeviceVO (
uuid VARCHAR(32) NOT NULL UNIQUE,
mdevSpecUuid VARCHAR(32) DEFAULT NULL,
mttyUuid VARCHAR(32) DEFAULT NULL,
mdevDeviceAddress VARCHAR(128) DEFAULT NULL,
PRIMARY KEY (uuid),
CONSTRAINT fkMdevDeviceVOPciDeviceVO
FOREIGN KEY (uuid) REFERENCES PciDeviceVO(uuid) ON DELETE CASCADE,
CONSTRAINT fkMdevDeviceVOMdevSpecVO
FOREIGN KEY (mdevSpecUuid) REFERENCES MdevDeviceSpecVO(uuid) ON DELETE SET NULL,
CONSTRAINT fkMdevDeviceVOMttyDeviceVO
FOREIGN KEY (mttyUuid) REFERENCES MttyDeviceVO(uuid) ON DELETE CASCADE
) ENGINE=InnoDB;

-- Step 4: Migrate mdev-specific data to new subtable
INSERT INTO MdevDeviceVO (uuid, mdevSpecUuid, mttyUuid, mdevDeviceAddress)
SELECT uuid, mdevSpecUuid, mttyUuid, mdevDeviceAddress
FROM MdevDeviceVO_old;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

缺少幂等性保护,重复执行会失败。

这个迁移脚本存在严重的幂等性问题:

  1. Line 32: RENAME TABLE MdevDeviceVO TO MdevDeviceVO_old - 如果脚本重复执行,MdevDeviceVO 表已不存在会导致失败
  2. Line 35: CREATE TABLE MdevDeviceVO - 如果表已存在会失败
  3. Lines 50-52: INSERT 语句没有 NOT EXISTSINSERT IGNORE 保护,重复执行会导致主键冲突

建议将整个迁移过程包装在存储过程中,使用 information_schema 检查表是否存在,参考 V6.0.0.2 的实现方式。

🛠️ 建议使用存储过程包装
+DELIMITER $$
+DROP PROCEDURE IF EXISTS migrate_mdev_subtable$$
+CREATE PROCEDURE migrate_mdev_subtable()
+BEGIN
+    -- Only proceed if old table exists and new subtable doesn't
+    IF EXISTS (SELECT 1 FROM information_schema.TABLES 
+               WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'MdevDeviceVO')
+       AND NOT EXISTS (SELECT 1 FROM information_schema.TABLE_CONSTRAINTS 
+                       WHERE TABLE_SCHEMA = DATABASE() 
+                       AND TABLE_NAME = 'MdevDeviceVO' 
+                       AND CONSTRAINT_NAME = 'fkMdevDeviceVOPciDeviceVO') THEN
+
+        -- Step 1: Migrate shared fields to PciDeviceVO
+        INSERT INTO `PciDeviceVO` (...)
+        SELECT ... FROM `MdevDeviceVO` m
+        WHERE NOT EXISTS (...);
+
+        -- Step 2: Rename old table
+        RENAME TABLE `MdevDeviceVO` TO `MdevDeviceVO_old`;
+
+        -- Step 3: Create new subtable
+        CREATE TABLE `MdevDeviceVO` (...);
+
+        -- Step 4: Migrate mdev-specific data
+        INSERT INTO `MdevDeviceVO` (...)
+        SELECT ... FROM `MdevDeviceVO_old`;
+
+    END IF;
+
+    -- Steps 5 & 6 can run safely with INSERT IGNORE and conditional DELETE
+END$$
+DELIMITER ;
+
+CALL migrate_mdev_subtable();
+DROP PROCEDURE IF EXISTS migrate_mdev_subtable;

Based on learnings: ZStack 数据库升级脚本需要通过 information_schema 检查表和约束的存在性来确保脚本可以安全重复执行。

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

In `@conf/db/upgrade/V6.0.0.3__mdev_subtable.sql` around lines 31 - 52,
脚本缺乏幂等性——在对 MdevDeviceVO 做 RENAME、CREATE、INSERT 操作时会在重复执行时失败;请将逻辑改为存储过程并在执行前通过
information_schema 检查表/约束是否存在(检查
MdevDeviceVO、MdevDeviceVO_old、PciDeviceVO、MdevDeviceSpecVO、MttyDeviceVO),仅在旧表存在且新表不存在时执行
RENAME TABLE MdevDeviceVO TO MdevDeviceVO_old,并仅在新表不存在时执行 CREATE TABLE
MdevDeviceVO,最后在插入数据时使用条件插入(例如基于 NOT EXISTS 或 INSERT IGNORE 的等效逻辑)以避免主键冲突;参考
V6.0.0.2 的存储过程实现模式来组织这些检查与 DDL/DML 操作。

- All 3 SQL files: wrap table/column identifiers in backticks for
  MySQL 8.0/GreatSQL reserved word safety
- V6.0.0.1: wrap in stored procedure with information_schema checks
  for column and index existence (idempotent)
- V6.0.0.3: wrap DDL (RENAME/CREATE TABLE) in stored procedure with
  constraint existence check; use INSERT IGNORE for Step 4 (idempotent)

Resolves: ZSTAC-68709

Change-Id: Ic7114c1ac5aeffd571ebb1e4f7b736adcc1c9c03

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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 `@conf/db/upgrade/V6.0.0.3__mdev_subtable.sql`:
- Around line 68-91: Steps 5-6 perform INSERT ... SELECT and DELETE against
AccountResourceRefVO with subqueries referencing the MdevDeviceVO table but lack
a guard that the MdevDeviceVO table exists; wrap these two statements in the
same information_schema existence check used elsewhere so they only run when
MdevDeviceVO exists (e.g., check information_schema.tables for
table_name='MdevDeviceVO' and current DATABASE()), and apply that guard around
the INSERT IGNORE ... SELECT (the statement selecting from AccountResourceRefVO
ar where ar.resourceType='MdevDeviceVO') and the subsequent DELETE FROM
`AccountResourceRefVO` WHERE `resourceType`='MdevDeviceVO' AND `resourceUuid` IN
(SELECT `uuid` FROM `MdevDeviceVO`) so the migration can be safely re-run in
non-mdev or partial-failure scenarios.

ℹ️ 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 bd225b0 and a9a0ab8.

📒 Files selected for processing (3)
  • conf/db/upgrade/V6.0.0.1__gpu_add_scope.sql
  • conf/db/upgrade/V6.0.0.2__bm2_gpu_migrate.sql
  • conf/db/upgrade/V6.0.0.3__mdev_subtable.sql

…check

Wrap AccountResourceRefVO migration (Steps 5-6) in IF EXISTS check for
MdevDeviceVO table, preventing failure in non-mdev deployments.

Resolves: ZSTAC-68709

Change-Id: I7affad970216837e5a3a8656d28bcaf446616476

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

♻️ Duplicate comments (3)
conf/db/upgrade/V6.0.0.3__mdev_subtable.sql (3)

8-13: ⚠️ Potential issue | 🟡 Minor

information_schema 查询里的标识符仍有未加反引号。

Line 8-13、69-70 中 information_schema.TABLES / information_schema.TABLE_CONSTRAINTSTABLE_SCHEMATABLE_NAMECONSTRAINT_NAME 仍为裸标识符。建议按仓库 SQL 规范统一加反引号。

🛠️ 建议修复
-    IF EXISTS (SELECT 1 FROM information_schema.TABLES
-               WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'MdevDeviceVO')
-       AND NOT EXISTS (SELECT 1 FROM information_schema.TABLE_CONSTRAINTS
-                       WHERE TABLE_SCHEMA = DATABASE()
-                       AND TABLE_NAME = 'MdevDeviceVO'
-                       AND CONSTRAINT_NAME = 'fkMdevDeviceVOPciDeviceVO') THEN
+    IF EXISTS (SELECT 1 FROM `information_schema`.`TABLES`
+               WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'MdevDeviceVO')
+       AND NOT EXISTS (SELECT 1 FROM `information_schema`.`TABLE_CONSTRAINTS`
+                       WHERE `TABLE_SCHEMA` = DATABASE()
+                       AND `TABLE_NAME` = 'MdevDeviceVO'
+                       AND `CONSTRAINT_NAME` = 'fkMdevDeviceVOPciDeviceVO') THEN
@@
-    IF EXISTS (SELECT 1 FROM information_schema.TABLES
-               WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'MdevDeviceVO') THEN
+    IF EXISTS (SELECT 1 FROM `information_schema`.`TABLES`
+               WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'MdevDeviceVO') THEN

As per coding guidelines: 所有表名和列名必须使用反引号包裹(例如:WHERE system = 1),以避免 MySQL 8.0 / GreatSQL 保留关键字冲突导致的语法错误。

Also applies to: 69-70

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

In `@conf/db/upgrade/V6.0.0.3__mdev_subtable.sql` around lines 8 - 13, The SQL
uses unquoted identifiers in the information_schema queries; update the IF
condition and other occurrences (e.g., references to information_schema.TABLES,
information_schema.TABLE_CONSTRAINTS, TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_NAME,
and values like MdevDeviceVO and fkMdevDeviceVOPciDeviceVO) to wrap all
schema/table/column/constraint names and literal identifiers in backticks per
repo SQL style (e.g., `information_schema`.`TABLES`, `TABLE_SCHEMA`,
`TABLE_NAME`, `CONSTRAINT_NAME`, `MdevDeviceVO`, `fkMdevDeviceVOPciDeviceVO`)
and apply the same change to the other occurrences noted at lines 69-70.

8-13: ⚠️ Potential issue | 🟠 Major

补齐断点恢复分支,否则重跑可能永久跳过迁移。

若一次执行在 Line 44 RENAME 成功后、Line 47 CREATE TABLE 前失败,库里会只剩 MdevDeviceVO_old。当前入口条件仅检查 MdevDeviceVO,重跑会跳过 Step 1-4,迁移无法自愈。建议增加 MdevDeviceVO_old 存在且 MdevDeviceVO 缺失时的恢复分支(继续建新表并从 old 回填)。

🛠️ 建议修复(关键条件示例)
-    IF EXISTS (SELECT 1 FROM information_schema.TABLES
-               WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'MdevDeviceVO')
-       AND NOT EXISTS (SELECT 1 FROM information_schema.TABLE_CONSTRAINTS
-                       WHERE TABLE_SCHEMA = DATABASE()
-                       AND TABLE_NAME = 'MdevDeviceVO'
-                       AND CONSTRAINT_NAME = 'fkMdevDeviceVOPciDeviceVO') THEN
+    IF (
+        EXISTS (SELECT 1 FROM `information_schema`.`TABLES`
+                WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'MdevDeviceVO')
+        AND NOT EXISTS (SELECT 1 FROM `information_schema`.`TABLE_CONSTRAINTS`
+                        WHERE `TABLE_SCHEMA` = DATABASE()
+                        AND `TABLE_NAME` = 'MdevDeviceVO'
+                        AND `CONSTRAINT_NAME` = 'fkMdevDeviceVOPciDeviceVO')
+    )
+    OR (
+        NOT EXISTS (SELECT 1 FROM `information_schema`.`TABLES`
+                    WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'MdevDeviceVO')
+        AND EXISTS (SELECT 1 FROM `information_schema`.`TABLES`
+                    WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'MdevDeviceVO_old')
+    ) THEN
#!/bin/bash
# 核验是否存在 MdevDeviceVO_old 的恢复路径
rg -nP --type=sql -C3 "TABLE_NAME\s*=\s*'MdevDeviceVO_old'|RENAME TABLE\s+`MdevDeviceVO`\s+TO\s+`MdevDeviceVO_old`|IF EXISTS\s*\(SELECT 1 FROM information_schema"

Based on learnings: 在 ZStack 数据库升级脚本中,直接使用 RENAME TABLE 不能保证幂等性。应该通过 information_schema.tables 检查表的存在性,只在源表存在且目标表不存在时才执行重命名操作,以确保升级脚本可以安全地重复执行。

Also applies to: 43-64

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

In `@conf/db/upgrade/V6.0.0.3__mdev_subtable.sql` around lines 8 - 13, The
migration currently only checks for MdevDeviceVO and a missing constraint
(fkMdevDeviceVOPciDeviceVO), so if the script failed after the RENAME that
produced MdevDeviceVO_old, re-running will skip steps and never recover; update
the conditional logic around the RENAME/CREATE sequence to be idempotent: add an
explicit recovery branch that detects when MdevDeviceVO_old exists but
MdevDeviceVO does not and then proceeds to CREATE the new MdevDeviceVO, reapply
constraints (fkMdevDeviceVOPciDeviceVO) and copy data back from
MdevDeviceVO_old, and change the RENAME logic to only execute RENAME TABLE
`MdevDeviceVO` TO `MdevDeviceVO_old` when MdevDeviceVO exists and
MdevDeviceVO_old does not so the migration can be safely re-run.

53-55: ⚠️ Potential issue | 🟠 Major

修复幂等性问题:处理 RENAME 与 CREATE 之间的故障恢复。

第 8-10 行的条件仅检查表是否存在(EXISTS MdevDeviceVO),无法处理 RENAME 成功但 CREATE 失败的场景。此时 MdevDeviceVO 已不存在,条件为假,后续迁移步骤被跳过,导致脚本无法恢复。

建议修改为检查 MdevDeviceVO_old 的存在,确保无论处于哪个中间状态都能继续执行:

     IF EXISTS (SELECT 1 FROM information_schema.TABLES
                WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'MdevDeviceVO')
        OR EXISTS (SELECT 1 FROM information_schema.TABLES
                   WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'MdevDeviceVO_old')
        AND NOT EXISTS (SELECT 1 FROM information_schema.TABLE_CONSTRAINTS

同时,第 8-10、69-70 行的 information_schema 标识符建议加上反引号(information_schema.`TABLES` 等)以符合 SQL 编码规范。

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

In `@conf/db/upgrade/V6.0.0.3__mdev_subtable.sql` around lines 53 - 55, The
migration's idempotency check should be expanded to handle the case where RENAME
succeeded but CREATE failed by checking for the existence of MdevDeviceVO_old as
well as MdevDeviceVO; update the conditional that currently only tests EXISTS
MdevDeviceVO to also test for EXISTS MdevDeviceVO_old (so the script will
proceed when the old table remains) and ensure the later renaming/creation steps
run correctly, and also wrap the information_schema identifiers in backticks
(e.g., `information_schema`.`TABLES`) in both the existence checks and any
queries referencing `information_schema` to follow SQL identifier quoting
conventions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@conf/db/upgrade/V6.0.0.3__mdev_subtable.sql`:
- Around line 8-13: The SQL uses unquoted identifiers in the information_schema
queries; update the IF condition and other occurrences (e.g., references to
information_schema.TABLES, information_schema.TABLE_CONSTRAINTS, TABLE_SCHEMA,
TABLE_NAME, CONSTRAINT_NAME, and values like MdevDeviceVO and
fkMdevDeviceVOPciDeviceVO) to wrap all schema/table/column/constraint names and
literal identifiers in backticks per repo SQL style (e.g.,
`information_schema`.`TABLES`, `TABLE_SCHEMA`, `TABLE_NAME`, `CONSTRAINT_NAME`,
`MdevDeviceVO`, `fkMdevDeviceVOPciDeviceVO`) and apply the same change to the
other occurrences noted at lines 69-70.
- Around line 8-13: The migration currently only checks for MdevDeviceVO and a
missing constraint (fkMdevDeviceVOPciDeviceVO), so if the script failed after
the RENAME that produced MdevDeviceVO_old, re-running will skip steps and never
recover; update the conditional logic around the RENAME/CREATE sequence to be
idempotent: add an explicit recovery branch that detects when MdevDeviceVO_old
exists but MdevDeviceVO does not and then proceeds to CREATE the new
MdevDeviceVO, reapply constraints (fkMdevDeviceVOPciDeviceVO) and copy data back
from MdevDeviceVO_old, and change the RENAME logic to only execute RENAME TABLE
`MdevDeviceVO` TO `MdevDeviceVO_old` when MdevDeviceVO exists and
MdevDeviceVO_old does not so the migration can be safely re-run.
- Around line 53-55: The migration's idempotency check should be expanded to
handle the case where RENAME succeeded but CREATE failed by checking for the
existence of MdevDeviceVO_old as well as MdevDeviceVO; update the conditional
that currently only tests EXISTS MdevDeviceVO to also test for EXISTS
MdevDeviceVO_old (so the script will proceed when the old table remains) and
ensure the later renaming/creation steps run correctly, and also wrap the
information_schema identifiers in backticks (e.g.,
`information_schema`.`TABLES`) in both the existence checks and any queries
referencing `information_schema` to follow SQL identifier quoting conventions.

ℹ️ 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 a9a0ab8 and e657076.

📒 Files selected for processing (1)
  • conf/db/upgrade/V6.0.0.3__mdev_subtable.sql

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