Skip to content

Commit 1382b3c

Browse files
authored
[Documentation] Add extra help information on serializer id errors (#5418)
* Add serializer id documentation * Remove wrong commit * Add documentation to SerializerException error messages * Add table explanation * Add trailing newline to make linter happy * Add file header and disable linter rule for <br>
1 parent ccb4670 commit 1382b3c

File tree

10 files changed

+155
-6
lines changed

10 files changed

+155
-6
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
uid: serializer-codes
3+
title: Serializer ID Code Table
4+
---
5+
<!-- markdownlint-disable MD033 -->
6+
# Akka Serializer ID Code Table
7+
8+
Serializers with ID range 1-100 are reserved for internal Akka.NET serializers. This table maps which
9+
ID belongs to what package and how you can add them into your ActorSystem HOCON configuration or which
10+
Akka.NET plugin initializer that automatically injects the serializer into your settings.
11+
12+
For example, if you are missing a serializer with id 13, you can add them by either adding a fallback
13+
to your configuration by calling `myConfig.WithFallback(ClusterSharding.DefaultConfig())` or by
14+
calling `ClusterSharding.Get(myActorSystem)`.
15+
16+
Serializers for `Akka.Remote` will be added automatically if you use `akka.actor.provider = remote` or
17+
`akka.actor.provider = "Akka.Remote.RemoteActorRefProvider, Akka.Remote"`
18+
19+
Serializers for `Akka.Cluster` will be added automatically if you use `akka.actor.provider = cluster` or
20+
`akka.actor.provider = "Akka.Cluster.ClusterActorRefProvider, Akka.Cluster"`
21+
22+
**Id**|**Serializer Class Name**|**Package**|**Direct Access Method**|**Injected By**
23+
-----|-----|-----|-----|-----
24+
1|NewtonSoftJsonSerializer|Akka|ConfigurationFactory.Default()|ActorSystem.Create()
25+
2|ProtobufSerializer|Akka.Remote|RemoteConfigFactory.Default()|
26+
3|DaemonMsgCreateSerializer|Akka.Remote|RemoteConfigFactory.Default()|
27+
4|ByteArraySerializer|Akka|ConfigurationFactory.Default()|ActorSystem,Create()
28+
5|ClusterMessageSerializer|Akka.Cluster|ClusterConfigFactory.Default()|ClusterSharding.Get()
29+
6|MessageContainerSerializer|Akka.Remote|RemoteConfigFactory.Default()|
30+
7|PersistenceMessageSerializer|Akka.Persistence|Persistence.DefaultConfig()|Persistence.Instance.Apply()
31+
8|PersistenceSnapshotSerializer|Akka.Persistence|Persistence.DefaultConfig()|Persistence.Instance.Apply()
32+
10|ClusterMetricsMessageSerializer|Akka.Cluster.Metrics|ClusterMetrics.DefaultConfig()|ClusterMetrics.Get()
33+
11|ReplicatedDataSerializer|Akka.DistributedData|DistributedData.DefaultConfig()<br>ClusterSharding.DefaultConfig()|ClusterSharding.Get()<br>DistributedData.Get()
34+
12|ReplicatorMessageSerializer|Akka.DistributedData|DistributedData.DefaultConfig()<br>ClusterSharding.DefaultConfig()|ClusterSharding.Get()<br>DistributedData.Get()
35+
13|ClusterShardingMessageSerializer|Akka.Cluster.Sharding|ClusterSharding.DefaultConfig()|ClusterSharding.Get()
36+
14|ClusterSingletonMessageSerializer|Akka.Cluster.Tools|DistributedPubSub.DefaultConfig()<br>ClusterSingletonProxy.DefaultConfig()<br>ClusterSingletonManager.DefaultConfig()|DistributedPubSub.Get()<br>ClusterSharding.Get()
37+
15|ClusterClientMessageSerializer|Akka.Cluster.Tools|ClusterClientReceptionist.DefaultConfig()|ClusterClientReceptionist.Get()
38+
16|MiscMessageSerializer|Akka.Remote|RemoteConfigFactory.Default()|
39+
17|PrimitiveSerializers|Akka.Remote|RemoteConfigFactory.Default()|
40+
22|SystemMessageSerializer|Akka.Remote|RemoteConfigFactory.Default()|
41+
30|StreamRefSerializer|Akka.Streams|ActorMaterializer.DefaultConfig()|ActorSystem.Materializer()
42+
48|PersistentSnapshotSerializer|Akka.Persistence.Redis|RedisPersistence.DefaultConfig()|RedisPersistence.Get()

docs/articles/serialization/toc.yml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- name: Serializer Code Table
2+
href: serializer-codes.md

docs/articles/toc.yml

+2
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,5 @@
202202
href: utilities/circuit-breaker.md
203203
- name: Configurations
204204
href: configuration/toc.yml
205+
- name: Serialization
206+
href: serialization/toc.yml

src/core/Akka.Remote.Tests/Serialization/BugFix5062Spec.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public void Failed_serialization_should_give_proper_exception_message()
5151
var o = new object();
5252
o.Invoking(s => MessageSerializer.Deserialize((ExtendedActorSystem)Sys, serialized)).Should()
5353
.Throw<SerializationException>()
54-
.WithMessage($"Failed to deserialize payload object when deserializing {nameof(ActorSelectionMessage)} with payload [SerializerId=13, Manifest=SM] addressed to [{childName}]")
54+
.WithMessage($"Failed to deserialize payload object when deserializing {nameof(ActorSelectionMessage)} with payload [SerializerId=13, Manifest=SM] addressed to [{childName}]. {Serializer.GetErrorForSerializerId(13)}")
5555
.WithInnerExceptionExactly<NotImplementedException>();
5656
}
5757

src/core/Akka.Remote/Endpoint.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1987,10 +1987,11 @@ private void LogTransientSerializationError(Message msg, Exception error)
19871987
var sm = msg.SerializedMessage;
19881988
_log.Warning(error,
19891989
"Deserialization failed for message with serializer id [{0}] and manifest [{1}]. " +
1990-
"Transient association error (association remains live). {2}",
1990+
"Transient association error (association remains live). {2}. {3}",
19911991
sm.SerializerId,
19921992
sm.MessageManifest.IsEmpty ? "" : sm.MessageManifest.ToStringUtf8(),
1993-
error.Message);
1993+
error.Message,
1994+
Serializer.GetErrorForSerializerId(sm.SerializerId));
19941995
}
19951996

19961997
private void NotReading()

src/core/Akka.Remote/Serialization/MessageContainerSerializer.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ public override object FromBinary(byte[] bytes, Type type)
100100
: string.Empty;
101101

102102
throw new SerializationException(
103-
$"Failed to deserialize payload object when deserializing {nameof(ActorSelectionMessage)} with payload [SerializerId={payload.SerializerId}, Manifest={manifest}] addressed to [{string.Join(",", elements.Select(e => e.ToString()))}]", ex);
103+
$"Failed to deserialize payload object when deserializing {nameof(ActorSelectionMessage)} with " +
104+
$"payload [SerializerId={payload.SerializerId}, Manifest={manifest}] addressed to [" +
105+
$"{string.Join(",", elements.Select(e => e.ToString()))}]. {GetErrorForSerializerId(payload.SerializerId)}", ex);
104106
}
105107

106108
return new ActorSelectionMessage(message, elements);

src/core/Akka.Tests/Serialization/SerializationSpec.cs

+29
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Collections.Concurrent;
1010
using System.IO;
1111
using System.Reflection;
12+
using System.Runtime.Serialization;
1213
using System.Text.RegularExpressions;
1314
using Akka.Actor;
1415
using Akka.Configuration;
@@ -579,6 +580,34 @@ public void Legacy_and_shortened_types_names_are_equivalent()
579580
Type.GetType(legacyTypeManifest).ShouldBeSame(Type.GetType(newTypeManifest));
580581
}
581582

583+
[Fact]
584+
public void Missing_known_internal_serializer_id_should_append_help_message()
585+
{
586+
var serializer = Sys.Serialization;
587+
serializer.Invoking(s => s.Deserialize(null, 13, typeof(object))).Should()
588+
.Throw<SerializationException>()
589+
.Where(ex => ex.Message.Contains(SerializerErrorCode.ErrorCodes[13].ToString()));
590+
}
591+
592+
[Fact]
593+
public void Missing_unknown_internal_serializer_id_should_append_help_message()
594+
{
595+
var serializer = Sys.Serialization;
596+
// no such thing as serializer with id 18
597+
serializer.Invoking(s => s.Deserialize(null, 18, typeof(object))).Should()
598+
.Throw<SerializationException>()
599+
.Where(ex => ex.Message.Contains("Could not find any internal Akka.NET serializer with Id [18]."));
600+
}
601+
602+
[Fact]
603+
public void Missing_custom_serializer_id_should_append_help_message()
604+
{
605+
var serializer = Sys.Serialization;
606+
serializer.Invoking(s => s.Deserialize(null, 101, typeof(object))).Should()
607+
.Throw<SerializationException>()
608+
.Where(ex => ex.Message.Contains("Serializer Id [101] is not one of the internal Akka.NET serializer."));
609+
}
610+
582611
public SerializationSpec():base(GetConfig())
583612
{
584613
}

