Skip to content

Feat/cqrs optimization#342

Merged
GeWuYou merged 8 commits into
mainfrom
feat/cqrs-optimization
May 8, 2026
Merged

Feat/cqrs optimization#342
GeWuYou merged 8 commits into
mainfrom
feat/cqrs-optimization

Conversation

@GeWuYou
Copy link
Copy Markdown
Owner

@GeWuYou GeWuYou commented May 8, 2026

Summary by CodeRabbit

发布说明

  • 新功能

    • 新增通知发布策略配置:支持顺序发布和并行发布两种内置策略
    • 新增通知发布器注册扩展方法,允许在组合根显式选择和配置发布策略
  • 文档

    • 更新通知分发行为文档,包含策略选择矩阵、失败语义说明及配置示例
  • 测试

    • 新增通知发布器注册和发布行为的完整测试覆盖
    • 扩展基准测试套件,对比多个框架的性能特性

GeWuYou added 7 commits May 8, 2026 16:27
- 新增 NotificationBenchmarks 的 Mediator concrete runtime 对照与对应通知合同实现

- 更新 benchmark README,明确 notification publish 已扩成三方对照

- 更新 cqrs-rewrite active tracking 与 trace,记录 RP-111 的基线、验证结果与下一恢复建议
- 新增 NotificationFanOutBenchmarks,量化固定四处理器 notification publish 对照

- 更新 benchmark README,补充 notification fan-out 场景说明

- 更新 cqrs-rewrite active tracking 与 trace,记录 RP-112 的基线、验证结果与下一恢复建议
- 新增 TaskWhenAllNotificationPublisher 内置并行通知发布器并保留默认顺序语义

- 补充通知发布策略回归测试与采用边界文档

- 更新 cqrs-rewrite 跟踪与执行追踪恢复点
- 新增默认顺序发布器与 TaskWhenAllNotificationPublisher 的 fixed 4 handler fan-out benchmark 对照

- 更新 benchmark README 与 cqrs-rewrite 恢复文档,记录 RP-114 的性能结论与下一步
- 新增 notification publisher 组合根注册扩展,提供 TaskWhenAll 与自定义策略入口

- 补充通知发布策略配置回归测试,并更新 CQRS 文档与恢复点记录
- 新增公开 SequentialNotificationPublisher,并让默认 runtime 回退复用该策略

- 增加顺序 notification publisher 组合根注册入口,并更新测试文档与恢复点
- 更新 notification publisher 的策略选择矩阵,明确顺序、并行与自定义 publisher 的适用边界

- 补充 CQRS 重写 tracking 与 trace,记录已撤回的无收益 request 热路径实验和当前恢复点
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 8, 2026

Review Change Stack

Warning

Rate limit exceeded

@GeWuYou has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 28 minutes and 12 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3dbcebb5-3d06-4539-ab11-8e6a23ced9eb

📥 Commits

Reviewing files that changed from the base of the PR and between 4121e12 and 59ceb06.

📒 Files selected for processing (5)
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs
  • GFramework.Cqrs/README.md
  • ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
  • ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
  • docs/zh-CN/core/cqrs.md
📝 Walkthrough

演练

此 PR 为 CQRS 通知发布框架引入了并行策略选项、公开的注册 API 和全面的性能基准。TaskWhenAllNotificationPublisher 实现了 Task.WhenAll 并行分发,聚合多个处理器的异常;SequentialNotificationPublisher 升级为公开 API;新增注册扩展方法支持策略显式配置;测试覆盖了异常聚合和处理器调用行为;两个基准套件(单通知和 4 处理器扇出)对比了框架和第三方实现;文档记录了采用边界和配置模式。

变更

通知发布者策略框架

