Skip to content

Commit

Permalink
Allow to incrementally adopt new ID format in distributed system (#7343)
Browse files Browse the repository at this point in the history
  • Loading branch information
tobias-tengler authored Aug 10, 2024
1 parent 5ca1d87 commit a5c1cb1
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ public static partial class RequestExecutorBuilderExtensions
/// <param name="maxIdLength">
/// The maximum allowed length of a node id.
/// </param>
/// <param name="outputNewIdFormat">
/// Whether the new ID format shall be used when serializing IDs.
/// </param>
/// <returns>
/// Returns the request executor builder.
/// </returns>
Expand All @@ -26,7 +29,8 @@ public static partial class RequestExecutorBuilderExtensions
/// </exception>
public static IRequestExecutorBuilder AddDefaultNodeIdSerializer(
this IRequestExecutorBuilder builder,
int maxIdLength = 1024)
int maxIdLength = 1024,
bool outputNewIdFormat = true)
{
if (builder == null)
{
Expand All @@ -47,7 +51,7 @@ public static IRequestExecutorBuilder AddDefaultNodeIdSerializer(
builder.Services.TryAddSingleton<INodeIdSerializer>(sp =>
{
var allSerializers = sp.GetServices<INodeIdValueSerializer>().ToArray();
return new DefaultNodeIdSerializer(allSerializers, maxIdLength);
return new DefaultNodeIdSerializer(allSerializers, maxIdLength, outputNewIdFormat);
});

builder.ConfigureSchemaServices(
Expand Down Expand Up @@ -76,7 +80,7 @@ public static IRequestExecutorBuilder AddDefaultNodeIdSerializer(
}
}
return new OptimizedNodeIdSerializer(boundSerializers, allSerializers, maxIdLength);
return new OptimizedNodeIdSerializer(boundSerializers, allSerializers, maxIdLength, outputNewIdFormat);
});
});
return builder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ namespace HotChocolate.Types.Relay;

public sealed class DefaultNodeIdSerializer(
INodeIdValueSerializer[]? serializers = null,
int maxIdLength = 1024)
int maxIdLength = 1024,
bool outputNewIdFormat = true)
: INodeIdSerializer
{
private const byte _delimiter = (byte)':';
Expand Down Expand Up @@ -42,6 +43,11 @@ public string Format(string typeName, object internalId)
throw new ArgumentNullException(nameof(internalId));
}

if (!outputNewIdFormat)
{
return LegacyNodeIdSerializer.FormatInternal(typeName, internalId);
}

var runtimeType = internalId.GetType();
var serializer = TryResolveSerializer(runtimeType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ internal sealed class LegacyNodeIdSerializer : INodeIdSerializer
private static readonly Encoding _utf8 = Encoding.UTF8;

public string Format(string typeName, object internalId)
=> FormatInternal(typeName, internalId);

internal static string FormatInternal(string typeName, object internalId)
{
if (string.IsNullOrEmpty(typeName))
{
Expand Down Expand Up @@ -186,7 +189,7 @@ private static NodeId Parse(string formattedId)
}
}

internal static object ParseValueInternal(ReadOnlySpan<byte> formattedId)
private static object ParseValueInternal(ReadOnlySpan<byte> formattedId)
{
object value;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ internal sealed class OptimizedNodeIdSerializer : INodeIdSerializer
private readonly SpanSerializerMap _spanSerializerMap;
private readonly INodeIdValueSerializer[] _serializers;
private readonly int _maxIdLength;
private readonly bool _outputNewIdFormat;

internal OptimizedNodeIdSerializer(
IEnumerable<BoundNodeIdValueSerializer> boundSerializers,
INodeIdValueSerializer[] allSerializers,
int maxIdLength = 1024)
int maxIdLength = 1024,
bool outputNewIdFormat = true)
{
#if NET8_0_OR_GREATER
_stringSerializerMap =
Expand All @@ -48,20 +50,28 @@ internal OptimizedNodeIdSerializer(
}

_maxIdLength = maxIdLength;
_outputNewIdFormat = outputNewIdFormat;
}

public string Format(string typeName, object internalId)
{
if (typeName is null)
if (string.IsNullOrEmpty(typeName))
{
throw new ArgumentNullException(nameof(typeName));
throw new ArgumentException(
"Value cannot be null or empty.",
nameof(typeName));
}

if (internalId is null)
{
throw new ArgumentNullException(nameof(internalId));
}

if (!_outputNewIdFormat)
{
return LegacyNodeIdSerializer.FormatInternal(typeName, internalId);
}

if (!_stringSerializerMap.TryGetValue(typeName, out var serializer))
{
throw new NodeIdMissingSerializerException(typeName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ public void Format_Small_StringId()
Assert.Equal("Rm9vOmFiYw==", id);
}

[Fact]
public void Format_Small_StringId_Without_Outputting_New_Format()
{
var serializer = new DefaultNodeIdSerializer(
serializers: [new StringNodeIdValueSerializer()],
outputNewIdFormat: false);

var id = serializer.Format("Foo", "abc");

Assert.Equal("Rm9vCmRhYmM=", id);
}

[Fact]
public void Parse_Small_StringId()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ public void Format_Small_StringId()
Assert.Equal("Rm9vOmFiYw==", id);
}

[Fact]
public void Format_Small_StringId_Without_Outputting_New_Format()
{
var stringSerializer = new StringNodeIdValueSerializer();
var serializer = new OptimizedNodeIdSerializer(
[new BoundNodeIdValueSerializer("Foo", stringSerializer)],
[stringSerializer],
outputNewIdFormat: false);

var id = serializer.Format("Foo", "abc");

Assert.Equal("Rm9vCmRhYmM=", id);
}

[Fact]
public void Parse_Small_StringId()
{
Expand Down

0 comments on commit a5c1cb1

Please sign in to comment.