src/core/Akka/Serialization/Serialization.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,8 @@ public object Deserialize(byte[] bytes, int serializerId, Type type)
380380
if (!_serializersById.TryGetValue(serializerId, out var serializer))
381381
throw new SerializationException(
382382
$"Cannot find serializer with id [{serializerId}] (class [{type?.Name}]). The most probable reason" +
383-
" is that the configuration entry 'akka.actor.serializers' is not in sync between the two systems.");
383+
" is that the configuration entry 'akka.actor.serializers' is not in sync between the two systems." +
384+
$" {Serializer.GetErrorForSerializerId(serializerId)}");
384385

385386
return serializer.FromBinary(bytes, type);
386387
});
@@ -402,7 +403,8 @@ public object Deserialize(byte[] bytes, int serializerId, string manifest)
402403
if (!_serializersById.TryGetValue(serializerId, out var serializer))
403404
throw new SerializationException(
404405
$"Cannot find serializer with id [{serializerId}] (manifest [{manifest}]). The most probable reason" +
405-
" is that the configuration entry 'akka.actor.serializers' is not in sync between the two systems.");
406+
" is that the configuration entry 'akka.actor.serializers' is not in sync between the two systems." +
407+
$" {Serializer.GetErrorForSerializerId(serializerId)}");
406408

407409
// not using `withTransportInformation { () =>` because deserializeByteBuffer is supposed to be the
408410
// possibility for allocation free serialization

src/core/Akka/Serialization/Serializer.cs

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//-----------------------------------------------------------------------
77

