Skip to content

Commit

Permalink
[Documentation] Add extra help information on serializer id errors (#…
Browse files Browse the repository at this point in the history
…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>
  • Loading branch information
Arkatufus authored Dec 8, 2021
1 parent ccb4670 commit 1382b3c
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 6 deletions.
42 changes: 42 additions & 0 deletions docs/articles/serialization/serializer-codes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
uid: serializer-codes
title: Serializer ID Code Table
---
<!-- markdownlint-disable MD033 -->
# Akka Serializer ID Code Table

Serializers with ID range 1-100 are reserved for internal Akka.NET serializers. This table maps which
ID belongs to what package and how you can add them into your ActorSystem HOCON configuration or which
Akka.NET plugin initializer that automatically injects the serializer into your settings.

For example, if you are missing a serializer with id 13, you can add them by either adding a fallback
to your configuration by calling `myConfig.WithFallback(ClusterSharding.DefaultConfig())` or by
calling `ClusterSharding.Get(myActorSystem)`.

Serializers for `Akka.Remote` will be added automatically if you use `akka.actor.provider = remote` or
`akka.actor.provider = "Akka.Remote.RemoteActorRefProvider, Akka.Remote"`

Serializers for `Akka.Cluster` will be added automatically if you use `akka.actor.provider = cluster` or
`akka.actor.provider = "Akka.Cluster.ClusterActorRefProvider, Akka.Cluster"`

**Id**|**Serializer Class Name**|**Package**|**Direct Access Method**|**Injected By**
-----|-----|-----|-----|-----
1|NewtonSoftJsonSerializer|Akka|ConfigurationFactory.Default()|ActorSystem.Create()
2|ProtobufSerializer|Akka.Remote|RemoteConfigFactory.Default()|
3|DaemonMsgCreateSerializer|Akka.Remote|RemoteConfigFactory.Default()|
4|ByteArraySerializer|Akka|ConfigurationFactory.Default()|ActorSystem,Create()
5|ClusterMessageSerializer|Akka.Cluster|ClusterConfigFactory.Default()|ClusterSharding.Get()
6|MessageContainerSerializer|Akka.Remote|RemoteConfigFactory.Default()|
7|PersistenceMessageSerializer|Akka.Persistence|Persistence.DefaultConfig()|Persistence.Instance.Apply()
8|PersistenceSnapshotSerializer|Akka.Persistence|Persistence.DefaultConfig()|Persistence.Instance.Apply()
10|ClusterMetricsMessageSerializer|Akka.Cluster.Metrics|ClusterMetrics.DefaultConfig()|ClusterMetrics.Get()
11|ReplicatedDataSerializer|Akka.DistributedData|DistributedData.DefaultConfig()<br>ClusterSharding.DefaultConfig()|ClusterSharding.Get()<br>DistributedData.Get()
12|ReplicatorMessageSerializer|Akka.DistributedData|DistributedData.DefaultConfig()<br>ClusterSharding.DefaultConfig()|ClusterSharding.Get()<br>DistributedData.Get()
13|ClusterShardingMessageSerializer|Akka.Cluster.Sharding|ClusterSharding.DefaultConfig()|ClusterSharding.Get()
14|ClusterSingletonMessageSerializer|Akka.Cluster.Tools|DistributedPubSub.DefaultConfig()<br>ClusterSingletonProxy.DefaultConfig()<br>ClusterSingletonManager.DefaultConfig()|DistributedPubSub.Get()<br>ClusterSharding.Get()
15|ClusterClientMessageSerializer|Akka.Cluster.Tools|ClusterClientReceptionist.DefaultConfig()|ClusterClientReceptionist.Get()
16|MiscMessageSerializer|Akka.Remote|RemoteConfigFactory.Default()|
17|PrimitiveSerializers|Akka.Remote|RemoteConfigFactory.Default()|
22|SystemMessageSerializer|Akka.Remote|RemoteConfigFactory.Default()|
30|StreamRefSerializer|Akka.Streams|ActorMaterializer.DefaultConfig()|ActorSystem.Materializer()
48|PersistentSnapshotSerializer|Akka.Persistence.Redis|RedisPersistence.DefaultConfig()|RedisPersistence.Get()
2 changes: 2 additions & 0 deletions docs/articles/serialization/toc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- name: Serializer Code Table
href: serializer-codes.md
2 changes: 2 additions & 0 deletions docs/articles/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,5 @@
href: utilities/circuit-breaker.md
- name: Configurations
href: configuration/toc.yml
- name: Serialization
href: serialization/toc.yml
2 changes: 1 addition & 1 deletion src/core/Akka.Remote.Tests/Serialization/BugFix5062Spec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public void Failed_serialization_should_give_proper_exception_message()
var o = new object();
o.Invoking(s => MessageSerializer.Deserialize((ExtendedActorSystem)Sys, serialized)).Should()
.Throw<SerializationException>()
.WithMessage($"Failed to deserialize payload object when deserializing {nameof(ActorSelectionMessage)} with payload [SerializerId=13, Manifest=SM] addressed to [{childName}]")
.WithMessage($"Failed to deserialize payload object when deserializing {nameof(ActorSelectionMessage)} with payload [SerializerId=13, Manifest=SM] addressed to [{childName}]. {Serializer.GetErrorForSerializerId(13)}")
.WithInnerExceptionExactly<NotImplementedException>();
}

