Skip to content

Serialization: delete the built-in serializer rows from module HOCON (#8676, PR 4a) - #8711

Merged
Aaronontheweb merged 4 commits into
akkadotnet:devfrom
Aaronontheweb:feature/delete-builtin-serializer-hocon
Oct 2, 2026
Merged

Aaronontheweb merged 4 commits into
akkadotnet:devfrom
Aaronontheweb:feature/delete-builtin-serializer-hocon

Conversation

@Aaronontheweb

@Aaronontheweb Aaronontheweb commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

Purpose

PR 4a of #8676. Since #8702, each first-party module's serializer table registers its serializers and bindings as defaults when the module is deployed, and the serializer ids live in code (#8694). The matching rows in each module's shipped HOCON were duplicates. This PR deletes them. The module tables are now the only source of built-in serializer registration.

Next in this plan: composing config from deployed modules at startup (#8707), which replaces the planned reload guard.

What was deleted

Rows under akka.actor.serializers, akka.actor.serialization-bindings and akka.actor.serialization-identifiers:

Module (file) serializers bindings identifiers
Akka.Remote (Remote.conf) 7 30 (29 types; PoisonPill was listed twice) 7
Akka.Streams (reference.conf) 1 3 1
Akka.Cluster (Cluster.conf) 2 3 2
Akka.Cluster.Tools Client 1 2 1
Akka.Cluster.Tools PublishSubscribe 1 2 1
Akka.Cluster.Tools Singleton 1 1 1
Akka.Cluster.Sharding 1 1 1
Akka.DistributedData 2 2 2
Akka.Cluster.Metrics 1 5 1
Akka.Persistence (persistence.conf) 2 2 2
Total 19 51 19

Kept: every akka.actor.serialization-settings block (for example primitive { use-legacy-behavior = on } in Remote.conf), and core's own rows in akka.conf (bytes, json, the System.Byte[] and System.Object bindings). Hyperion and other non-table serializers are untouched.

What read the deleted rows

Product code: nothing read them. SerializerIdentifierHelper still reads serialization-identifiers, but only as the fallback for a serializer that does not declare its own Identifier (a custom serializer, or a subclass of a built-in one). Every built-in serializer declares its id in code.

Readers I changed:

  • ReplicatorSettingsSpec (Akka.DistributedData.Tests) asserted the DistributedData rows through Settings.Config. It now asks Serialization (GetSerializerById, FindSerializerForType).
  • BuiltInSerializerIdentifierSpec (Akka.API.Tests) compared every serializer's Identifier with the live serialization-identifiers rows. SerializerTableSpec replaces it (see below).
  • Three DData benchmarks (SerializerORSet, SerializerORDictionary, SerializerLwwDictionary) carried a private copy of the DistributedData rows. They now start a plain system.
  • Docs: serialization.md (new section, and the "final HOCON settings" example no longer lists Remote's ids), source-generated-serialization.md (Remote no longer registers Artery's serializer from Remote.conf), cluster-metrics.md (the sample config no longer shows the rows). XML comments in the module tables and ArteryControlMessageSerializer no longer point at Remote.conf.

Akka.Hosting does not read the rows and needed no change.

Tests

The guard is an approval test of the live tables, in one place: src/core/Akka.API.Tests, the repo's "approved contracts" project, which already references all eight module assemblies.

  • SerializerTableSpec.ApproveModuleSerializerTables builds every table in ModuleSerializerTable.Default against a real ActorSystem and verifies the result with Verify, using the same settings as CoreAPISpec. The snapshot is src/core/Akka.API.Tests/verify/SerializerTableSpec.ApproveModuleSerializerTables.DotNet.verified.txt (77 lines): per module, one line per alias with its serializer type and id, then one line per bound type, all sorted ordinally.
  • The first approved snapshot is exactly what 1.6.0-beta1 shipped in the module HOCON. I checked that two ways: the text equals a rendering of the old frozen rows (byte for byte, apart from the BOM Verify adds), and a separate script that parses the 1.6.0-beta1 tag's conf files gives the same 19 aliases, ids and 50 bound types.
  • To approve an intentional change, use the same Verify workflow as the public API: run dotnet test src/core/Akka.API.Tests, compare *.received.txt with *.verified.txt in verify/, and copy the received file over once the diff is what you meant. A new serializer or a binding moved at a cut-over (V2 rows, for example) then shows up in review as a snapshot diff.
  • The same class checks, over the live tables and core's own rows: aliases unique, serializer ids unique, every bound type bound once, every serializer type under one alias. It also starts a plain ActorSystem with every module deployed and no module rows, and checks each id and bound type resolves to the table's serializer with dynamic type loading on and off.
  • Each module's *SerializersSpec keeps its behavioural checks, with expected values taken from the live table: a plain system resolves every table entry (dynamic type loading on and off), the Akka.Hosting spelling resolves, a binding override wins with and without copied rows in the config, and a new alias override test (akka.actor.serializers.<alias> pointed at another serializer moves the alias's bound types).
  • Hosting: BuiltInSerializerSpecs in Akka.Cluster.Hosting.Tests starts a hosted app with remoting, clustering and pub-sub, checks the built-in serializers resolve with no rows in the config, and round-trips a message. A second test checks a hosted HOCON binding still wins.
  • ModuleSerializersSpec: a skewed module table (load-time, static-initializer and build-time failures) logs one error and startup succeeds; a module that is not deployed logs nothing.
  • Removed: the "table lists no type that the live conf does not" tests (they compared a table with HOCON that no longer has the rows), the hand-written frozen copy of the rows, and the reflection baselines built from it. The snapshot covers that.

Breaking changes

  • Built-in module serializers, bindings and identifiers no longer appear in ActorSystem.Settings.Config under akka.actor.serializers, akka.actor.serialization-bindings and akka.actor.serialization-identifiers. Code that read them to find a built-in serializer must use Serialization (for example FindSerializerForType, FindSerializerFor).
  • The HOCON rows no longer act as a fallback when a module's serializer table fails to load (version skew, such as Akka.Remote and Akka.dll at different versions). Before, the rows could still resolve those serializers by reflection. Now that case logs an error naming the module, and the module's serializers are not registered, so its messages fall back to other serializers. Startup still succeeds, and a module that is simply not deployed stays silent.
  • User HOCON can still override a built-in alias or binding, and SerializationSetup still wins over both. Rows an application copied from 1.5 still work. Wire format and serializer ids are unchanged.
  • Remote.conf and the other module configs shrink, so anything that diffed or rendered them sees fewer rows.

I did not touch BREAKING_CHANGES_V1.6.md.

Verification

  • dotnet build Akka.slnx -c Release -warnaserror: 0 warnings, 0 errors.
  • dotnet test src/core/Akka.API.Tests: 30 passed, no public API change.
  • --filter "FullyQualifiedName~Serializ" passes in Akka.Tests (154), Akka.Remote.Tests (185), Akka.Cluster.Tests (79), Akka.Persistence.Tests (24), Akka.Streams.Tests (12), Akka.Cluster.Tools.Tests (59), Akka.Cluster.Sharding.Tests (25), Akka.DistributedData.Tests (31) and Akka.Cluster.Metrics.Tests (9). Hosting BuiltInSerializerSpecs: 2 passed.
  • I renamed an alias and moved a binding in RemoteSerializers locally. The snapshot test failed with the expected diff both times, and I reverted both.
  • Earlier on this branch (first commit): full Akka.Remote.Tests 713 passed, 5 skipped; full Akka.Cluster.Tests 439 passed; all Hosting test projects pass; both AOT canaries print their OK line and scripts/CheckAotWarnings.cs reports no new warnings. The commits since touch tests, docs and the new version-skew error log only.

…kkadotnet#8676, PR 4a)

Each first-party module's serializer table now registers its serializers,
bindings and ids as defaults, so the matching rows in Remote.conf,
Cluster.conf, the Cluster.Tools confs, Sharding, DistributedData,
Cluster.Metrics, persistence.conf and Streams' reference.conf were
duplicates. Delete them (19 serializers, 51 bindings, 19 ids); keep the
serialization-settings blocks and core's own rows.

Table parity tests now compare against a frozen copy of the rows from the
1.6.0-beta1 tag, plus cross-module checks for unique aliases and ids and one
alias per bound type.
… version skew; docs fixes

With the HOCON rows gone, a module whose serializer table fails to load (for
example Akka.Remote and Akka.dll at different versions) no longer has a row to
fall back on, so its serializers vanished without a trace. Log an error naming
the module and the exception; startup still carries on, and a module that is
simply not deployed stays silent.

Docs: rewrite "Serializer Ids" (built-in ids are fixed in code, HOCON ids are
for custom serializers, a subclass reads its own row), use a user-owned
serializer in the source-generated registration example, fix the router
mapping note in cluster-metrics, and fix stale comments about module
reference.conf rows.
Both words were already in serialization.md; the docs edits moved them out of a span the
code-fence ignore pattern happened to cover.
…of a frozen copy of the rows

SerializerTableSpec (Akka.API.Tests) dumps every table in ModuleSerializerTable.Default
- one line per alias with its serializer type and id, one line per bound type,
sorted ordinally - and verifies it against an approved snapshot, the way the repo
approves the public API. The first snapshot is exactly what 1.6.0-beta1 shipped.
Cross-module uniqueness checks (aliases, ids, one alias per bound type, one alias
per serializer type) run over the live tables and core's rows in the same class.

Delete FrozenSerializerRows and BuiltInSerializerIdentifierSpec. The per-module
specs keep their behavioural checks, with expected values taken from the live table.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant