Merge master into otel - #7956
Merged
Merged
Conversation
* Trimming warning approval * Minimal * Binglog * Remove verbosity * Simplify * Cancellation * Fix a regex to not be so greedy, so that messages with array types don't get cut off New regex uses [^\]] instead of . for characters inside the path to make sure it doesn't escape the bounds of one [PATH] block, and adds a [/\\] in the middle, so that essentially a path has to have the following between [ and ]: 1. One or more not ] characters 2. At least one path separator char, with both / and \ as options to account for platform 3. One or more not ] characters * Update src/NServiceBus.Core.Tests/API/TrimmabilityWarnings.cs Co-authored-by: David Boike <david.boike@gmail.com> --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com> Co-authored-by: David Boike <david.boike@gmail.com>
…tively exercise the MS DI seam which doesn't make any sense anymore. (#7801) Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com>
* Pass predictably shaped state objects to underlying logging providers * Add test to verify structured state preservation in slot scope logging --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com>
Co-authored-by: dependencyupdates[bot] <218638057+dependencyupdates[bot]@users.noreply.github.com>
…rloads are called from interface Handle (#7807) * Fix NSB0033 false positive for Handle overloads with a non-message first parameter A public method named `Handle` whose second parameter is `IMessageHandlerContext` and that returns `Task` was classified as a convention-based handler regardless of its first parameter type. A helper overload such as `Handle(string text, IMessageHandlerContext context, ...)` therefore looked like a convention-based handler for messages of type `string`, and on a class that also implements `IHandleMessages<T>` this raised NSB0033 (mixed interface/convention style). `IsValidConventionBasedHandleMethod` now requires the first parameter to be a plausible message type: it rejects framework types (any non-`None` SpecialType such as `string`, primitives, `object`), value types, and types in the `System` namespace. NServiceBus messages are user-defined reference types, so none of these can be a message. This is complementary to #7770, which only excluded methods implementing a handler-derived interface member; a standalone helper overload implements no interface and slipped through. * Enhance convention-based handler detection by incorporating compilation context * More efficient * It is fine to use the symbols directly since we are not caching them --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com>
Co-authored-by: dependencyupdates[bot] <218638057+dependencyupdates[bot]@users.noreply.github.com>
Co-authored-by: dependencyupdates[bot] <218638057+dependencyupdates[bot]@users.noreply.github.com>
…sposed without StopAsync (#7806) * DisposeAsync should pass a bounded cancellation token to transport Shutdown * . * Update RunningEndpointInstance.cs * . * , * Update StartableEndpoint.cs * Update RunningEndpointInstance.cs * Remove shutdown token timeout since it would create akward split between dispose and shutdown. Instead cancel the token immediately on dispose. * Core acceptance test * Ignore exceptions during DisposeAsync to prevent throwing from disposal process --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com>
Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com>
* Enhance ContextBag with inline storage and retrieval optimizations - Implemented inline storage for up to 8 entries to improve performance. - Added methods for setting, getting, and removing inline entries. - Updated merge logic to handle inline entries and stash more efficiently. - Introduced tests to validate inline entry functionality and behavior. * Update struct conventions tests to reflect immutability rules for ContextBag+Slot * Length constant * Comments Co-authored-by: David Boike <david.boike@gmail.com> * Length constant for inline array --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com> Co-authored-by: David Boike <david.boike@gmail.com>
#7821) * Trimming safe mapping by using guarding against IsDynamicCodeSupported Also adds trimming annotations up to the entry points for this path * Move the defaults to the other defaults * Enhance TrimmingSafeMessageMapper to throw NotSupportedException for interface and abstract type mappings; add tests for concrete event publishing with trimming-safe mapper * Fix usings * Proper xml doc * Approve new test for downstreams * Small twist to the doc --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com>
Co-authored-by: dependencyupdates[bot] <218638057+dependencyupdates[bot]@users.noreply.github.com>
* Taking a defensive copy of the contexts in the transport tests to enable pooling * Copy headers in acceptance tests --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com>
…ning Saga Persister (#7829) * Make SystemJson serializer AOT and trimming friendly Apply the same pattern used in NServiceBus.Persistence.NonDurable's SagaEntry: resolve JsonTypeInfo via TypeInfoResolver and use the typed overloads when available, falling back to reflection-based serialization only when JsonSerializer.IsReflectionEnabledByDefault is true. Suppress trimming/AOT warnings on the reflection fallback methods. * Approve removal of SystemJson serializer trimming warnings The JsonMessageSerializer trimming warnings (IL2026 for both Serialize and Deserialize) are no longer present after making the serializer AOT and trimming friendly. * Add SerializerOptions property to SagaManifest * Pass SerializerOptions through SagaManifestCollection * Make Learning Saga Persister AOT and trimming friendly Apply the same pattern used in the SystemJson serializer: resolve JsonTypeInfo via TypeInfoResolver and use the typed overloads when available, falling back to reflection-based serialization only when JsonSerializer.IsReflectionEnabledByDefault is true. Suppress trimming/AOT warnings on the reflection fallback methods. * Add SagaSerializerOptions extension method for Learning Saga Persister Allows users to configure custom JsonSerializerOptions for the learning saga persister via the persistence configuration API. * Approve removal of Learning Saga Persister trimming warnings and new public API The SagaStorageFile trimming warnings (IL2026 for both SerializeAsync and DeserializeAsync) are no longer present. The new SagaSerializerOptions extension method is added to the public API. * Guard JsonStringEnumConverter behind IsReflectionEnabledByDefault Only include the converter in non-AOT mode. Suppress the IL3050 AOT warning on the factory method since the converter is only created when reflection serialization is available. * Extract ResolveTypeInfo into reusable extension method Consolidates the duplicated ResolveTypeInfo logic from both JsonMessageSerializer and SagaStorageFile into a single extension method on JsonSerializerOptions? in the Serializers folder. * Pass default options for persistence tests * Nullable options * Fix flaky test that fails in Debug mode * Fix warning * Update src/NServiceBus.PersistenceTests/PersistenceTestsConfiguration.cs Co-authored-by: Andreas Öhlund <andreas.ohlund@particular.net> --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com> Co-authored-by: Andreas Öhlund <andreas.ohlund@particular.net>
Co-authored-by: dependencyupdates[bot] <218638057+dependencyupdates[bot]@users.noreply.github.com>
…oints (#7833) * Cleanup usings * Add acceptance test to reproduce the most complex scenario * Properly fallback to global keyed services where necessary and update test cases * Use "is" * Refactor service lookup methods to use ConcurrentDictionary for improved performance --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com>
* Update actions/setup-dotnet action to v5.4.0 * Change invalid global.json sdk version --------- Co-authored-by: dependencyupdates[bot] <218638057+dependencyupdates[bot]@users.noreply.github.com> Co-authored-by: David Boike <david.boike@gmail.com>
* Update dependency Particular.Licensing.Sources to 7.2.0 * Update for nullability and upgrade protection removal --------- Co-authored-by: dependencyupdates[bot] <218638057+dependencyupdates[bot]@users.noreply.github.com> Co-authored-by: Brandon Ording <bording@gmail.com>
Co-authored-by: dependencyupdates[bot] <218638057+dependencyupdates[bot]@users.noreply.github.com>
Co-authored-by: dependencyupdates[bot] <218638057+dependencyupdates[bot]@users.noreply.github.com>
* Introduce support for nullable reference types #6257 * Fix spacing --------- Co-authored-by: Brandon Ording <bording@gmail.com>
* enable , Nullable in the remaining file in Core Hosting folder
* fix empty space
* fix empty space
* fall back to a default value ("Unknown assembly") instead of propagating null
* Formatting
* Make nullable enable spacing consistent in Hosting folder
---------
Co-authored-by: Brandon Ording <bording@gmail.com>
* add #nullable enable to missed file * Make nullable enable spacing consistent in the Logging folder --------- Co-authored-by: Brandon Ording <bording@gmail.com>
* support for nullable reference type * add support for nullable reference types in PopulateRecoverabilityTraceMetadataBehaviorTests
* add support for nullable types * add nullable support to test file * remove extra line
Co-authored-by: dependencyupdates[bot] <218638057+dependencyupdates[bot]@users.noreply.github.com>
Deleting this file, as the guidelines are really out of date and they are not complete enough to be helpful.
Co-authored-by: dependencyupdates[bot] <218638057+dependencyupdates[bot]@users.noreply.github.com>
Co-authored-by: dependencyupdates[bot] <218638057+dependencyupdates[bot]@users.noreply.github.com>
Several files had the #nullable enable annotation on line 1 but skipped the blank line before the namespace declaration that the rest of the codebase uses. Fixing them separately from the new directory-completion approval tests that depend on this convention.
* Address unsafe context in Pooled Buffer Writers * Switch NonCryptoHash to span based only to avoid unsafe propagation * Bound safety in SourceWriter to be contained in the safe boundaries * Safety comments on Unsafe.As (they don't flag yet in v11 preview 7) --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com>
* Add characterization tests for message metadata and serializer behavior * Add cache-only message metadata lookup and reflection-free logical message creation * Add AddMessageType with interceptor and trimming-safe registration * Add executable scanner-disabled trimmed endpoint validation and fix pipeline behavior trimming * Pass generated correlation accessor to SagaMapper, emit finder-only sagas, and gate interceptor suppressions * Key generated saga correlation accessors by saga-data type and exercise them at runtime * Share message hierarchy ordering between handler and AddMessageType generation * Fix saga trim annotations and exercise saga scenarios in the trimmed endpoint sample * Verify build-transitive package consumption of trimming-safe registrations * Add executable NativeAOT validation for scanner-disabled endpoint * Activate strict registered-only message metadata mode for disabled scanning in trimmed endpoints * Assert strict registered-only metadata mode in the trimmed endpoint sample * Verify EnableTrimAnalyzer generates interceptors without activating strict runtime mode * Verify PublishAot alone emits the runtime strict-mode signal * Cleanup trimming program * Modernize slightly * Remove attribute approach and replace with target * Reuse existing configs * Remove overly excessive test because it is too internals focused * Remove Directory.Build.props changes * Tweaks to TrimmedEndpoint naming / location * Import the files instead * Rename target * Clean up usings * Formatting * Clarify conventions * Extend trimming tests * Restoring the trimming warnings from before * Refactor strict missing metadata exception handling * Wording suggestion Co-authored-by: David Boike <david.boike@gmail.com> * Add nullable reference type support to GetType method * Improve message --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com> Co-authored-by: Brandon Ording <bording@gmail.com> Co-authored-by: David Boike <david.boike@gmail.com>
* Reproduce rolling logger TOCTU race that already existed before but becomes now apparent with the slot infrastructure * Fix the TOCTU on the rolling file appender * Address review comment * Failing tests for review comment * Fix * Strengthen filename retry regression test Co-authored-by: danielmarbach <174258+danielmarbach@users.noreply.github.com> --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: danielmarbach <174258+danielmarbach@users.noreply.github.com>
* Make logical message mutators trimming-aware with declared message types Add typed replacement APIs so incoming and outgoing instance mutators can declare the logical message type instead of relying on runtime type discovery: - IIncomingLogicalMessageContext gains UpdateMessageInstance<T>(T) and UpdateMessageInstance(object, Type) following the typed UpdateMessage precedent on IOutgoingLogicalMessageContext, with PreObsolete and RequiresUnreferencedCode on the runtime-type-routing object overload - IncomingLogicalMessageContext resolves replacement metadata through LogicalMessageFactory without reflection over the instance, preserving strict registered-only metadata mode - MutateIncomingMessageContext.UpdateMessageInstance and MutateOutgoingMessageContext.UpdateMessage accept typed or explicitly typed replacements and carry the declared type via a DAM-annotated internal property - Mutator behaviors pass the declared type into the pipeline contexts, keeping the legacy object path untouched - TestableIncomingLogicalMessageContext mirrors the typed members * Extend message migration analyzer to mutator context setters The legacy mutator pattern assigns an object to the Message or OutgoingMessage property of a mutator context, which routes by the runtime type. Analyze SimpleAssignment operations on those two setters and reuse the existing NSB0039/NSB0040 classification (direct creation and value types are provably safe; everything else is a runtime-type routing warning), gated by the same trimming/AOT build properties. The code fixer rewrites a safe assignment to the typed replacement API: - context.Message = new MyMessage() -> context.UpdateMessageInstance<MyMessage>(new MyMessage()) - context.OutgoingMessage = new MyEvent() -> context.UpdateMessage<MyEvent>(new MyEvent()) * Add incoming UpdateMessageInstance coverage to the migration analyzer * Simplify * Deprecate mutator context setters in favor of typed replacement APIs The Message and OutgoingMessage setters on the logical mutator contexts route by the runtime type, which the trimmer cannot analyze. The typed UpdateMessage/UpdateMessageInstance APIs added in this branch are the replacement, so the setters now warn on assignment using the Particular.Obsoletes authoring pair (error from 11, removed in 12). The setter-level attribute leaves the getter silent, verified on the current compiler. The typed methods assign the backing field directly to avoid self-triggered warnings, and deliberate legacy-path test coverage is pragma-suppressed until the setters are removed with the object-overload batch tracked in #7906. * Simplify interface method * Suppress mutator warnings because code path only reachable if using other RequiresUnreferencedCode is used (#7928) * Fix property name in outgoing mutator suppression justification --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com> Co-authored-by: David Boike <david.boike@gmail.com>
Co-authored-by: dependencyupdates[bot] <218638057+dependencyupdates[bot]@users.noreply.github.com>
Co-authored-by: dependencyupdates[bot] <218638057+dependencyupdates[bot]@users.noreply.github.com>
Co-authored-by: dependencyupdates[bot] <218638057+dependencyupdates[bot]@users.noreply.github.com>
… limit as -0.001s (#7937) When Run is handed a cancellable token the Done limit is Timeout.InfiniteTimeSpan, and the timeout message rendered that as "The maximum time limit for this test(-0.001s) has been reached".
* Update must reset the ReplacementType * Effective dynamic loading diagnostics * Add mutator coverage * Simplify registration to facilitate source gen registration under the hood * All public APIs that use those sources are properly annotated so we can suppress it unconditionally. * Enable trimming analyzers for now * Mark as trimmable * XmlSerialization subsystem honest annotations and improved message. Also moved the delegate factory because it should only be used there and no longer be a util. * Remove unnecessary make generic type * Refactor JSON serialization logic and update suppression attributes for trimming * Suppress FileVersionRetriever because the code handles graceful fallback * MessageHandlerRegistry path invokes generic methods and therefore also requires dynamic code. The source generated path doesn't require that so suppress * KeyedServiceProviderAdapter suppressions * Grammar fix * Better explanation (hopefully) * Reword comment on provider adapter * IsAotCompatible * Fix approvals --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com>
Co-authored-by: dependencyupdates[bot] <218638057+dependencyupdates[bot]@users.noreply.github.com>
Co-authored-by: dependencyupdates[bot] <218638057+dependencyupdates[bot]@users.noreply.github.com>
) Calling ToString() on an enum value boxes it, so every outgoing message allocated 24 bytes and paid roughly 12ns to produce a string that is known at compile time. nameof yields the identical string for free. MessageIntent.Send.ToString() 24.0 B/op 11.97 ns/op nameof(MessageIntent.Send) 0.0 B/op 1.45 ns/op ReplyConnector already did this; this brings the other four sites into line. The enum has no attributes that affect ToString and no duplicate values, so the header value on the wire is unchanged: both forms produce "Send" and "Publish". This is a small change in proportion - 24 bytes against roughly 5.4 KB for a full outgoing pipeline run, about 0.4% - so it is worth doing for being free and consistent rather than for moving the needle.
…7945) ToWireFormattedString went through ToString("yyyy-MM-dd HH:mm:ss:ffffff Z"). A custom format string is reparsed on every call - DateTimeFormat.FormatCustomized walks it character by character calling ParseRepeatPattern - and that parsing dominates the cost. The layout here is fixed and ASCII, so the digits can be written straight into the string buffer instead. This follows what the framework does for its own standard date formats. DateTimeFormat .TryFormatO writes into fixed offsets of the destination span and takes each two digit field from a lookup table rather than dividing per digit; the same two techniques are used here. ToString(format) 80.0 B/op 111.06 ns/op string.Create 80.0 B/op 27.37 ns/op Roughly four times faster. The 80 bytes is the resulting string itself and is unchanged, so this is a CPU saving rather than an allocation one. For reference, an earlier revision of this change divided per digit instead of using the lookup table and measured 30.95 ns/op, so the table is worth about 12% on top - a small margin, kept because it matches how the framework formats dates and costs only a static table. ToDateTimeOffset directly below already parses by hand for the same reason, so this makes the two directions consistent. Verified byte identical to the previous implementation over 3,000,012 values: a random sweep of the whole representable tick range crossed with offsets from -14:00 to +14:00, plus DateTimeOffset.MinValue and MaxValue, the Unix epoch, a leap day, an offset that rolls the date across a boundary, a three digit year, and sub-microsecond ticks either side of the truncation boundary. Zero mismatches. Also round tripped 200,000 values back through ToDateTimeOffset with zero failures. This runs at least once per outgoing message for the TimeSent header, and again for DeliverAt on delayed sends.
…#7943) * Skip outbox work in the receive pipeline when no outbox is configured With no outbox configured the storage is a no-op, yet the receive connector still did all the outbox work on every message: it built an OutboxMessage and a TransportOperation[] for NoOpOutboxStorage.Store to discard, opened and committed a no-op transaction, and parked a placeholder NoOpOutboxTransaction in the context purely so the storage session could read it back. It also snapshotted PendingTransportOperations.Operations twice, and each read copies a ConcurrentStack. CompletableSynchronizedStorageSessionExtensions.Open now substitutes the no-op transaction when none is present, rather than requiring one via a hard Get<IOutboxTransaction>(). Persisters already received exactly that instance when no outbox was configured, so the input to their TryOpen is unchanged. That frees the connector to skip the transaction entirely on the no-outbox path. Invoke is now a non-async dispatcher over InvokeWithOutbox and InvokeWithoutOutbox, so it has no state machine of its own and exactly one is boxed per message, sized for the path actually taken. Without an outbox, Get always returns null and SetAsDispatched does nothing, so the deduplication branch and both calls are unreachable and are gone from that path. The boxed state machine drops from 200 B to 104 B per message; the outbox path stays at 208 B. Measured on TransportReceiveToPhysicalMessageConnector.Invoke driving one incoming message, against master, back to back on the same machine (net10.0, i5-12600, BenchmarkDotNet ShortRun). Allocation is deterministic and reproduces exactly; the timings carry a few percent of run-to-run variance. outgoing outbox master this change 0 off 223.0 ns / 1.35 KB 204.6 ns / 1.30 KB 1 off 386.9 ns / 2.19 KB 322.4 ns / 1.95 KB 2 off 428.9 ns / 2.61 KB 344.4 ns / 2.30 KB 0 on 234.3 ns / 1.45 KB 228.1 ns / 1.45 KB 1 on 368.3 ns / 2.28 KB 356.0 ns / 2.16 KB 2 on 434.2 ns / 2.70 KB 410.3 ns / 2.58 KB The absolute figures include the harness's own per-iteration allocations, so only the deltas are meaningful. No configuration regresses; the outbox path also gains from the single Operations snapshot. Note that IOutboxTransaction is no longer present in the context when the outbox is disabled. Code reading it directly via Get<IOutboxTransaction>() would now throw rather than receive the no-op instance. * Extract shared dispatch tail into single DispatchOperations helper Co-authored-by: danielmarbach <174258+danielmarbach@users.noreply.github.com> * Avoid extra state machine: share only synchronous dispatch instrumentation Co-authored-by: danielmarbach <174258+danielmarbach@users.noreply.github.com> * Use target type where it is obvious * Stop unnecessary wrapping * Switch to built-in logger --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com> Co-authored-by: danielmarbach <174258+danielmarbach@users.noreply.github.com>
* Add engineering context map and public context index AGENTS.md is the workflow for finding and preserving design rationale and is identical across repositories; docs/README.md describes this repository, indexes the public sources, and lists the pull requests that record decisions; docs/decisions/README.md holds ADR guidance. * Address review: generic component wording, drop a fix from the decision list --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com>
Resolve conflicts between the OpenTelemetry work and the master changes: - TransportReceiveToPhysicalMessageConnector: keep the outbox-skip paths from master and the outbox fetch/store/duplicate metrics from otel. The dispatching events now respect EmitMessageDispatchingEvents again. - ReceiveComponent: pass PipelineMetrics, InstrumentationOptions and the ILogger to the connector. - RetryAcknowledgementBehavior: keep the trace context propagation on the acknowledgement message. - Test package versions from master, OpenTelemetry package from otel. - Re-approve the public API and the LogManager usage list (ActivityFactory still uses LogManager).
The inlined event gating in the with-outbox path had an inverted condition and duplicated the fork block. Both paths now call Dispatch, which forks directly when the dispatching events are off and goes through DispatchWithEvents when they are on. Removes the unused WriteStartDispatchingEvent helper.
The factory is created before the container exists, so it cannot take an ILogger in its constructor. RecordError now receives the behavior context's service provider instead of the unused ContextBag and resolves ILogger<ActivityFactory> on first use. The message is emitted through a LoggerMessage method. Removes the last LogManager usage added by the otel branch.
irinascurtu
approved these changes
Oct 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merges
master(72af5c4) intootel. This keeps theotelbranch current with 76 commits frommasterso that #7798 can merge without a large conflict set.Conflict resolution
TransportReceiveToPhysicalMessageConnector: keep the outbox-skip paths frommasterand the outbox fetch, store and duplicate metrics fromotel. The dispatching events respectEmitMessageDispatchingEventson both paths.ReceiveComponent: passPipelineMetrics,InstrumentationOptionsand theILoggerto the connector.RetryAcknowledgementBehavior: keep the trace context propagation on the acknowledgement message. Keep the fork afternextfrommaster.master,OpenTelemetrypackage fromotel.LogManagerusage list.Follow-up
ActivityFactorystill usesLogManager. It is now on the approved usage list.otelinto the stacked branches.