Expand Down
5 changes: 3 additions & 2 deletions src/core/Akka.Remote/Endpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1987,10 +1987,11 @@ private void LogTransientSerializationError(Message msg, Exception error)
var sm = msg.SerializedMessage;
_log.Warning(error,
"Deserialization failed for message with serializer id [{0}] and manifest [{1}]. " +
"Transient association error (association remains live). {2}",
"Transient association error (association remains live). {2}. {3}",
sm.SerializerId,
sm.MessageManifest.IsEmpty ? "" : sm.MessageManifest.ToStringUtf8(),
error.Message);
error.Message,
Serializer.GetErrorForSerializerId(sm.SerializerId));
}

private void NotReading()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ public override object FromBinary(byte[] bytes, Type type)
: string.Empty;

throw new SerializationException(
$"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);
$"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()))}]. {GetErrorForSerializerId(payload.SerializerId)}", ex);
}

return new ActorSelectionMessage(message, elements);
Expand Down
29 changes: 29 additions & 0 deletions src/core/Akka.Tests/Serialization/SerializationSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Collections.Concurrent;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using Akka.Actor;
using Akka.Configuration;
Expand Down Expand Up @@ -579,6 +580,34 @@ public void Legacy_and_shortened_types_names_are_equivalent()
Type.GetType(legacyTypeManifest).ShouldBeSame(Type.GetType(newTypeManifest));
}

[Fact]
public void Missing_known_internal_serializer_id_should_append_help_message()
{
var serializer = Sys.Serialization;
serializer.Invoking(s => s.Deserialize(null, 13, typeof(object))).Should()
.Throw<SerializationException>()
.Where(ex => ex.Message.Contains(SerializerErrorCode.ErrorCodes[13].ToString()));
}

[Fact]
public void Missing_unknown_internal_serializer_id_should_append_help_message()
{
var serializer = Sys.Serialization;
// no such thing as serializer with id 18
serializer.Invoking(s => s.Deserialize(null, 18, typeof(object))).Should()
.Throw<SerializationException>()
.Where(ex => ex.Message.Contains("Could not find any internal Akka.NET serializer with Id [18]."));
}

[Fact]
public void Missing_custom_serializer_id_should_append_help_message()
{
var serializer = Sys.Serialization;
serializer.Invoking(s => s.Deserialize(null, 101, typeof(object))).Should()
.Throw<SerializationException>()
.Where(ex => ex.Message.Contains("Serializer Id [101] is not one of the internal Akka.NET serializer."));
}

public SerializationSpec():base(GetConfig())
{
}
Expand Down
6 changes: 4 additions & 2 deletions src/core/Akka/Serialization/Serialization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,8 @@ public object Deserialize(byte[] bytes, int serializerId, Type type)
if (!_serializersById.TryGetValue(serializerId, out var serializer))
throw new SerializationException(
$"Cannot find serializer with id [{serializerId}] (class [{type?.Name}]). The most probable reason" +
" is that the configuration entry 'akka.actor.serializers' is not in sync between the two systems.");
" is that the configuration entry 'akka.actor.serializers' is not in sync between the two systems." +
$" {Serializer.GetErrorForSerializerId(serializerId)}");

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

