Repository navigation
AOT: ExtensionsSetup and a first-party akka.extensions table - #8648
Merged
Merged
Conversation
This was referenced Sep 25, 2026
Aaronontheweb
added this pull request to stack #8662
September 29, 2026 13:53
Aaronontheweb
force-pushed
the
feature/aot-ext-1-extensions-setup
branch
2 times, most recently
from
September 29, 2026 18:02
ae47923 to
4b0a478
Compare
Member
Author
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
Adds ExtensionsSetup, which registers extensions by id instead of by type name in akka.extensions. LoadExtensions reads it at the same point in startup as the HOCON list; an extension named in both registers once. akka.extensions entries naming DistributedData, DistributedPubSub, ClusterClientReceptionist or ClusterMetrics now resolve from a table of literal type names, so the trimmer can keep those providers. With Akka.DynamicTypeLoading on (the default) behavior is unchanged: the table resolves the same type Type.GetType would, and an assembly that is absent or will not load falls through to the existing reflection path.
…tests moved to Sharding.Tests - ExtensionsSetup exposes a read-only wrapper, rejects null ids, and documents that a second ExtensionsSetup replaces the first. - First-party table members are #nullable enable and return IExtensionId?. - DData and Cluster.Tools parity rows move to Akka.Cluster.Sharding.Tests, which already sees core internals; drop the Akka.DistributedData.Tests InternalsVisibleTo. Parity asserts against Type.GetType on the raw name. - ExtensionsSetupSpec derives from AkkaSpec, and the dedupe test proves the Setup's id wins over the HOCON one. - Update the LoadExtensions entry's comment in the AOT warning baseline.
Aaronontheweb
force-pushed
the
feature/aot-ext-1-extensions-setup
branch
from
September 30, 2026 00:47
4b0a478 to
4f23e84
Compare
Aaronontheweb
commented
Sep 30, 2026
| #nullable enable | ||
| // Akka's own extensions that akka.extensions commonly names: bare type name -> the assembly it must name, and a | ||
| // factory passing its own literal so the trimmer keeps that type. Nothing is probed unless a row matches. | ||
| private static readonly Dictionary<string, (string Assembly, Func<IExtensionId?> Create)> FirstPartyExtensions = |
| { | ||
| var extensions = new List<IExtensionId>(); | ||
| // Setup ids go first; one also named in HOCON registers once, since RegisterExtension keys by type | ||
| var extensions = new List<IExtensionId>(_settings.Setup.Get<ExtensionsSetup>() |
| .And(ExtensionsSetup.Create(extensionIds)); | ||
|
|
||
| [Fact(DisplayName = "An extension in ExtensionsSetup is registered when the ActorSystem starts")] | ||
| public async Task Should_register_extension_When_named_in_Setup() |
This was referenced Sep 30, 2026
Aaronontheweb
added a commit
to Aaronontheweb/akka.net
that referenced
this pull request
Sep 30, 2026
…t-party extensions as not-built-in Two review fixes on top of the extensions/provider switch-off guard: 1. ProviderSelection.GetProvider matched akka.actor.provider by exact string, so a spelling variant of a built-in provider - no space after the comma (e.g. "Akka.Cluster.ClusterActorRefProvider,Akka.Cluster"), or a versioned assembly-qualified name - fell through to Custom. With the switch off that meant a working built-in provider threw NotBuiltIn. GetProvider now normalizes through TypeExtensions.TrySplitTypeName (the akkadotnet#8613 rule: strip the assembly identity, split at the comma, compare the assembly case-insensitively) against a small BuiltInProviders table, so every accepted spelling maps to the same ProviderSelection instead. This also fixes Settings.HasCluster being wrongly false for such a spelling, independent of the switch. 2. LoadExtensions treated a first-party extensions-table name whose assembly isn't deployed (TryCreateFirstPartyExtension returns null either way) the same as a name that isn't in the table at all, so with the switch off it threw NotBuiltIn for a name akkadotnet#8648 already promised to log-and-skip. A new IsFirstPartyExtensionName reports a table hit regardless of whether the assembly loads, so LoadExtensions can log-and-skip the absent-module case before reaching the switch-off guard - matching the switch-on behavior, which already fell through to the same log message. Tests added to AkkaFeaturesSpec (provider spelling classification and its end-to-end consequence in both switch states, plus the absent-assembly extension case) and ExtensionsSetupSpec (IsFirstPartyExtensionName). Updates the BREAKING_CHANGES_V1.6.md row to name both cases.
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.
Changes
First PR of a stack that lets extensions start without type names in HOCON, so Native AOT apps with
Akka.DynamicTypeLoadingoff can use them. Additive only; no behavior change with the switch on.ExtensionsSetup(new,Akka.Actor.Setup): holdsIExtensionIds, exposed as a read-only list.Createrejects null ids. Like any Setup, a second one passed toActorSystemSetup.Andreplaces the first.LoadExtensionsreads it at the same point in startup asakka.extensions. Setup ids go first, then the HOCON list; an extension named in both registers once, from the Setup's id, becauseRegisterExtensionkeys by extension type.First-party table in
ActorSystemImpl: anakka.extensionsentry naming one of these resolves from a literal type name instead ofType.GetTypeon the HOCON string:Akka.DistributedData.DistributedDataProvider, Akka.DistributedDataAkka.Cluster.Tools.PublishSubscribe.DistributedPubSubExtensionProvider, Akka.Cluster.ToolsAkka.Cluster.Tools.Client.ClusterClientReceptionistExtensionProvider, Akka.Cluster.ToolsAkka.Cluster.Metrics.ClusterMetricsExtensionProvider, Akka.Cluster.MetricsMatching is strict on type name plus assembly (assembly identity is ignored, assembly name case-insensitive, same as
Type.GetType). Each row passes its own literal to a helper whose parameter is[DynamicallyAccessedMembers(PublicParameterlessConstructor)], the AOT: resolve built-in actor ref providers from constant type names so the trimmer preserves them #8599 / AOT: core module-serializer table (#8626, part 1) #8645 pattern.Type.GetTypeis called only when a row matches. An assembly that is absent or won't load counts as absent and falls through to today's reflection path, which logs the same error as before.One
InternalsVisibleTo, forAkka.Cluster.Metrics.Tests, so it can check its table row against reflection. The other rows are checked fromAkka.Cluster.Sharding.Tests, which already had internals access.The AOT warning baseline's
LoadExtensionscomment now says the table exists and the entry stays until the reflection path is guarded.No switch-off throws here; that comes in a later PR.
A note on "failing" extensions
Today
akka.extensionslogs and skips only names it can't resolve or construct. IfCreateExtensionthrows,ActorSystem.Createfails. A Setup holds ids that already exist, so the only failure left isCreateExtension, and it behaves the same way on both paths. The spec checks that parity instead of a log-and-skip.Tests
Akka.Tests/Actor/Setup/ExtensionsSetupSpec(anAkkaSpec): Setup registers an extension; a throwing extension fails startup the same way via HOCON and via Setup; HOCON plus Setup registers the extension once, from the Setup's id (two id classes with separate counters); null ids are rejected; the table ignores names that aren't first-party (bare name, wrong assembly, unknown type) and names whose assembly is absent.Akka.Cluster.Sharding.Tests/FirstPartyExtensionSpec(DData and both Cluster.Tools rows, several spellings) andAkka.Cluster.Metrics.Tests/FirstPartyExtensionSpec: the table returns the same type asType.GetTypeon the raw name.DistributedPubSubConfigSpec,ClusterClientSerializerSpec,ClusterMetricsAutostartSpec.Checklist
dotnet build -c Release -warnaserrorfor Akka and the touched test projectsAkka.TestsActor filter: 571 passed, 12 skipped, 0 failed (Actor + Util before the review fixes: 712 passed, 12 skipped)Akka.API.Tests:ApproveCoreupdated only forExtensionsSetupand the MetricsInternalsVisibleToline[canary] OKwith the same 4 warnings; rootedCheckAotWarningsreports no new warnings (12/12 baseline)