Skip to content

<feature>[core]: support resnotify webhook infrastructure#3477

Closed
ZStack-Robot wants to merge 2 commits into5.5.12from
sync/yaohua.wu/feature/ZSTAC-80472@@2
Closed

<feature>[core]: support resnotify webhook infrastructure#3477
ZStack-Robot wants to merge 2 commits into5.5.12from
sync/yaohua.wu/feature/ZSTAC-80472@@2

Conversation

@ZStack-Robot
Copy link
Collaborator

ZSTAC-80472: Resource Notification Webhook - Core Infrastructure

Changes

Module Change
core/db DatabaseFacadeImpl: CopyOnWriteArrayList for thread-safe lifecycle callbacks, multi-listener support, uninstallEntityLifeCycleCallback method
conf/db V5.5.12 schema: ResNotifySubscriptionVO + ResNotifyWebhookRefVO tables with FK constraints and indexes

Notes

  • VOs/APIs/services are in premium repo (separate MR)
  • Core only provides framework-level support (thread-safe callbacks) and DB schema

Related: ZSTAC-80472

sync from gitlab !9337

Add CopyOnWriteArrayList for thread-safe entity
lifecycle callbacks in DatabaseFacadeImpl. Add
uninstallEntityLifeCycleCallback method. Add
ResNotify DB schema with FK and indexes.

Change-Id: Ia3667e35d74c06796946bb805aba4db5c87a3052
@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 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: 101490ac-9fa7-483d-86be-a1c1bd2c06fc

📥 Commits

Reviewing files that changed from the base of the PR and between 67acc0a and f5459df.