// not using `withTransportInformation { () =>` because deserializeByteBuffer is supposed to be the
// possibility for allocation free serialization
Expand Down
3 changes: 3 additions & 0 deletions src/core/Akka/Serialization/Serializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//-----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
Expand Down Expand Up @@ -37,6 +38,8 @@ namespace Akka.Serialization
/// </summary>
public abstract class Serializer
{
internal static string GetErrorForSerializerId(int id) => SerializerErrorCode.GetErrorForSerializerId(id);

/// <summary>
/// The actor system to associate with this serializer.
/// </summary>
Expand Down
66 changes: 66 additions & 0 deletions src/core/Akka/Serialization/SerializerErrorCode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// //-----------------------------------------------------------------------
// // <copyright file="ErrorCodes.cs" company="Akka.NET Project">
// // Copyright (C) 2009-2021 Lightbend Inc. <http://www.lightbend.com>
// // Copyright (C) 2013-2021 .NET Foundation <https://github.com/akkadotnet/akka.net>
// // </copyright>
// //-----------------------------------------------------------------------

using System.Collections.Generic;
using System.Text;

namespace Akka.Serialization
{
internal class SerializerErrorCode
{
public static readonly Dictionary<int, SerializerErrorCode> ErrorCodes = new Dictionary<int, SerializerErrorCode>
{
[1] = new SerializerErrorCode(1, "Akka.Serialization.NewtonSoftJsonSerializer, Akka", "ConfigurationFactory.Default()", "ActorSystem.Create()"),
[2] = new SerializerErrorCode(2, "Akka.Remote.Serialization.ProtobufSerializer, Akka.Remote", "RemoteConfigFactory.Default()", null),
[3] = new SerializerErrorCode(3, "Akka.Remote.Serialization.DaemonMsgCreateSerializer, Akka.Remote", "RemoteConfigFactory.Default()", null),
[4] = new SerializerErrorCode(4, "Akka.Serialization.ByteArraySerializer, Akka", "ConfigurationFactory.Default()", "ActorSystem.Create()"),
[5] = new SerializerErrorCode(5, "Akka.Cluster.Serialization.ClusterMessageSerializer, Akka.Cluster", "ClusterConfigFactory.Default()", "ClusterSharding.Get()"),
[6] = new SerializerErrorCode(6, "Akka.Remote.Serialization.MessageContainerSerializer, Akka.Remote", "RemoteConfigFactory.Default()", null),
[7] = new SerializerErrorCode(7, "Akka.Persistence.Serialization.PersistenceMessageSerializer, Akka.Persistence", "Persistence.DefaultConfig()", "Persistence.Instance.Apply()"),
[8] = new SerializerErrorCode(8, "Akka.Persistence.Serialization.PersistenceSnapshotSerializer, Akka.Persistence", "Persistence.DefaultConfig()", "Persistence.Instance.Apply()"),
[10] = new SerializerErrorCode(10, "Akka.Cluster.Metrics.Serialization.ClusterMetricsMessageSerializer, Akka.Cluster.Metrics", "ClusterMetrics.DefaultConfig()", "ClusterMetrics.Get()"),
[11] = new SerializerErrorCode(11, "Akka.DistributedData.Serialization.ReplicatedDataSerializer, Akka.DistributedData", "DistributedData.DefaultConfig() or ClusterSharding.DefaultConfig()", "ClusterSharding.Get(), DistributedData.Get()"),
[12] = new SerializerErrorCode(12, "Akka.DistributedData.Serialization.ReplicatorMessageSerializer, Akka.DistributedData", "DistributedData.DefaultConfig() or ClusterSharding.DefaultConfig()", "ClusterSharding.Get(), DistributedData.Get()"),
[13] = new SerializerErrorCode(13, "Akka.Cluster.Sharding.Serialization.ClusterShardingMessageSerializer, Akka.Cluster.Sharding", "ClusterSharding.DefaultConfig()", "ClusterSharding.Get()"),
[14] = new SerializerErrorCode(14, "Akka.Cluster.Tools.Singleton.Serialization.ClusterSingletonMessageSerializer, Akka.Cluster.Tools", "DistributedPubSub.DefaultConfig(), ClusterSingletonProxy.DefaultConfig(), or ClusterSingletonManager.DefaultConfig()", "DistributedPubSub.Get(), ClusterSharding.Get()"),
[15] = new SerializerErrorCode(15, "Akka.Cluster.Tools.Client.Serialization.ClusterClientMessageSerializer, Akka.Cluster.Tools", "ClusterClientReceptionist.DefaultConfig()", "ClusterClientReceptionist.Get()"),
[16] = new SerializerErrorCode(16, "Akka.Remote.Serialization.MiscMessageSerializer, Akka.Remote", "RemoteConfigFactory.Default()", null),
[17] = new SerializerErrorCode(17, "Akka.Remote.Serialization.PrimitiveSerializers, Akka.Remote", "RemoteConfigFactory.Default()", null),
[22] = new SerializerErrorCode(22, "Akka.Remote.Serialization.SystemMessageSerializer, Akka.Remote", "RemoteConfigFactory.Default()", null),
[30] = new SerializerErrorCode(30, "Akka.Streams.Serialization.StreamRefSerializer, Akka.Streams", "ActorMaterializer.DefaultConfig()", "ActorSystem.Materializer()"),
[48] = new SerializerErrorCode(48, "Akka.Persistence.Redis.Serialization.PersistentSnapshotSerializer, Akka.Persistence.Redis", "RedisPersistence.DefaultConfig()", "RedisPersistence.Get()"),
};

public static string GetErrorForSerializerId(int id)
=> ErrorCodes.TryGetValue(id, out var err)
? err.ToString() : id > 100
? $"Serializer Id [{id}] is not one of the internal Akka.NET serializer. Please contact your plugin provider for more information."
: $"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].";

private SerializerErrorCode(int id, string fqcn, string directAccess, string injector)
{
Id = id;
Fqcn = fqcn;
DirectAccess = directAccess;
Injector = injector;
}

public int Id { get; }
public string Fqcn { get; }
public string DirectAccess { get; }
public string Injector { get; }

public override string ToString()
{
var sb = new StringBuilder($"Serializer Id [{Id}] is used to instantiate [{Fqcn}]. ")
.Append($"You can add it by adding a fallback to your ActorSystem configuration by using [{DirectAccess}].");
if (Injector != null)
sb.Append($" It is also automatically injected into your configuration when you call [{Injector}].");
return sb.ToString();
}
}
}

0 comments on commit 1382b3c

Please sign in to comment.