Repository navigation
AOT: remove the Akka.Streams stream-ref reflection warnings - #8671
Merged
Aaronontheweb merged 3 commits intoSep 30, 2026
Merged
Conversation
Akka.Streams' StreamRefSerializer is always registered through the akkadotnet#8658 module table, so a Native AOT publish of any Akka.Hosting app hit three warnings regardless of whether the app ever exchanges a stream ref: IL2057 in SerializationTools.TypeFromString, and IL3050 in SinkRefImpl.Create and SourceRefImpl.Create. Guards all three sites behind the existing Akka.DynamicTypeLoading switch, matching the if (AkkaFeatures.IsDynamicTypeLoadingSupported) / NotBuiltIn pattern used elsewhere in core. With the switch on (the default), nothing changes - same Type.GetType/MakeGenericType path, same wire format. With it off, deserializing a stream ref now throws a clear SerializationException naming the element type and the switch, instead of leaving trimmed-but-still-reachable reflection behind. The element type genuinely drives Sink<T, NotUsed>/Source<T, NotUsed> and the (T)payload cast in the stage logic, so materializing as object was not an option - the guard is the honest fix here, not a rewrite. Adds tests for the switch-off failure and confirms the existing StreamRefsSpec suite still passes. Verified with a throwaway AOT publish (Akka.Streams rooted via TrimmerRootAssembly): 18 warnings before, 15 after, with exactly these three gone and nothing else shifted.
5 tasks
- Replace AkkaFeatures.NotBuiltIn with a dedicated message for the stream-ref guard: that helper is worded for a HOCON setting with a built-in fallback table, and reads oddly here since there is neither. SerializationTools.StreamRefTypeNotSupported is the single source of truth for the three call sites. - Consolidate the three switch-off unit tests into one end-to-end test through the real StreamRefSerializer: ToBinary on a SourceRefImpl<int> succeeds with the switch off (sending needs only typeof(T)), and the resulting FromBinary throws SerializationException (receiving needs Type.GetType). Drops the now-unused WithDynamicTypeLoadingOff helper. - Halve the BREAKING_CHANGES_V1.6.md row, link PR akkadotnet#8671 in the PR column like the other rows, note that only receiving a stream ref fails (sending and in-process use still work), that a JIT app using the switch as a strict mode loses this too, and drop the inline "Closes akkadotnet#8667" - that belongs in the PR description, not the ledger. - File akkadotnet#8673 (opt-in stream-ref element-type registry) as the tracked follow-up for an application that needs both the switch off and stream refs over the wire, and link it from the ledger migration text.
7 tasks
# Conflicts: # BREAKING_CHANGES_V1.6.md
Aaronontheweb
commented
Sep 30, 2026
| if (AkkaFeatures.IsDynamicTypeLoadingSupported) | ||
| return CreateGeneric(eventType, initialPartnerRef); | ||
|
|
||
| throw new SerializationException(SerializationTools.StreamRefTypeNotSupported(eventType.FullName ?? eventType.Name)); |
Member
Author
There was a problem hiding this comment.
reflection-based serialization won't work under AOT
| } | ||
|
|
||
| [Fact(DisplayName = "StreamRefSerializer should serialize a SourceRef but fail to deserialize it When dynamic type loading is off")] | ||
| public void Should_serialize_but_not_deserialize_a_SourceRef_When_dynamic_type_loading_is_disabled() |
Aaronontheweb
added a commit
to Aaronontheweb/akka.net
that referenced
this pull request
Sep 30, 2026
…he stream-ref warnings
Aaronontheweb
added a commit
that referenced
this pull request
Sep 30, 2026
* AOT: Akka.Hosting canary app and CI job Promotes the Akka.Hosting Native AOT prototype (spike/aot-hosting-canary) to a real canary alongside the plain-core one: - src/aot/Akka.Hosting.AOT.App: boots through Host.CreateApplicationBuilder + AddAkka(...), exercising AddHocon, a custom WithExtension<T>, WithActorSystemLivenessCheck, ConfigureLoggers' AddLoggerFactory, registry/DI-constructed actors, Tell, and DeathWatch. Self-terminates after its assertions pass instead of waiting on an external SIGINT/SIGTERM, so CI gets a deterministic exit. Added to Akka.slnx next to Akka.AOT.App. - scripts/CheckAotWarnings.cs now takes --scope (comma-separated prefixes, default src/core/Akka/), so the same checker gates the Hosting canary's own baseline (src/aot/Akka.Hosting.AOT.App/aot-warnings.baseline.txt) against src/contrib/hosting/, src/contrib/dependencyinjection/, and src/core/Akka.Streams/ - the last because Hosting registers the stream-ref serializer at startup and nothing else watches that surface. src/core/Akka/ itself is left out; the plain-core canary's baseline already covers it. Three known Streams warnings (stream-ref MakeGenericType, SerializationTools.TypeFromString) are baselined with a pointer to #8667. - build-system/pr-validation.yaml: new HostingAotCanary job, Linux, blocking. One unrooted publish serves both the run and the warning check - the three baselined Streams warnings already surface on that real boot, so a second rooted publish would not currently add coverage. Verified locally: unrooted publish is clean from Hosting/DI and matches the expected 4 core + 3 Streams warnings; the app exits 0 and prints '[canary-hosting] OK'; the warning check passes against the new baseline; a temporary Type.GetType probe added to src/contrib/hosting/Akka.Hosting/AkkaHostingExtensions.cs (reverted) proved the check fails on a new warning in scope. * Hosting AOT canary: assertions that can fail, fewer lines Review findings on the Hosting canary: - The extension check always passed: WithExtension<T,TI>() resolves-or-creates, so marking it proved nothing. Require HasExtension<CanaryExtension>() before that call, which only reads. - The health check accepted a report with zero entries. Require the 'akka.actorsystem' key WithActorSystemLivenessCheck() actually registers. - Comments describing WithExtension<T>() as an akka.extensions/Type.GetType round-trip were stale since #8649, which moved it to ExtensionsSetup. Corrected in CanaryExtension.cs, Program.cs, and the README. - WatchdogLoggerProvider now also accepts Information and waits for a dedicated marker line before the final ThrowIfAnyProblems, closing a race and independently proving Akka's logs reach Microsoft.Extensions.Logging - if the marker never arrives, the run fails. - Dropped the CI self-test step and its fixture for this job: it only re-exercises the checker's "measured nothing" branch, which the core canary already covers. - Dropped the unused RootAkka ItemGroup - this canary stays unrooted-only - and the now-redundant Akka/Akka.DependencyInjection ProjectReferences (both come in transitively through Akka.Hosting). - Added a 300s timeout to the CI run step. Cuts: removed NotifyActor/NotificationSink and AssertTellAsync (every Ask reply is already a Tell), AddHocon/AssertHocon, and the redundant TrimmerSingleWarn CLI flag (set once in the csproj). Shortened the README, YAML comments, csproj comments, and PrintFailure. Made AssertExtensionAsync synchronous now that it has no await. * AOT canaries: run the Hosting canary as steps in the existing AotCanary job * Hosting AOT canary: empty baseline now that #8671 removed the stream-ref warnings
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.
Summary
A Native AOT publish of any Akka.Hosting app hits three warnings from Akka.Streams, because Hosting references Akka.Streams and
StreamRefSerializer(id 30) is always registered through theStreamsSerializersmodule table added in #8658 - regardless of whether the app ever exchanges a stream ref:IL3050inSinkRefImpl.Create(MakeGenericType)IL3050inSourceRefImpl.Create(MakeGenericType)IL2057inSerializationTools.TypeFromString(Type.GetType)The element type genuinely drives the closed generic -
Sink<T, NotUsed>/Source<T, NotUsed>and the(T)payloadcast inside the stage logic both depend on it - so materializing asobjectwould change behavior and was not an option. Instead this guards all three sites behind the existingAkka.DynamicTypeLoadingfeature switch, using the sameif (AkkaFeatures.IsDynamicTypeLoadingSupported) { ... } else { throw ... }shape used elsewhere in core, with one dedicated message instead ofAkkaFeatures.NotBuiltIn(that helper is worded for a HOCON setting with a built-in fallback table; there's no HOCON setting or built-in table here, so it read oddly):Type.GetType/MakeGenericTypepath, same wire format.SinkRef/SourceRef, alone or enclosed in a message) throwsSerializationException. Sending one, and using one in-process, are unaffected -ToBinaryonly needstypeof(T), which the trimmer can always see.Closes #8667.
Records the switch-off behavior change in the Breaking changes section below (there was no guard at all before this, so switch-off previously behaved the same as switch-on for stream refs; the shared
BREAKING_CHANGES_V1.6.mdledger is updated separately in a batch PR), and files #8673 to track an opt-in element-type registry for applications that need to both disable the switch and exchange stream refs remotely.Note on #8668: that PR is still open. Its Hosting canary baseline currently lists only these same 3 warnings, so once this PR merges and those warnings stop firing, #8668's baseline goes stale and
CheckAotWarningshits its "measured nothing" branch (exit 1) against an unrebased baseline. The plan is for this PR to merge first, then #8668 rebases on top of it and ships with a header-only baseline. This PR does not touch any #8668 files.Test plan
dotnet build src/core/Akka.Streams -c Release- clean builddotnet build src/core/Akka.Streams.Tests -c Release -warnaserror- clean, zero warningsdotnet test src/core/Akka.Streams.Tests -c Release -f net10.0 --filter "FullyQualifiedName~StreamRef|FullyQualifiedName~StreamsSerializersSpec"- 24 passed, 1 pre-existing skip (unrelated), 0 failed. Includes one new end-to-end test:StreamRefSerializer.ToBinaryon aSourceRefImpl<int>succeeds with the switch off, andFromBinaryon the resulting bytes throwsSerializationException.dotnet format --verify-no-changeson the touched files - clean (the pre-existing whitespace issues inSourceRefImpl.cspredate this change and are untouched by it)TrimmerRootAssembly=Akka.Streams,Akka.DynamicTypeLoading=false,dotnet publish -r linux-x64, not committed): 18 warnings before this change, 15 after, with exactly these three gone and nothing else shiftedBreaking changes
Akka.Streams(serialization / stream refs)SinkRefImpl.Create,SourceRefImpl.Create,SerializationTools.TypeFromString) behindAkka.DynamicTypeLoading, removing three IL2057/IL3050 warnings every Akka.Hosting app hit under Native AOT. Switch on (default): unchanged. Switch off -- including a JIT app using it as a strict mode -- only receiving a stream ref now throwsSerializationException; sending one, and in-process use, still work.