88
using System;
9+
using System.Collections.Generic;
910
using System.Linq;
1011
using System.Reflection;
1112
using System.Runtime.Serialization;
@@ -37,6 +38,8 @@ namespace Akka.Serialization
3738
/// </summary>
3839
public abstract class Serializer
3940
{
41+
internal static string GetErrorForSerializerId(int id) => SerializerErrorCode.GetErrorForSerializerId(id);
42+
4043
/// <summary>
4144
/// The actor system to associate with this serializer.
4245
/// </summary>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// //-----------------------------------------------------------------------
2+
// // <copyright file="ErrorCodes.cs" company="Akka.NET Project">
3+
// // Copyright (C) 2009-2021 Lightbend Inc. <http://www.lightbend.com>
4+
// // Copyright (C) 2013-2021 .NET Foundation <https://github.com/akkadotnet/akka.net>
5+
// // </copyright>
6+
// //-----------------------------------------------------------------------
7+
8+
using System.Collections.Generic;
9+
using System.Text;
10+
11+
namespace Akka.Serialization
12+
{
13+
internal class SerializerErrorCode
14+
{
15+
public static readonly Dictionary<int, SerializerErrorCode> ErrorCodes = new Dictionary<int, SerializerErrorCode>
16+
{
17+
[1] = new SerializerErrorCode(1, "Akka.Serialization.NewtonSoftJsonSerializer, Akka", "ConfigurationFactory.Default()", "ActorSystem.Create()"),
18+
[2] = new SerializerErrorCode(2, "Akka.Remote.Serialization.ProtobufSerializer, Akka.Remote", "RemoteConfigFactory.Default()", null),
19+
[3] = new SerializerErrorCode(3, "Akka.Remote.Serialization.DaemonMsgCreateSerializer, Akka.Remote", "RemoteConfigFactory.Default()", null),
20+
[4] = new SerializerErrorCode(4, "Akka.Serialization.ByteArraySerializer, Akka", "ConfigurationFactory.Default()", "ActorSystem.Create()"),
21+
[5] = new SerializerErrorCode(5, "Akka.Cluster.Serialization.ClusterMessageSerializer, Akka.Cluster", "ClusterConfigFactory.Default()", "ClusterSharding.Get()"),
22+
[6] = new SerializerErrorCode(6, "Akka.Remote.Serialization.MessageContainerSerializer, Akka.Remote", "RemoteConfigFactory.Default()", null),
23+
[7] = new SerializerErrorCode(7, "Akka.Persistence.Serialization.PersistenceMessageSerializer, Akka.Persistence", "Persistence.DefaultConfig()", "Persistence.Instance.Apply()"),
24+
[8] = new SerializerErrorCode(8, "Akka.Persistence.Serialization.PersistenceSnapshotSerializer, Akka.Persistence", "Persistence.DefaultConfig()", "Persistence.Instance.Apply()"),
25+
[10] = new SerializerErrorCode(10, "Akka.Cluster.Metrics.Serialization.ClusterMetricsMessageSerializer, Akka.Cluster.Metrics", "ClusterMetrics.DefaultConfig()", "ClusterMetrics.Get()"),
26+
[11] = new SerializerErrorCode(11, "Akka.DistributedData.Serialization.ReplicatedDataSerializer, Akka.DistributedData", "DistributedData.DefaultConfig() or ClusterSharding.DefaultConfig()", "ClusterSharding.Get(), DistributedData.Get()"),
27+
[12] = new SerializerErrorCode(12, "Akka.DistributedData.Serialization.ReplicatorMessageSerializer, Akka.DistributedData", "DistributedData.DefaultConfig() or ClusterSharding.DefaultConfig()", "ClusterSharding.Get(), DistributedData.Get()"),
28+
[13] = new SerializerErrorCode(13, "Akka.Cluster.Sharding.Serialization.ClusterShardingMessageSerializer, Akka.Cluster.Sharding", "ClusterSharding.DefaultConfig()", "ClusterSharding.Get()"),
29+
[14] = new SerializerErrorCode(14, "Akka.Cluster.Tools.Singleton.Serialization.ClusterSingletonMessageSerializer, Akka.Cluster.Tools", "DistributedPubSub.DefaultConfig(), ClusterSingletonProxy.DefaultConfig(), or ClusterSingletonManager.DefaultConfig()", "DistributedPubSub.Get(), ClusterSharding.Get()"),
30+
[15] = new SerializerErrorCode(15, "Akka.Cluster.Tools.Client.Serialization.ClusterClientMessageSerializer, Akka.Cluster.Tools", "ClusterClientReceptionist.DefaultConfig()", "ClusterClientReceptionist.Get()"),
31+
[16] = new SerializerErrorCode(16, "Akka.Remote.Serialization.MiscMessageSerializer, Akka.Remote", "RemoteConfigFactory.Default()", null),
32+
[17] = new SerializerErrorCode(17, "Akka.Remote.Serialization.PrimitiveSerializers, Akka.Remote", "RemoteConfigFactory.Default()", null),
33+
[22] = new SerializerErrorCode(22, "Akka.Remote.Serialization.SystemMessageSerializer, Akka.Remote", "RemoteConfigFactory.Default()", null),
34+
[30] = new SerializerErrorCode(30, "Akka.Streams.Serialization.StreamRefSerializer, Akka.Streams", "ActorMaterializer.DefaultConfig()", "ActorSystem.Materializer()"),
35+
[48] = new SerializerErrorCode(48, "Akka.Persistence.Redis.Serialization.PersistentSnapshotSerializer, Akka.Persistence.Redis", "RedisPersistence.DefaultConfig()", "RedisPersistence.Get()"),
36+
};
37+
38+
public static string GetErrorForSerializerId(int id)
39+
=> ErrorCodes.TryGetValue(id, out var err)
40+
? err.ToString() : id > 100
41+
? $"Serializer Id [{id}] is not one of the internal Akka.NET serializer. Please contact your plugin provider for more information."
42+
: $"Could not find any internal Akka.NET serializer with Id [{id}]. Please create an issue in our GitHub at [https://github.com/akkadotnet/akka.net].";
43+
44+
private SerializerErrorCode(int id, string fqcn, string directAccess, string injector)
45+
{
46+
Id = id;
47+
Fqcn = fqcn;
48+
DirectAccess = directAccess;
49+
Injector = injector;
50+
}
51+
52+
public int Id { get; }
53+
public string Fqcn { get; }
54+
public string DirectAccess { get; }
55+
public string Injector { get; }
56+
57+
public override string ToString()
58+
{
59+
var sb = new StringBuilder($"Serializer Id [{Id}] is used to instantiate [{Fqcn}]. ")
60+
.Append($"You can add it by adding a fallback to your ActorSystem configuration by using [{DirectAccess}].");
61+
if (Injector != null)
62+
sb.Append($" It is also automatically injected into your configuration when you call [{Injector}].");
63+
return sb.ToString();
64+
}
65+
}
66+
}

0 commit comments

Comments
 (0)