层 / 文件 摘要
发布者契约与可见性
GFramework.Cqrs/Notification/SequentialNotificationPublisher.cs
SequentialNotificationPublisherinternal 升级为 public,命名空间从 Internal 移至 Notification;实现 INotificationPublisher 顺序分发语义。
Task.WhenAll 发布者实现
GFramework.Cqrs/Notification/TaskWhenAllNotificationPublisher.cs
新增 TaskWhenAllNotificationPublisher 实现并行分发:零个处理器返回已完成任务,单个直接调用,多个调用使用 Task.WhenAll 聚合异常。
注册扩展 API
GFramework.Cqrs/Extensions/NotificationPublisherRegistrationExtensions.cs
提供四个扩展方法在 IIocContainer 上注册发布者:实例绑定、泛型类型注册、以及两个内置策略快捷方法;私有守卫防止单容器多发布者注册。
行为与注册测试
GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs, GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
验证 TaskWhenAllNotificationPublisher 在前置处理器失败时仍调用后续处理器并聚合异常;验证 SequentialNotificationPublisher 在首次失败时停止;测试注册扩展防止重复配置;测试上下文注入。
单通知基准
GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs
扩展 1-vs-1 对比(GFramework vs MediatR)为 1-vs-2,添加源代码生成 Mediator 混凝土中介者路径;分离 MediatR 和 Mediator 服务提供程序;通知 DTO 和处理器支持三个框架的契约。
扇出基准套件
GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs
对比四处理器扇出分发五个场景:直接基准、GFramework 顺序运行时、GFramework Task.WhenAll 运行时、MediatR 发布、Mediator 源代码生成;包含配置、设置、清理和四个处理器实现。
基准文档更新
GFramework.Cqrs.Benchmarks/README.md
补充 NotificationBenchmarksNotificationFanOutBenchmarks 的场景说明,明确与 MediatR 的单处理器和固定 4 处理器扇出对比维度。
CQRS README
GFramework.Cqrs/README.md
扩展通知默认行为文档:说明 INotificationPublisher 注册时的策略复用规则;记录内置发布者选项和失败语义表格;添加组合根显式配置代码示例;更新子系统映射。
中文核心文档
docs/zh-CN/core/cqrs.md
在通知分发顺序语义章节扩展默认复用与回退规则;新增策略选择矩阵;添加顺序、并行 TaskWhenAll 和自定义发布者实例的代码示例。
迁移跟踪与恢复点
ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md, ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
将恢复点更新为 RP-117;新增风险说明:Mediator 混凝土运行时对照范围限制;调整下一步建议;记录 RP-111 到 RP-117 的六阶段追踪(从 Mediator 基准到策略收口)。

预估代码审查工作量

🎯 3 (中等) | ⏱️ ~20 分钟

可能关联的 PR

  • GeWuYou/GFramework#341:同样修改 CQRS 通知发布基础设施、添加 TaskWhenAll vs Sequential 发布者对比、扩展基准套件。
  • GeWuYou/GFramework#340:共享 Mediator 源代码生成基准支持、通知发布者策略实现和 DI 宿主工厂。
  • GeWuYou/GFramework#308:同样更新 CQRS 通知文档(docs/zh-CN/core/cqrs.md)。
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive 标题"Feat/cqrs optimization"过于宽泛,未能准确反映本次PR的核心改动:实现notification publisher策略注册扩展与并行发布能力。 建议更新标题以明确表达核心变更,例如:"Add notification publisher strategy registration and TaskWhenAllNotificationPublisher implementation"或"Support pluggable notification publisher strategies"。
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cqrs-optimization

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 8, 2026

Summary

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Other ❓ Flaky 🍂 Duration ⏱️
2330    ↑5 2330    ↑5 0 0 0 0 37.6s    ↓625ms

Test Results

passed 2330 passed ↑5

Details

tests 2330 tests ↑5
clock 37.6s ↓625ms
tool nunit
build CI - Build & Test arrow-right build-and-test link #1094
pull-request Feat/cqrs optimization link #342

Insights

Average Tests per Run Total Flaky Tests Total Failed Slowest Test (p95)
2236 0 1 4.7s

Fail Rate

Fail Rate 0.00%
Test 📝 Results 📊 Passed ✅ Failed ❌ Fail Rate (%) 📈
CreateStream_Should_Throw_When_Stream_Pipeline_Behavior_Context_Does_Not_Implement_IArchitectureContext 8 7 1 12.50    ↓1.79

build-and-test: Run #1094

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
2330 2330 0 0 0 0 0 37.6s

🎉 All tests passed!

Slowest Tests

Test 📝 Results 📊 Duration (avg) ⏱️ Duration (p95) ⏱️
CreateStream_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently 26 4.1s 4.7s
SendRequestAsync_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently 41 1.5s 4.7s
Does_Not_Report_When_FieldInjectedModel_Is_Registered 41 2.2s 2.3s
Generates_Scene_Behavior_Boilerplate 41 1.9s 2.0s
CleanupDuringAcquire_Should_NotCauseRaceCondition 41 1.1s 1.1s
Append_ShouldNotBlock 41 1.0s 1.0s
Context_Caching_Should_Improve_Performance 41 782ms 794ms
PendingCount_ShouldReflectQueuedEntries 41 501ms 501ms
Cleanup_Should_NotRemoveActiveLocks 41 404ms 405ms
Cleanup_Should_RemoveUnusedLocks 41 401ms 402ms

