feat(messaging): Outbox dispatcher + Inbox + RabbitMQ publisher (fase 1) - #1
Conversation
Implementa o núcleo de mensageria confiável dos BuildingBlocks (spec §5/§10), sem tocar nos serviços ainda. Messaging (sem dependência de EF, para evitar ciclo): - OutboxRecord, IEventSerializer + JsonEventSerializer - IOutboxProcessor (recebe o publish como callback; a transação vive no Persistence) - OutboxDispatcher (BackgroundService, polling, escopo por iteração, logging source-gen) - RabbitMqEventPublisher (RabbitMQ.Client v7, publisher confirms, Id do evento -> message-id) - DI: AddRabbitMqPublisher / AddOutboxDispatcher Persistence (depende de Messaging): - Configs EF snake_case (outbox/inbox), payload jsonb, índice parcial em pending - MessagingDbContext base, EfOutbox (mesma UoW), EfInbox (dedup por PK) - EfOutboxProcessor: SELECT ... FOR UPDATE SKIP LOCKED em transação; marca processed só após o confirm - DI: AddOutboxInbox Testes: - OutboxDispatcherTests (Testcontainers: Postgres + RabbitMQ reais) prova publish exactly-once com message-id correto e 2ª drenagem no-op Pacotes ajustados para versões reais (EF 10.0.8, Npgsql 10.0.2, RabbitMQ.Client 7.2.1, Testcontainers 4.12.0). Build verde; Unit 4/4; Integration 1 passou + 3 skipped.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughThis PR introduces a transactional outbox pattern for reliable event publishing to RabbitMQ. It adds messaging contracts and types, a JSON event serializer, a RabbitMQ publisher with confirmations, a background OutboxDispatcher, EF Core schema and implementations for outbox/inbox processing with row locking, DI registration extensions, package/version updates, and integration tests validating exactly-once delivery. ChangesOutbox Pattern with RabbitMQ Integration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (8)
src/BuildingBlocks/Messaging/IOutboxProcessor.cs (1)
3-8: 💤 Low valueConsider removing database-specific implementation detail from interface documentation.
The documentation mentions
FOR UPDATE SKIP LOCKED, which is a Postgres-specific locking mechanism. While the current implementation uses Postgres, the interface abstraction should remain database-agnostic. Consider rephrasing to describe the behavior (e.g., "locks selected rows to prevent concurrent processing") rather than the specific SQL syntax.📝 Suggested documentation improvement
/// <summary> -/// Drains a batch of pending outbox rows inside a single DB transaction that holds a -/// <c>FOR UPDATE SKIP LOCKED</c> lock on the selected rows. The transaction (and the EF -/// dependency) lives in the persistence layer; the publish side is injected as a callback -/// so Messaging stays free of any EF reference (avoids the circular dependency). +/// Drains a batch of pending outbox rows inside a single DB transaction that locks +/// selected rows to prevent concurrent processing. The transaction (and the EF dependency) +/// lives in the persistence layer; the publish side is injected as a callback so Messaging +/// stays free of any EF reference (avoids the circular dependency). /// </summary>🤖 Prompt for 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. In `@src/BuildingBlocks/Messaging/IOutboxProcessor.cs` around lines 3 - 8, Update the XML doc on the IOutboxProcessor interface to remove the Postgres-specific phrase "FOR UPDATE SKIP LOCKED" and instead describe the behavior: that the method drains a batch of pending outbox rows inside a single database transaction which locks the selected rows to prevent concurrent processing, while keeping the publish callback injection and EF/persistence separation wording intact (edit the summary for the method/ interface named IOutboxProcessor to be database-agnostic and describe the locking behaviour rather than specific SQL syntax).src/BuildingBlocks/Messaging/JsonEventSerializer.cs (1)
12-12: 💤 Low valueJsonSerializerDefaults.Web uses camelCase property naming.
The
JsonSerializerDefaults.Webpreset configures property names to be camelCase (e.g.,orderIdinstead ofOrderId). Ensure downstream consumers expect this convention, or explicitly document it as part of the message contract.🤖 Prompt for 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. In `@src/BuildingBlocks/Messaging/JsonEventSerializer.cs` at line 12, The JsonEventSerializer currently uses JsonSerializerDefaults.Web which applies camelCase property names (JsonSerializerOptions Options = new(JsonSerializerDefaults.Web)); update the serializer to make naming explicit and align with the message contract: either set Options.PropertyNamingPolicy explicitly (e.g., JsonNamingPolicy.CamelCase if consumers expect camelCase, or null/Default if PascalCase is required) and remove reliance on the Web preset, or add clear documentation in the message contract for JsonEventSerializer that downstream consumers must expect camelCase property names; adjust any serialization/deserialization tests and consumers accordingly to match the chosen policy.tests/Integration/OutboxDispatcherTests.cs (1)
99-111: 💤 Low valueConsider adding validation for UserInfo parsing edge cases.
The password parsing logic at line 102 could throw
IndexOutOfRangeExceptionifuri.UserInfois empty or malformed. While Testcontainers should always provide a valid connection string, adding a guard would make the test more robust.🛡️ Proposed defensive fix
private IOptions<RabbitMqOptions> BuildRabbitOptions() { var uri = new Uri(_rabbit.GetConnectionString()); - var parts = uri.UserInfo.Split(':', 2); + if (string.IsNullOrEmpty(uri.UserInfo)) + { + throw new InvalidOperationException("RabbitMQ connection string missing UserInfo"); + } + + var parts = uri.UserInfo.Split(':', 2); return Options.Create(new RabbitMqOptions { Host = uri.Host,🤖 Prompt for 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. In `@tests/Integration/OutboxDispatcherTests.cs` around lines 99 - 111, The UserInfo parsing in BuildRabbitOptions can throw when uri.UserInfo is empty/malformed; update BuildRabbitOptions to defensively handle that by checking string.IsNullOrEmpty(uri.UserInfo) before splitting, or by using a split that safely yields zero/one/two elements, then set Username and Password from the resulting parts with fallbacks (empty string) if parts.Length < 1 or < 2; ensure you reference BuildRabbitOptions, uri.UserInfo and the parts array when making the guard so Username/Password never cause an IndexOutOfRangeException.src/BuildingBlocks/Persistence/Configurations/OutboxMessageConfiguration.cs (2)
18-18: ⚡ Quick winConsider adding a check constraint for non-negative attempts.
Adding a check constraint ensures data integrity at the database level, preventing invalid retry counts.
🛡️ Suggested enhancement
-builder.Property(x => x.Attempts).HasColumnName("attempts"); +builder.Property(x => x.Attempts) + .HasColumnName("attempts") + .HasCheckConstraint("CK_outbox_attempts_nonnegative", "attempts >= 0");🤖 Prompt for 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. In `@src/BuildingBlocks/Persistence/Configurations/OutboxMessageConfiguration.cs` at line 18, Add a DB-level check constraint to ensure OutboxMessage attempts cannot be negative: in OutboxMessageConfiguration after the existing builder.Property(x => x.Attempts).HasColumnName("attempts") call, add a HasCheckConstraint on the entity (using OutboxMessageConfiguration's builder) such as a constraint name like "CK_OutboxMessage_Attempts_NonNegative" with condition referencing the attempts column (e.g., "attempts >= 0") so the database enforces non-negative values for the Attempts property.
12-13: ⚡ Quick winConsider explicit column type for Id.
Explicitly configuring the Id column type as
uuidimproves schema clarity and ensures consistent behavior across environments.📝 Suggested enhancement
builder.HasKey(x => x.Id); -builder.Property(x => x.Id).HasColumnName("id"); +builder.Property(x => x.Id) + .HasColumnName("id") + .HasColumnType("uuid");🤖 Prompt for 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. In `@src/BuildingBlocks/Persistence/Configurations/OutboxMessageConfiguration.cs` around lines 12 - 13, The Id column lacks an explicit DB type; update the OutboxMessageConfiguration to set the column type for the Id property by extending the builder.Property(x => x.Id) configuration (the same property referenced by builder.HasKey(x => x.Id)) to include a HasColumnType("uuid") call so the column is created as uuid in the schema.src/BuildingBlocks/Persistence/Configurations/InboxMessageConfiguration.cs (1)
13-14: ⚡ Quick winConsider explicit column type for MessageId.
While EF Core will infer the column type from the C# property, explicitly configuring it improves clarity and prevents migration drift if the property type changes.
📝 Suggested enhancement
If
MessageIdis aGuid:builder.HasKey(x => x.MessageId); -builder.Property(x => x.MessageId).HasColumnName("message_id"); +builder.Property(x => x.MessageId) + .HasColumnName("message_id") + .HasColumnType("uuid");If
MessageIdis astring:builder.HasKey(x => x.MessageId); -builder.Property(x => x.MessageId).HasColumnName("message_id"); +builder.Property(x => x.MessageId) + .HasColumnName("message_id") + .HasMaxLength(255);🤖 Prompt for 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. In `@src/BuildingBlocks/Persistence/Configurations/InboxMessageConfiguration.cs` around lines 13 - 14, The mapping for MessageId in InboxMessageConfiguration currently only sets the column name; explicitly configure the column type to prevent migration drift: update the builder.Property(x => x.MessageId) chain to include HasColumnType with the appropriate SQL type (e.g., "uuid" or "uniqueidentifier" for a Guid, or a sized text type like "varchar(36)" / "nvarchar(36)" for a string) while keeping builder.HasKey(x => x.MessageId) and the HasColumnName("message_id") call.src/BuildingBlocks/Persistence/EfOutboxProcessor.cs (1)
41-47: 💤 Low valueConsider documenting at-least-once delivery semantics in failure scenarios.
The current implementation provides exactly-once publishing in the happy path. However, if
publishAsyncsucceeds (message confirmed by RabbitMQ) butSaveChangesorCommitAsyncsubsequently fails, the row remains pending and will be re-published on the next poll, resulting in at-least-once delivery.This is inherent to the outbox pattern with a separate message broker and requires idempotent consumers. Consider adding a note to the class documentation to make this explicit for future maintainers.
🤖 Prompt for 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. In `@src/BuildingBlocks/Persistence/EfOutboxProcessor.cs` around lines 41 - 47, Update the EfOutboxProcessor class documentation to explicitly state that while the happy path provides single publish and removal, failures after publishAsync (e.g., SaveChangesAsync or CommitAsync failing) will leave the outbox row pending and lead to message re-publish on the next poll, resulting in at-least-once delivery semantics; mention that consumers must be idempotent and reference the relevant methods publishAsync, SaveChangesAsync and CommitAsync in the doc comment so future maintainers understand the failure mode and needed precautions.src/BuildingBlocks/Messaging/MessagingServiceCollectionExtensions.cs (1)
20-27: ⚡ Quick winConsider guarding against multiple dispatcher registrations.
AddHostedService(line 25) does not prevent duplicate registrations likeTryAddSingletondoes. CallingAddOutboxDispatchermultiple times will register multipleOutboxDispatcherinstances that run concurrently and compete for work viaFOR UPDATE SKIP LOCKED.While functionally safe, multiple instances within a single process create unnecessary overhead (duplicate polling, scope creation, and queries) unless explicitly desired for in-process horizontal scaling.
Consider using
TryAddEnumerableto prevent accidental double-registration:🛡️ Suggested guard using TryAddEnumerable
services.Configure(configure ?? (_ => { })); - services.AddHostedService<OutboxDispatcher>(); + services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, OutboxDispatcher>()); return services;Alternatively, document that multiple registrations are supported if in-process scaling is an intentional design choice.
🤖 Prompt for 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. In `@src/BuildingBlocks/Messaging/MessagingServiceCollectionExtensions.cs` around lines 20 - 27, The AddOutboxDispatcher extension currently calls services.AddHostedService<OutboxDispatcher>() which allows duplicate registrations; change it to register the dispatcher with TryAddEnumerable to prevent accidental double-registration by using TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, typeof(OutboxDispatcher) / new ServiceDescriptor(typeof(IHostedService), typeof(OutboxDispatcher), ServiceLifetime.Singleton)>) so that AddOutboxDispatcher (and OutboxDispatcher) are guarded against multiple registrations while keeping the existing services.Configure(configure) behavior; update the registration call referencing AddOutboxDispatcher, OutboxDispatcher, AddHostedService and TryAddEnumerable accordingly.
🤖 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 `@src/BuildingBlocks/Messaging/JsonEventSerializer.cs`:
- Around line 14-21: JsonEventSerializer.Serialize currently uses
message.GetType().Name as the event discriminator (in method Serialize and for
IntegrationEvent messages), which is fragile; change it to use a more stable
discriminator such as message.GetType().FullName (falling back to Name if
FullName is null), and sanitize common artifacts (replace '+' with '.' and strip
generic backtick suffixes) so generic and nested types produce reasonable
strings; additionally add a TODO comment and/or plan to implement a robust type
registry/attribute-based mapping (e.g., EventTypeAttribute) for future stable
identifiers and ensure consumers use the same mapping for deserialization.
In `@src/BuildingBlocks/Messaging/RabbitMqEventPublisher.cs`:
- Around line 88-101: The DisposeAsync method currently disposes _channel,
_connection and _gate sequentially so an exception from _channel.DisposeAsync()
can prevent disposing the others; update RabbitMqEventPublisher.DisposeAsync to
ensure each resource is disposed regardless of earlier failures by wrapping each
await _channel.DisposeAsync(), await _connection.DisposeAsync(), and
_gate.Dispose() in their own try-catch (or using a try/finally that guarantees
subsequent disposals), catching and optionally logging exceptions so disposal
proceeds for _connection and _gate even if _channel disposal fails.
- Around line 22-43: The PublishAsync method uses record.Type and record.Payload
without validating them; add defensive null/empty checks at the start of
PublishAsync (after ArgumentNullException.ThrowIfNull(record)) to throw
ArgumentException/ArgumentNullException with clear messages if record.Type is
null/empty or record.Payload is null; update error messages to reference
OutboxRecord.Id for context, and only proceed to call EnsureChannelAsync,
construct BasicProperties, and call channel.BasicPublishAsync when these
validations pass.
In `@src/BuildingBlocks/Persistence/BuildingBlocks.Persistence.csproj`:
- Line 13: The project TFM is set to net10.0 in the TargetFramework element of
BuildingBlocks.Persistence.csproj which requires a .NET 10 SDK (preview) not
guaranteed in current CI/dev images; either pin the required preview SDK by
adding/updating a global.json that references the exact .NET 10 preview SDK
version used in your build matrix, or change the TargetFramework in
BuildingBlocks.Persistence.csproj from net10.0 to a GA TFM your CI already
supports (e.g., net8.0 or net7.0) and update any API usage accordingly so
local/CI builds no longer depend on an unavailable preview SDK.
In `@src/BuildingBlocks/Persistence/EfOutboxProcessor.cs`:
- Around line 35-39: EfOutboxProcessor currently reads OutboxMessage.Attempts
but never updates it; implement attempt tracking and backoff by incrementing
Attempts and recording a LastAttemptAt timestamp on each process try, performing
a backoff check before calling publishAsync, and only setting ProcessedAt after
a successful publish; specifically, in the loop that iterates pending messages
(where publishAsync(new OutboxRecord(...)) is called) update message.Attempts++
and message.LastAttemptAt = DateTimeOffset.UtcNow before attempting publish,
skip/pause attempts if (now - message.LastAttemptAt) <
backoffForAttempts(message.Attempts), and on successful publish set
message.ProcessedAt = UtcNow, while on repeated failures when message.Attempts
exceeds a poison threshold mark it as failed/dead-letter (or set ProcessedAt and
a Poisoned flag) so poison-message handling works as intended.
---
Nitpick comments:
In `@src/BuildingBlocks/Messaging/IOutboxProcessor.cs`:
- Around line 3-8: Update the XML doc on the IOutboxProcessor interface to
remove the Postgres-specific phrase "FOR UPDATE SKIP LOCKED" and instead
describe the behavior: that the method drains a batch of pending outbox rows
inside a single database transaction which locks the selected rows to prevent
concurrent processing, while keeping the publish callback injection and
EF/persistence separation wording intact (edit the summary for the method/
interface named IOutboxProcessor to be database-agnostic and describe the
locking behaviour rather than specific SQL syntax).
In `@src/BuildingBlocks/Messaging/JsonEventSerializer.cs`:
- Line 12: The JsonEventSerializer currently uses JsonSerializerDefaults.Web
which applies camelCase property names (JsonSerializerOptions Options =
new(JsonSerializerDefaults.Web)); update the serializer to make naming explicit
and align with the message contract: either set Options.PropertyNamingPolicy
explicitly (e.g., JsonNamingPolicy.CamelCase if consumers expect camelCase, or
null/Default if PascalCase is required) and remove reliance on the Web preset,
or add clear documentation in the message contract for JsonEventSerializer that
downstream consumers must expect camelCase property names; adjust any
serialization/deserialization tests and consumers accordingly to match the
chosen policy.
In `@src/BuildingBlocks/Messaging/MessagingServiceCollectionExtensions.cs`:
- Around line 20-27: The AddOutboxDispatcher extension currently calls
services.AddHostedService<OutboxDispatcher>() which allows duplicate
registrations; change it to register the dispatcher with TryAddEnumerable to
prevent accidental double-registration by using
TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService,
typeof(OutboxDispatcher) / new ServiceDescriptor(typeof(IHostedService),
typeof(OutboxDispatcher), ServiceLifetime.Singleton)>) so that
AddOutboxDispatcher (and OutboxDispatcher) are guarded against multiple
registrations while keeping the existing services.Configure(configure) behavior;
update the registration call referencing AddOutboxDispatcher, OutboxDispatcher,
AddHostedService and TryAddEnumerable accordingly.
In `@src/BuildingBlocks/Persistence/Configurations/InboxMessageConfiguration.cs`:
- Around line 13-14: The mapping for MessageId in InboxMessageConfiguration
currently only sets the column name; explicitly configure the column type to
prevent migration drift: update the builder.Property(x => x.MessageId) chain to
include HasColumnType with the appropriate SQL type (e.g., "uuid" or
"uniqueidentifier" for a Guid, or a sized text type like "varchar(36)" /
"nvarchar(36)" for a string) while keeping builder.HasKey(x => x.MessageId) and
the HasColumnName("message_id") call.
In `@src/BuildingBlocks/Persistence/Configurations/OutboxMessageConfiguration.cs`:
- Line 18: Add a DB-level check constraint to ensure OutboxMessage attempts
cannot be negative: in OutboxMessageConfiguration after the existing
builder.Property(x => x.Attempts).HasColumnName("attempts") call, add a
HasCheckConstraint on the entity (using OutboxMessageConfiguration's builder)
such as a constraint name like "CK_OutboxMessage_Attempts_NonNegative" with
condition referencing the attempts column (e.g., "attempts >= 0") so the
database enforces non-negative values for the Attempts property.
- Around line 12-13: The Id column lacks an explicit DB type; update the
OutboxMessageConfiguration to set the column type for the Id property by
extending the builder.Property(x => x.Id) configuration (the same property
referenced by builder.HasKey(x => x.Id)) to include a HasColumnType("uuid") call
so the column is created as uuid in the schema.
In `@src/BuildingBlocks/Persistence/EfOutboxProcessor.cs`:
- Around line 41-47: Update the EfOutboxProcessor class documentation to
explicitly state that while the happy path provides single publish and removal,
failures after publishAsync (e.g., SaveChangesAsync or CommitAsync failing) will
leave the outbox row pending and lead to message re-publish on the next poll,
resulting in at-least-once delivery semantics; mention that consumers must be
idempotent and reference the relevant methods publishAsync, SaveChangesAsync and
CommitAsync in the doc comment so future maintainers understand the failure mode
and needed precautions.
In `@tests/Integration/OutboxDispatcherTests.cs`:
- Around line 99-111: The UserInfo parsing in BuildRabbitOptions can throw when
uri.UserInfo is empty/malformed; update BuildRabbitOptions to defensively handle
that by checking string.IsNullOrEmpty(uri.UserInfo) before splitting, or by
using a split that safely yields zero/one/two elements, then set Username and
Password from the resulting parts with fallbacks (empty string) if parts.Length
< 1 or < 2; ensure you reference BuildRabbitOptions, uri.UserInfo and the parts
array when making the guard so Username/Password never cause an
IndexOutOfRangeException.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: a556cdfa-eba4-4127-832e-740a17898d42
📒 Files selected for processing (22)
Directory.Packages.propssrc/BuildingBlocks/Messaging/BuildingBlocks.Messaging.csprojsrc/BuildingBlocks/Messaging/IEventPublisher.cssrc/BuildingBlocks/Messaging/IEventSerializer.cssrc/BuildingBlocks/Messaging/IOutboxProcessor.cssrc/BuildingBlocks/Messaging/JsonEventSerializer.cssrc/BuildingBlocks/Messaging/MessagingServiceCollectionExtensions.cssrc/BuildingBlocks/Messaging/OutboxDispatcher.cssrc/BuildingBlocks/Messaging/OutboxDispatcherOptions.cssrc/BuildingBlocks/Messaging/OutboxRecord.cssrc/BuildingBlocks/Messaging/RabbitMqEventPublisher.cssrc/BuildingBlocks/Messaging/RabbitMqOptions.cssrc/BuildingBlocks/Persistence/BuildingBlocks.Persistence.csprojsrc/BuildingBlocks/Persistence/Configurations/InboxMessageConfiguration.cssrc/BuildingBlocks/Persistence/Configurations/OutboxMessageConfiguration.cssrc/BuildingBlocks/Persistence/EfInbox.cssrc/BuildingBlocks/Persistence/EfOutbox.cssrc/BuildingBlocks/Persistence/EfOutboxProcessor.cssrc/BuildingBlocks/Persistence/MessagingDbContext.cssrc/BuildingBlocks/Persistence/PersistenceServiceCollectionExtensions.cstests/Integration/OutboxDispatcherTests.cstests/Integration/Tests.Integration.csproj
| </ItemGroup> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net10.0</TargetFramework> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check installed .NET SDK versions and validate target framework
# Check installed SDKs
dotnet --list-sdks
# Check if the project builds successfully
dotnet build src/BuildingBlocks/Persistence/BuildingBlocks.Persistence.csproj --configuration Debug 2>&1 | head -20Repository: thomasmoreira/distributed-consistency-lab
Length of output: 193
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Print the relevant csproj section around the TargetFramework
FILE="src/BuildingBlocks/Persistence/BuildingBlocks.Persistence.csproj"
if [ -f "$FILE" ]; then
sed -n '1,60p' "$FILE" | cat -n
else
echo "Missing file: $FILE"
exit 1
fiRepository: thomasmoreira/distributed-consistency-lab
Length of output: 721
🌐 Web query:
.NET target framework monikers net10.0 released preview as of 2025
💡 Result:
As of 2026-06-08, the.NET target framework moniker (TFM) for.NET 10 is net10.0 [1]..NET 10 was released in 2025, with its first preview (Preview 1) announced on February 25, 2025 [2]. It subsequently underwent a standard preview release cycle throughout the year before the General Availability (GA) version, 10.0.0, was officially released on November 11, 2025 [3][4][5]. The net10.0 TFM is currently the standard moniker for targeting the stable.NET 10 framework [1].
Citations:
- 1: https://learn.microsoft.com/en-us/dotnet/standard/frameworks
- 2: https://devblogs.microsoft.com/dotnet/dotnet-10-preview-1/
- 3: https://github.com/dotnet/core/blob/main/release-notes/10.0/10.0.0/10.0.0.md
- 4: https://github.com/dotnet/core/releases/tag/v10.0.0
- 5: https://versionsof.net/core/10.0/10.0.0/
Clarify net10.0 support: it’s .NET 10 (preview in early 2025), not a March-2025 GA TFM
src/BuildingBlocks/Persistence/BuildingBlocks.Persistence.csproj targets net10.0. .NET 10’s preview started in Feb 2025, with GA arriving later (Nov 2025), so a March-2025 build environment must have the matching .NET 10 preview SDK available for net10.0 or the build will fail.
Make CI/dev builds use the correct SDK (e.g., via a pinned global.json), or switch the TFM to a GA framework your supported build matrix already provides.
🤖 Prompt for 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.
In `@src/BuildingBlocks/Persistence/BuildingBlocks.Persistence.csproj` at line 13,
The project TFM is set to net10.0 in the TargetFramework element of
BuildingBlocks.Persistence.csproj which requires a .NET 10 SDK (preview) not
guaranteed in current CI/dev images; either pin the required preview SDK by
adding/updating a global.json that references the exact .NET 10 preview SDK
version used in your build matrix, or change the TargetFramework in
BuildingBlocks.Persistence.csproj from net10.0 to a GA TFM your CI already
supports (e.g., net8.0 or net7.0) and update any API usage accordingly so
local/CI builds no longer depend on an unavailable preview SDK.
…guro) Resolve os pontos válidos da review automatizada do PR: - RabbitMqEventPublisher: valida record.Type/Payload não-nulos antes de publicar - RabbitMqEventPublisher: DisposeAsync com finally aninhado garante que conexão e semáforo são liberados mesmo se o dispose do canal lançar (sem leak) - Documenta limitações conscientes como <remarks>: discriminador GetType().Name (frágil a rename; ok para records planos) e ausência de backoff/poison handling (attempts não incrementado; head-of-line blocking) — ambas fora do escopo da fase 1 Falso positivo descartado: net10.0 não é preview (GA nov/2025, SDK pinado, CI verde). Build verde; unit 4/4; integration exactly-once passou.
|
|
feat(messaging): Outbox dispatcher + Inbox + RabbitMQ publisher (fase 1)
Summary by CodeRabbit
New Features
Tests
Chores