⛔ Files ignored due to path filters (13)
  • sdk/src/main/java/SourceClassMap.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/DeleteResNotifySubscriptionAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/DeleteResNotifySubscriptionResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/QueryResNotifySubscriptionAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/QueryResNotifySubscriptionResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/ResNotifySubscriptionInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/ResNotifySubscriptionState.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/ResNotifyType.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/ResNotifyWebhookRefInventory.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/SubscribeResNotifyAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/SubscribeResNotifyResult.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/UpdateResNotifySubscriptionAction.java is excluded by !sdk/**
  • sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/UpdateResNotifySubscriptionResult.java is excluded by !sdk/**
📒 Files selected for processing (2)
  • core/src/main/java/org/zstack/core/db/DatabaseFacade.java
  • testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy

Walkthrough

新增资源通知相关数据库表(ResNotifySubscriptionVO、ResNotifyWebhookRefVO),并将实体生命周期回调从单一回调改为每事件可挂载多个回调,新增卸载回调的公共接口。

Changes

Cohort / File(s) Summary
数据库 Schema 迁移
conf/db/upgrade/V5.5.12__schema.sql
新增表 ResNotifySubscriptionVOResNotifyWebhookRefVO,包含主键、索引、外键约束(ON DELETE CASCADE)及字符集/引擎定义,用于资源通知与 webhook 引用。
实体生命周期回调实现
core/src/main/java/org/zstack/core/db/DatabaseFacadeImpl.java
将事件回调存储由单一 EntityLifeCycleCallback 改为 List<EntityLifeCycleCallback>(使用 CopyOnWriteArrayList),修改触发逻辑以遍历并执行多个回调,新增卸载回调的实现。
公共接口扩展
core/src/main/java/org/zstack/core/db/DatabaseFacade.java
在接口中新增 void uninstallEntityLifeCycleCallback(Class entityClass, EntityEvent evt, EntityLifeCycleCallback cb),对外暴露回调卸载能力。

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 新表冒出地里忙,
订阅与钩子一并藏,
回调排队列成行,
卸载来时不慌张,
我在代码田间蹦蹦忙 🌱

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 拉取请求标题遵循指定格式 [scope]: ,长度为57个字符,不超过72个字符限制,标题清晰地总结了主要变更内容。
Description check ✅ Passed 拉取请求描述与变更集相关,包含具体的模块变更、相关问题跟踪ID和同步来源信息,充分解释了本次提交的目的。

✏️ 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/feature/ZSTAC-80472@@2
📝 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)
core/src/main/java/org/zstack/core/db/DatabaseFacadeImpl.java (2)

909-921: uninstallinstall 在 EntityInfo 为空时的行为不一致

installEntityLifeCycleCallback(第 900 行)在找不到 EntityInfo 时会触发断言失败,而 uninstallEntityLifeCycleCallback 则静默跳过。这种不一致可能会隐藏调用方传入错误类的 bug。

♻️ 建议添加警告日志保持行为一致
 `@Override`
 public void uninstallEntityLifeCycleCallback(Class clz, EntityEvent evt, EntityLifeCycleCallback cb) {
     if (clz != null) {
         EntityInfo info = entityInfoMap.get(clz);
         if (info != null) {
             info.uninstallLifeCycleCallback(evt, cb);
+        } else {
+            logger.warn(String.format("cannot find EntityInfo for the class[%s] when uninstalling lifecycle callback", clz));
         }
     } else {
         for (EntityInfo info : entityInfoMap.values()) {
             info.uninstallLifeCycleCallback(evt, cb);
         }
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/src/main/java/org/zstack/core/db/DatabaseFacadeImpl.java` around lines
909 - 921, uninstallEntityLifeCycleCallback currently silently skips when
entityInfoMap.get(clz) returns null, but installEntityLifeCycleCallback asserts
on missing EntityInfo; make behavior consistent by checking EntityInfo for null
and either assert or log a warning before returning; update the method
uninstallEntityLifeCycleCallback to mirror installEntityLifeCycleCallback's
handling (use EntityInfo info = entityInfoMap.get(clz); if (info == null) {
throw new AssertionError(...) } or at minimum processLogger.warn(...) with class
name), then call info.uninstallLifeCycleCallback(evt, cb) when present
(references: uninstallEntityLifeCycleCallback, installEntityLifeCycleCallback,
EntityInfo, entityInfoMap, uninstallLifeCycleCallback).

457-469: contains + add 存在轻微的 TOCTOU 竞态条件

在并发场景下,两个线程可能同时通过 contains 检查后都执行 add,导致重复的回调被注册。

由于使用了 CopyOnWriteArrayList,最坏情况仅为回调重复执行,影响较小。如需完全避免,可考虑使用 addIfAbsent 方法(CopyOnWriteArrayList 原生支持):

♻️ 建议使用原子操作
 void installLifeCycleCallback(EntityEvent evt, EntityLifeCycleCallback l) {
     List<EntityLifeCycleCallback> cbs = listeners.computeIfAbsent(evt, k -> new CopyOnWriteArrayList<>());
-    if (!cbs.contains(l)) {
-        cbs.add(l);
-    }
+    ((CopyOnWriteArrayList<EntityLifeCycleCallback>) cbs).addIfAbsent(l);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/src/main/java/org/zstack/core/db/DatabaseFacadeImpl.java` around lines
457 - 469, The installLifeCycleCallback method currently does a contains check
then add on the CopyOnWriteArrayList stored in listeners, which can race and
register duplicates; change the logic in installLifeCycleCallback to call the
CopyOnWriteArrayList atomic method addIfAbsent (rather than contains + add) so
duplicate callbacks for an EntityEvent/EntityLifeCycleCallback pair are avoided;
keep uninstallLifeCycleCallback as-is but ensure it still operates on
listeners.get(evt) and removes the callback if present.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@core/src/main/java/org/zstack/core/db/DatabaseFacadeImpl.java`:
- Around line 909-921: uninstallEntityLifeCycleCallback currently silently skips
when entityInfoMap.get(clz) returns null, but installEntityLifeCycleCallback
asserts on missing EntityInfo; make behavior consistent by checking EntityInfo
for null and either assert or log a warning before returning; update the method
uninstallEntityLifeCycleCallback to mirror installEntityLifeCycleCallback's
handling (use EntityInfo info = entityInfoMap.get(clz); if (info == null) {
throw new AssertionError(...) } or at minimum processLogger.warn(...) with class
name), then call info.uninstallLifeCycleCallback(evt, cb) when present
(references: uninstallEntityLifeCycleCallback, installEntityLifeCycleCallback,
EntityInfo, entityInfoMap, uninstallLifeCycleCallback).
- Around line 457-469: The installLifeCycleCallback method currently does a
contains check then add on the CopyOnWriteArrayList stored in listeners, which
can race and register duplicates; change the logic in installLifeCycleCallback
to call the CopyOnWriteArrayList atomic method addIfAbsent (rather than contains
+ add) so duplicate callbacks for an EntityEvent/EntityLifeCycleCallback pair
are avoided; keep uninstallLifeCycleCallback as-is but ensure it still operates
on listeners.get(evt) and removes the callback if present.

ℹ️ 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: 5c235990-161f-4ec7-952f-9a81acff5a22

📥 Commits

Reviewing files that changed from the base of the PR and between 681a301 and 67acc0a.

📒 Files selected for processing (2)
  • conf/db/upgrade/V5.5.12__schema.sql
  • core/src/main/java/org/zstack/core/db/DatabaseFacadeImpl.java

Add uninstallEntityLifeCycleCallback to DatabaseFacade
interface. Generate SDK action classes and ApiHelper
methods for resnotify webhook subscription CRUD APIs.

Change-Id: I9191952ca0a0959644bc2d90d539a818cf13d30a
@MatheMatrix MatheMatrix deleted the sync/yaohua.wu/feature/ZSTAC-80472@@2 branch March 13, 2026 02:56
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