± Comparison with run #1093 at 005cdbc | 🍂 No flaky tests detected across all runs. | ⏱️ Measured over 41 runs.

Github Test Reporter by CTRF 💚

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 8, 2026

⚠️MegaLinter analysis: Success with warnings

Descriptor Linter Files Fixed Errors Warnings Elapsed time
⚠️ CSHARP dotnet-format yes 1 no 5.16s
✅ REPOSITORY gitleaks yes no no 8.0s
✅ REPOSITORY trufflehog yes no no 6.3s

Detailed Issues

⚠️ CSHARP / dotnet-format - 1 error
Welcome to .NET 9.0!
---------------------
SDK Version: 9.0.114

----------------
Installed an ASP.NET Core HTTPS development certificate.
To trust the certificate, run 'dotnet dev-certs https --trust'
Learn about HTTPS: https://aka.ms/dotnet-https

----------------
Write your first app: https://aka.ms/dotnet-hello-world
Find out what's new: https://aka.ms/dotnet-whats-new
Explore documentation: https://aka.ms/dotnet-docs
Report issues and find source on GitHub: https://github.com/dotnet/core
Use 'dotnet --help' to see available commands or visit: https://aka.ms/dotnet-cli
--------------------------------------------------------------------------------------
Unhandled exception: System.Exception: Restore operation failed.
   at Microsoft.CodeAnalysis.Tools.CodeFormatter.OpenMSBuildWorkspaceAsync(String solutionOrProjectPath, WorkspaceType workspaceType, Boolean noRestore, Boolean requiresSemantics, String binaryLogPath, Boolean logWorkspaceWarnings, ILogger logger, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.Tools.CodeFormatter.FormatWorkspaceAsync(FormatOptions formatOptions, ILogger logger, CancellationToken cancellationToken, String binaryLogPath)
   at Microsoft.CodeAnalysis.Tools.FormatCommandCommon.FormatAsync(FormatOptions formatOptions, ILogger`1 logger, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.Tools.Commands.RootFormatCommand.FormatCommandDefaultHandler.InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken)
   at System.CommandLine.Invocation.InvocationPipeline.InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken)

See detailed reports in MegaLinter artifacts
Set VALIDATE_ALL_CODEBASE: true in mega-linter.yml to validate all sources, not only the diff

MegaLinter is graciously provided by OX Security
Show us your support by starring ⭐ the repository

Copy link
Copy Markdown
Contributor

@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: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md`:
- Around line 156-159: The markdown currently shows an older fan-out measurement
in the line containing "固定 `4 handler` notification fan-out 对照当前约为 baseline
`8.302 ns / 0 B`、`Mediator` `4.314 ns / 0 B`、`MediatR` `230.304 ns / 1256
B`、`GFramework.Cqrs` `434.413 ns / 408 B` (referenced next to the `dotnet run
... --filter "*NotificationFanOutBenchmarks*"` command); update these numbers to
the latest fan-out benchmark results or, if you intend to keep the historical
values, append an explicit note like “历史基线(RP-112)” next to that inline code
snippet so readers aren’t confused with RP-117.
- Around line 10-13: 在文档中的“当前 PR 锚点:`PR `#341``”条目已过期,请将其更新为最新的 PR 编号(`PR
`#342``),确保文档中所有出现 `PR `#341`` 的位置(例如条目“当前 PR 锚点:`PR `#341``”)都替换为 `PR `#342`` 以同步恢复点
CQRS-REWRITE-RP-117 的入口指向最新评审上下文。

In `@GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs`:
- Around line 229-234: MediatR explicit
INotificationHandler<BenchmarkNotification>.Handle implementations (found in
BenchmarkNotificationHandler1, BenchmarkNotificationHandler2,
BenchmarkNotificationHandler3, BenchmarkNotificationHandler4) currently return
Task.CompletedTask and bypass shared checks; change each explicit Handle to
invoke the existing HandleCore(notification, cancellationToken) (or await/return
its Task) so the MediatR branch performs the same null/cancellation logic as the
baseline/GFramework/Mediator path and maintains consistent fan-out benchmarking.

In `@GFramework.Cqrs/README.md`:
- Around line 131-136: The markdown table containing
SequentialNotificationPublisher, TaskWhenAllNotificationPublisher and
UseNotificationPublisher needs a blank line before and after it to satisfy
MD058; edit the README.md around the table and insert one empty line immediately
above the pipe-row starting with "| 策略" and one empty line immediately below the
final paragraph row so the table is separated from surrounding text.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9bb2e5c4-b6b5-47dd-9de0-9cc298e5e66f

📥 Commits

Reviewing files that changed from the base of the PR and between 7ca21af and 4121e12.

📒 Files selected for processing (12)
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs
  • GFramework.Cqrs.Benchmarks/README.md
  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
  • GFramework.Cqrs/Extensions/NotificationPublisherRegistrationExtensions.cs
  • GFramework.Cqrs/Notification/SequentialNotificationPublisher.cs
  • GFramework.Cqrs/Notification/TaskWhenAllNotificationPublisher.cs
  • GFramework.Cqrs/README.md
  • ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
  • ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
  • docs/zh-CN/core/cqrs.md
📜 Review details
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Analyze (C#)
  • GitHub Check: Code Quality & Security
  • GitHub Check: Build and Test
🧰 Additional context used
📓 Path-based instructions (10)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Apply [Log] attribute for automatic logging field and logging helper method generation
Apply [Priority] attribute for automatic priority comparison implementation generation
Apply [GenerateEnumExtensions] attribute to generate enumeration extension capabilities
Apply [ContextAware] attribute to automatically implement IContextAware boilerplate logic

**/*.cs: All public, protected, and internal types and members MUST include XML documentation comments (///) in C#
XML documentation MUST use <summary>, <param>, <returns>, <exception>, and <remarks> where applicable, and explain intent, contract, and usage constraints instead of restating syntax
If a C# member participates in lifecycle, threading, registration, or disposal behavior, document that behavior explicitly
Core framework components (Architecture, Module, System, Context, Registry, Service Module, Lifecycle types) MUST include high-level explanations of responsibilities, lifecycle, interaction with other components, why abstraction exists, and when to use instead of alternatives
Generated logic and source generator pipelines MUST explain what is generated, why it is generated, semantic assumptions the generator relies on, and any diagnostics or fallback behavior
Do not rely on implicit imports. Declare every required using explicitly in C#
Write null-safe code that respects nullable annotations instead of suppressing warnings by default in C#
Use namespace pattern GFramework.{Module}.{Feature} with PascalCase segments in C#
Follow standard C# naming: Types/methods/properties/events/constants use PascalCase, Interfaces use I prefix, Parameters and locals use camelCase, Private fields use _camelCase
Use Allman braces style for C#
Keep using directives at the top of the file and sort them consistently in C#
Prefer one primary type per file unless surrounding project already uses different local pattern
Prefer explicit, readable code over clever shorthand in framework internals
M...

Files:

  • GFramework.Cqrs/Notification/TaskWhenAllNotificationPublisher.cs
  • GFramework.Cqrs/Extensions/NotificationPublisherRegistrationExtensions.cs
  • GFramework.Cqrs/Notification/SequentialNotificationPublisher.cs
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs
**/*[!.]*

📄 CodeRabbit inference engine (AGENTS.md)

For files with shebang lines, keep shebang as first line and place license header immediately after it

Files:

  • GFramework.Cqrs/Notification/TaskWhenAllNotificationPublisher.cs
  • GFramework.Cqrs.Benchmarks/README.md
  • GFramework.Cqrs/Extensions/NotificationPublisherRegistrationExtensions.cs
  • GFramework.Cqrs/README.md
  • GFramework.Cqrs/Notification/SequentialNotificationPublisher.cs
  • ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs
  • docs/zh-CN/core/cqrs.md
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs
  • ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
**/*.{cs,ts,tsx,js,jsx,py,sh}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{cs,ts,tsx,js,jsx,py,sh}: All generated or modified code MUST include clear and meaningful comments where required by documentation rules
Comments MUST NOT be trivial, redundant, or misleading. Prefer explaining why and when, not just what. Code should remain understandable without requiring external context
Avoid obvious comments such as // increment i

Files:

  • GFramework.Cqrs/Notification/TaskWhenAllNotificationPublisher.cs
  • GFramework.Cqrs/Extensions/NotificationPublisherRegistrationExtensions.cs
  • GFramework.Cqrs/Notification/SequentialNotificationPublisher.cs
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs
**/*.{cs,ts,tsx,js,jsx,py}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{cs,ts,tsx,js,jsx,py}: Add inline comments for non-trivial logic, concurrency/threading behavior, performance-sensitive paths, workarounds/compatibility constraints/edge cases, and registration order/lifecycle sequencing/generated code assumptions
Methods with non-trivial logic MUST document core idea, key decisions, and edge case handling
Separate logical blocks with blank lines when it improves readability
Unless there is clear and documented reason to keep file large, keep single source file under roughly 800-1000 lines
Validate external or user-controlled input before it reaches file system, serialization, reflection, code generation, or process boundaries
Do not build command strings, file paths, type names, or generated code from untrusted input without strict validation or allow-listing
Avoid logging secrets, tokens, credentials, or machine-specific sensitive data
Prefer least-privilege behavior for file, process, and environment access

Files:

  • GFramework.Cqrs/Notification/TaskWhenAllNotificationPublisher.cs
  • GFramework.Cqrs/Extensions/NotificationPublisherRegistrationExtensions.cs
  • GFramework.Cqrs/Notification/SequentialNotificationPublisher.cs
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs
**/*.{csproj,cs}

📄 CodeRabbit inference engine (AGENTS.md)

Framework runtime, abstractions, and meta-package projects MUST NOT reference *.SourceGenerators* projects or packages, and MUST NOT use source-generator attributes

Files:

  • GFramework.Cqrs/Notification/TaskWhenAllNotificationPublisher.cs
  • GFramework.Cqrs/Extensions/NotificationPublisherRegistrationExtensions.cs
  • GFramework.Cqrs/Notification/SequentialNotificationPublisher.cs
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs
**/*.{cs,ts,tsx,js,jsx,py,sh,xml,csproj,props,targets}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4 spaces for indentation. Do not use tabs

Files:

  • GFramework.Cqrs/Notification/TaskWhenAllNotificationPublisher.cs
  • GFramework.Cqrs/Extensions/NotificationPublisherRegistrationExtensions.cs
  • GFramework.Cqrs/Notification/SequentialNotificationPublisher.cs
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs
**/*.{cs,ts,tsx,js,jsx,py,sh,xml}

📄 CodeRabbit inference engine (AGENTS.md)

Keep line length readable. Around 120 characters is preferred upper bound

Files:

  • GFramework.Cqrs/Notification/TaskWhenAllNotificationPublisher.cs
  • GFramework.Cqrs/Extensions/NotificationPublisherRegistrationExtensions.cs
  • GFramework.Cqrs/Notification/SequentialNotificationPublisher.cs
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs
**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{md,mdx}: Keep code samples, package names, and command examples aligned with current repository state in documentation
When public page references XML docs or API coverage, convert evidence into reader-facing guidance: explain which types/namespaces/entry points readers should inspect and why
For integration-oriented features such as AI-First config system, documentation MUST cover: project directory layout/file conventions, required project/package wiring, minimal working example, migration/compatibility notes
When examples are rewritten, preserve only parts that remain true. Delete or replace speculative examples instead of lightly editing into another inaccurate form

Files:

  • GFramework.Cqrs.Benchmarks/README.md
  • GFramework.Cqrs/README.md
  • ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
  • docs/zh-CN/core/cqrs.md
  • ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
docs/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Documentation should be organized with Chinese content in docs/zh-CN/ and structured to include getting started, module-specific capabilities (Core, Game, Godot, ECS), source generator usage, tutorials, best practices, and troubleshooting

Files:

  • docs/zh-CN/core/cqrs.md
**/Cqrs/**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

Use CQRS (Command Query Responsibility Segregation) pattern with the Cqrs naming entry point instead of the historical Mediator alias

Files:

  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
🧠 Learnings (1)
📚 Learning: 2026-04-06T12:45:43.921Z
Learnt from: GeWuYou
Repo: GeWuYou/GFramework PR: 190
File: GFramework.Game/Config/GameConfigBootstrap.cs:1-3
Timestamp: 2026-04-06T12:45:43.921Z
Learning: In the GeWuYou/GFramework repository, C# files may omit explicit `using System*` imports because the project-wide `GlobalUsings.cs` (referenced via manual global `using` directives) supplies common namespaces (e.g., `System`, `System.Threading`, `System.Threading.Tasks`). During code review, do not flag missing `using System...` directives in `.cs` files as long as `GlobalUsings.cs` is present/used to provide those namespaces.

Applied to files:

  • GFramework.Cqrs/Notification/TaskWhenAllNotificationPublisher.cs
  • GFramework.Cqrs/Extensions/NotificationPublisherRegistrationExtensions.cs
  • GFramework.Cqrs/Notification/SequentialNotificationPublisher.cs
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs
🪛 LanguageTool
GFramework.Cqrs/README.md

[uncategorized] ~134-~134: 您的意思是“"不"齐”?
Context: ...序 | 不会在首个失败时停止其余处理器;会聚合最终异常或取消结果 | 更适合语义补齐,不是性能开关 | | `UseNotificationPublish...

(BU)

🪛 markdownlint-cli2 (0.22.1)
GFramework.Cqrs/README.md

[warning] 135-135: Tables should be surrounded by blank lines

(MD058, blanks-around-tables)

🔇 Additional comments (9)
GFramework.Cqrs/Notification/SequentialNotificationPublisher.cs (1)

8-35: 实现与文档语义一致,改动可接受。

公开化后的类型与“顺序分发、失败即停”行为保持一致,注释也覆盖了关键契约。

GFramework.Cqrs/Notification/TaskWhenAllNotificationPublisher.cs (1)

27-58: 并行发布路径设计清晰,边界处理到位。

0/1/多处理器分支与 Task.WhenAll 聚合路径实现一致,可读性和可维护性都不错。

GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs (1)

73-93: 基准扩展与清理路径同步,改动稳健。

新增 Mediator 对照路径后,初始化/释放和处理器契约都配套更新了,整体实现一致性很好。

Also applies to: 113-155

GFramework.Cqrs/Extensions/NotificationPublisherRegistrationExtensions.cs (1)

30-111: 注册入口分层明确,防重复注册逻辑合理。

API 形态(实例/类型/内置快捷)和冲突保护都设计得很清楚。

docs/zh-CN/core/cqrs.md (1)

120-162: 文档与实现对齐良好,策略选择说明清晰。

新增矩阵与示例能直接指导组合根配置,实用性很高。

GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs (1)

94-127: 新增用例有效覆盖了并行发布器的核心语义差异。

该测试能稳定验证“异常后仍触发后续处理器”的行为,价值很高。

GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs (1)

25-106: 注册扩展的关键行为覆盖完整。

语义测试(并行/顺序/实例复用/重复注册)都覆盖到了,回归保护充分。

GFramework.Cqrs.Benchmarks/README.md (1)

32-34: 这段文档补充清晰且对齐当前 benchmark 场景

NotificationFanOutBenchmarks 的入口与对比维度描述完整,便于读者快速定位新增基准。

ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md (1)

5-180: 迁移追踪记录完整,恢复信息可执行性强

阶段决策、验证命令和结论都落到了同一段里,作为恢复入口可读性很好。

Comment thread ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md Outdated
Comment thread GFramework.Cqrs/README.md Outdated
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 8, 2026

Greptile Summary

This PR introduces two built-in notification publisher strategies (SequentialNotificationPublisher and TaskWhenAllNotificationPublisher), composition-root extension methods for registering them in the IoC container, and a fan-out benchmark suite comparing GFramework against MediatR and the Mediator source-generated library.

  • NotificationPublisherRegistrationExtensions adds UseTaskWhenAllNotificationPublisher, UseSequentialNotificationPublisher, and UseNotificationPublisher overloads that register a publisher strategy in the container and guard against duplicate registration.
  • TaskWhenAllNotificationPublisher fans out to all handlers concurrently, using an async wrapper (InvokeHandlerSafelyAsync) to ensure synchronous handler exceptions are captured in faulted tasks rather than escaping the Task.WhenAll loop.
  • NotificationFanOutBenchmarks adds a four-handler fan-out benchmark comparing the two new strategies against MediatR and the Mediator library.

Confidence Score: 4/5

Safe to merge for projects that use the ArchitectureContext / module startup path; developers using the two-argument CqrsRuntimeFactory.CreateRuntime overload after registering a publisher in the container will silently receive sequential dispatch instead of their chosen strategy.

The container registration extensions work correctly through the module path but do not integrate with the two-argument CqrsRuntimeFactory.CreateRuntime overload, which always constructs a new SequentialNotificationPublisher regardless of what is registered in the container.

GFramework.Cqrs/Extensions/NotificationPublisherRegistrationExtensions.cs — the integration gap with CqrsRuntimeFactory's two-argument overload should be addressed either in the factory or documented clearly in the extension's remarks.

Important Files Changed

Filename Overview
GFramework.Cqrs/Extensions/NotificationPublisherRegistrationExtensions.cs New composition-root extension for registering notification publisher strategies; has a gap where registration is silently ignored when the runtime is created via the two-argument CqrsRuntimeFactory overload rather than through the module path.
GFramework.Cqrs/Notification/TaskWhenAllNotificationPublisher.cs New parallel notification publisher using Task.WhenAll; correctly wraps synchronous throws via InvokeHandlerSafelyAsync for the N>1 path, but the single-handler fast path inconsistently allows synchronous exceptions to escape.
GFramework.Cqrs/Notification/SequentialNotificationPublisher.cs New sequential publisher that extracts the pre-existing dispatcher behavior into an explicit, composable type; implementation is straightforward and well-documented.
GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs New test file covering instance-overload registration, duplicate-registration guard, and sequential/parallel semantics; generic type-registration overload UseNotificationPublisher lacks coverage.
GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs Adds a test verifying TaskWhenAllNotificationPublisher continues to invoke handlers after the first one throws; implementation is correct.
GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs New fan-out benchmark comparing GFramework sequential and TaskWhenAll publishers against MediatR and the Mediator source-generated library; handler parity with the baseline is maintained correctly.
GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs Adds the Mediator source-generated library as an additional benchmark comparison; renames existing fields for clarity; straightforward extension.
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
GFramework.Cqrs/Extensions/NotificationPublisherRegistrationExtensions.cs:17-22
**Container registration silently ignored by `CqrsRuntimeFactory.CreateRuntime(container, logger)`**

`UseNotificationPublisher` / `UseTaskWhenAllNotificationPublisher` / `UseSequentialNotificationPublisher` register `INotificationPublisher` in the IoC container, but `CqrsRuntimeFactory.CreateRuntime(IIocContainer, ILogger)` — the two-argument overload that is the most common direct-use entry point — always creates a fresh `new SequentialNotificationPublisher()` without ever querying the container for a registered publisher. A developer who follows the natural pattern of "register in container → create runtime from the same container" will silently receive sequential publishing regardless of the strategy they registered, with no error or warning.

The XML `<remarks>` mentions that the module path will honor the registration, but it does not explicitly warn that `CqrsRuntimeFactory.CreateRuntime(container, logger)` ignores it. Either the factory's two-argument overload should be updated to check `container.HasRegistration(typeof(INotificationPublisher))` and use the resolved publisher, or the `<remarks>` should add a clear note that direct factory use must pass the publisher explicitly via the three-argument overload.

### Issue 2 of 2
GFramework.Cqrs/Notification/TaskWhenAllNotificationPublisher.cs:34-39
The fast path for a single handler calls `context.InvokeHandlerAsync` directly from a non-`async` method. If the underlying handler throws synchronously (before its first `await`), the exception escapes `PublishAsync` synchronously rather than being captured in a faulted `ValueTask`. For the N>1 case, `InvokeHandlerSafelyAsync`'s `async` state machine captures those same synchronous throws. Routing the single-handler case through `InvokeHandlerSafelyAsync` keeps exception-delivery semantics consistent for all callers.

```suggestion
        return context.Handlers.Count switch
        {
            0 => ValueTask.CompletedTask,
            1 => InvokeHandlerSafelyAsync(context, context.Handlers[0], cancellationToken),
            _ => PublishCoreAsync(context, cancellationToken)
        };
```

Reviews (2): Last reviewed commit: "fix(cqrs): 收口 PR342 审查遗留问题" | Re-trigger Greptile

Comment thread GFramework.Cqrs/README.md Outdated
- 修复 NotificationFanOutBenchmarks 中 MediatR handler 绕过 HandleCore 的对照偏差

- 更新 README 与中文文档中的 notification publisher 示例和表格格式

- 同步 cqrs-rewrite tracking 与 trace 到 PR #342 审查恢复点和最新验证结果
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 8, 2026

Summary

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Other ❓ Flaky 🍂 Duration ⏱️
2330 2330 0 0 0 0 38.8s    ↑1.2s

Test Results

passed 2330 passed

Details

tests 2330 tests
clock 38.8s ↑1.2s
tool nunit
build CI - Build & Test arrow-right build-and-test link #1095
pull-request Feat/cqrs optimization link #342

Insights

Average Tests per Run Total Flaky Tests Total Failed Slowest Test (p95)
2238 0 1 4.7s

Fail Rate

Fail Rate 0.00%
Test 📝 Results 📊 Passed ✅ Failed ❌ Fail Rate (%) 📈
CreateStream_Should_Throw_When_Stream_Pipeline_Behavior_Context_Does_Not_Implement_IArchitectureContext 9 8 1 11.11    ↓1.39

build-and-test: Run #1095

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
2330 2330 0 0 0 0 0 38.8s

🎉 All tests passed!

Slowest Tests

Test 📝 Results 📊 Duration (avg) ⏱️ Duration (p95) ⏱️
CreateStream_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently 27 4.1s 4.7s
SendRequestAsync_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently 42 1.5s 4.7s
Does_Not_Report_When_FieldInjectedModel_Is_Registered 42 2.2s 2.3s
Generates_Scene_Behavior_Boilerplate 42 1.9s 2.0s
CleanupDuringAcquire_Should_NotCauseRaceCondition 42 1.1s 1.1s
Append_ShouldNotBlock 42 1.0s 1.0s
Context_Caching_Should_Improve_Performance 42 782ms 794ms
PendingCount_ShouldReflectQueuedEntries 42 501ms 501ms
Cleanup_Should_NotRemoveActiveLocks 42 404ms 405ms
Cleanup_Should_RemoveUnusedLocks 42 401ms 402ms

± Comparison with run #1094 at 341205d | 🍂 No flaky tests detected across all runs. | ⏱️ Measured over 42 runs.

Github Test Reporter by CTRF 💚

Comment on lines +17 to +22
{
/// <summary>
/// 将指定的 notification publisher 实例注册为当前容器唯一的发布策略。
/// </summary>
/// <param name="container">目标依赖注入容器。</param>
/// <param name="notificationPublisher">要复用的 notification publisher 实例。</param>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Container registration silently ignored by CqrsRuntimeFactory.CreateRuntime(container, logger)

UseNotificationPublisher / UseTaskWhenAllNotificationPublisher / UseSequentialNotificationPublisher register INotificationPublisher in the IoC container, but CqrsRuntimeFactory.CreateRuntime(IIocContainer, ILogger) — the two-argument overload that is the most common direct-use entry point — always creates a fresh new SequentialNotificationPublisher() without ever querying the container for a registered publisher. A developer who follows the natural pattern of "register in container → create runtime from the same container" will silently receive sequential publishing regardless of the strategy they registered, with no error or warning.

The XML <remarks> mentions that the module path will honor the registration, but it does not explicitly warn that CqrsRuntimeFactory.CreateRuntime(container, logger) ignores it. Either the factory's two-argument overload should be updated to check container.HasRegistration(typeof(INotificationPublisher)) and use the resolved publisher, or the <remarks> should add a clear note that direct factory use must pass the publisher explicitly via the three-argument overload.

Prompt To Fix With AI
This is a comment left during a code review.
Path: GFramework.Cqrs/Extensions/NotificationPublisherRegistrationExtensions.cs
Line: 17-22

Comment:
**Container registration silently ignored by `CqrsRuntimeFactory.CreateRuntime(container, logger)`**

`UseNotificationPublisher` / `UseTaskWhenAllNotificationPublisher` / `UseSequentialNotificationPublisher` register `INotificationPublisher` in the IoC container, but `CqrsRuntimeFactory.CreateRuntime(IIocContainer, ILogger)` — the two-argument overload that is the most common direct-use entry point — always creates a fresh `new SequentialNotificationPublisher()` without ever querying the container for a registered publisher. A developer who follows the natural pattern of "register in container → create runtime from the same container" will silently receive sequential publishing regardless of the strategy they registered, with no error or warning.

The XML `<remarks>` mentions that the module path will honor the registration, but it does not explicitly warn that `CqrsRuntimeFactory.CreateRuntime(container, logger)` ignores it. Either the factory's two-argument overload should be updated to check `container.HasRegistration(typeof(INotificationPublisher))` and use the resolved publisher, or the `<remarks>` should add a clear note that direct factory use must pass the publisher explicitly via the three-argument overload.

How can I resolve this? If you propose a fix, please make it concise.

@GeWuYou GeWuYou enabled auto-merge May 8, 2026 12:03
@GeWuYou GeWuYou merged commit d389eb3 into main May 8, 2026
7 checks passed
@GeWuYou GeWuYou deleted the feat/cqrs-optimization branch May 8, 2026 12:08
@coderabbitai coderabbitai Bot mentioned this pull request May 9, 2026
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.

1 participant