Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Runtime.Serialization;
using System.Buffers;
using System.Runtime.Serialization;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
Expand All @@ -8,6 +9,7 @@
using BenchmarkDotNet.Toolchains.InProcess.Emit;
using MemoryPack;
using MessagePack;
using ZiggyCreatures.Caching.Fusion.Internals;
using ZiggyCreatures.Caching.Fusion.Serialization;
using ZiggyCreatures.Caching.Fusion.Serialization.CysharpMemoryPack;
using ZiggyCreatures.Caching.Fusion.Serialization.NeueccMessagePack;
Expand Down Expand Up @@ -71,7 +73,7 @@ public Config()
}

[ParamsSource(nameof(GetSerializers))]
public IFusionCacheSerializer Serializer = null!;
public IBufferFusionCacheSerializer Serializer = null!;
protected List<SampleModel> _Models = [];
protected byte[] _Blob = null!;

Expand All @@ -92,25 +94,53 @@ public void Serialize()
Serializer.Serialize(_Models);
}

[Benchmark]
public void Serialize_Buffer()
{
using var writer = new ArrayPoolBufferWriter();
Serializer.Serialize(_Models, writer);
}

[Benchmark]
public void Deserialize()
{
Serializer.Deserialize<List<SampleModel>>(_Blob);
}

[Benchmark]
public void Deserialize_Buffer()
{
var blob = new ReadOnlySequence<byte>(_Blob);
Serializer.Deserialize<List<SampleModel>>(in blob);
}

[Benchmark]
public async Task SerializeAsync()
{
await Serializer.SerializeAsync(_Models).ConfigureAwait(false);
}

[Benchmark]
public async Task SerializeAsync_Buffer()
{
using var writer = new ArrayPoolBufferWriter();
await Serializer.SerializeAsync(_Models, writer).ConfigureAwait(false);
}

[Benchmark]
public async Task DeserializeAsync()
{
await Serializer.DeserializeAsync<List<SampleModel>>(_Blob).ConfigureAwait(false);
}

public static IEnumerable<IFusionCacheSerializer> GetSerializers()
[Benchmark]
public async Task DeserializeAsync_Buffer()
{
var blob = new ReadOnlySequence<byte>(_Blob);
await Serializer.DeserializeAsync<List<SampleModel>>(blob).ConfigureAwait(false);
}

public static IEnumerable<IBufferFusionCacheSerializer> GetSerializers()
{
yield return new FusionCacheCysharpMemoryPackSerializer();
yield return new FusionCacheNeueccMessagePackSerializer();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.Buffers;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;

namespace ZiggyCreatures.Caching.Fusion.Chaos;

/// <summary>
/// An implementation of <see cref="IBufferDistributedCache"/> that acts on behalf of another one, but with a (controllable) amount of chaos in-between.
/// </summary>
public class ChaosBufferDistributedCache : ChaosDistributedCache, IBufferDistributedCache
{
private readonly IBufferDistributedCache _innerCache;

/// <summary>
/// Initializes a new instance of the ChaosDistributedCache class.
/// </summary>
/// <param name="innerCache">The actual <see cref="IBufferDistributedCache"/> used if and when chaos does not happen.</param>
/// <param name="logger">The logger to use, or <see langword="null"/>.</param>
public ChaosBufferDistributedCache(IBufferDistributedCache innerCache, ILogger<ChaosDistributedCache>? logger = null)
: base(innerCache, logger)
{
_innerCache = innerCache;
}

/// <inheritdoc/>
public bool TryGet(string key, IBufferWriter<byte> destination)
{
MaybeChaos();
return _innerCache.TryGet(key, destination);
}

/// <inheritdoc/>
public async ValueTask<bool> TryGetAsync(string key, IBufferWriter<byte> destination, CancellationToken token = default)
{
await MaybeChaosAsync(token).ConfigureAwait(false);
return await _innerCache.TryGetAsync(key, destination, token).ConfigureAwait(false);
}

/// <inheritdoc/>
public void Set(string key, ReadOnlySequence<byte> value, DistributedCacheEntryOptions options)
{
MaybeChaos();
_innerCache.Set(key, value, options);
}

/// <inheritdoc/>
public async ValueTask SetAsync(string key, ReadOnlySequence<byte> value, DistributedCacheEntryOptions options, CancellationToken token = default)
{
await MaybeChaosAsync(token).ConfigureAwait(false);
await _innerCache.SetAsync(key, value, options, token).ConfigureAwait(false);
}
}
52 changes: 52 additions & 0 deletions src/ZiggyCreatures.FusionCache.Chaos/ChaosBufferSerializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.Buffers;
using Microsoft.Extensions.Logging;
using ZiggyCreatures.Caching.Fusion.Serialization;

namespace ZiggyCreatures.Caching.Fusion.Chaos;

/// <summary>
/// An implementation of <see cref="IBufferFusionCacheSerializer"/> that acts on behalf of another one, but with a (controllable) amount of chaos in-between.
/// </summary>
public class ChaosBufferSerializer : ChaosSerializer, IBufferFusionCacheSerializer
{
private readonly IBufferFusionCacheSerializer _innerSerializer;

/// <summary>
/// Initializes a new instance of the ChaosSerializer class.
/// </summary>
/// <param name="innerSerializer">The actual <see cref="IBufferFusionCacheSerializer"/> used if and when chaos does not happen.</param>
/// <param name="logger">The logger to use, or <see langword="null"/>.</param>
public ChaosBufferSerializer(IBufferFusionCacheSerializer innerSerializer, ILogger<ChaosSerializer>? logger = null)
: base(innerSerializer, logger)
{
_innerSerializer = innerSerializer;
}

/// <inheritdoc/>
public void Serialize<T>(T? obj, IBufferWriter<byte> destination)
{
MaybeChaos();
_innerSerializer.Serialize<T>(obj, destination);
}

/// <inheritdoc/>
public T? Deserialize<T>(in ReadOnlySequence<byte> data)
{
MaybeChaos();
return _innerSerializer.Deserialize<T>(data);
}

/// <inheritdoc/>
public async ValueTask SerializeAsync<T>(T? obj, IBufferWriter<byte> destination, CancellationToken token = default)
{
await MaybeChaosAsync().ConfigureAwait(false);
await _innerSerializer.SerializeAsync<T>(obj, destination, token).ConfigureAwait(false);
}

/// <inheritdoc/>
public async ValueTask<T?> DeserializeAsync<T>(ReadOnlySequence<byte> data, CancellationToken token = default)
{
await MaybeChaosAsync().ConfigureAwait(false);
return await _innerSerializer.DeserializeAsync<T>(data, token).ConfigureAwait(false);
}
}
12 changes: 12 additions & 0 deletions src/ZiggyCreatures.FusionCache.Chaos/ChaosDistributedCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ public ChaosDistributedCache(IDistributedCache innerCache, ILogger<ChaosDistribu
_innerCache = innerCache ?? throw new ArgumentNullException(nameof(innerCache));
}

/// <summary>
/// Initializes a new instance of the ChaosDistributedCache class that implements the <see cref="IBufferDistributedCache"/> interface if the given <paramref name="innerCache"/> does.
/// </summary>
/// <param name="innerCache">The actual <see cref="IDistributedCache"/> used if and when chaos does not happen.</param>
/// <param name="logger">The logger to use, or <see langword="null"/>.</param>
public static ChaosDistributedCache Create(IDistributedCache innerCache, ILogger<ChaosDistributedCache>? logger = null)
{
return innerCache is IBufferDistributedCache bufferCache
? new ChaosBufferDistributedCache(bufferCache, logger)
: new ChaosDistributedCache(innerCache, logger);
}

/// <inheritdoc/>
public byte[]? Get(string key)
{
Expand Down
12 changes: 12 additions & 0 deletions src/ZiggyCreatures.FusionCache.Chaos/ChaosSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ public ChaosSerializer(IFusionCacheSerializer innerSerializer, ILogger<ChaosSeri
_innerSerializer = innerSerializer ?? throw new ArgumentNullException(nameof(innerSerializer));
}

/// <summary>
/// Initializes a new instance of the ChaosSerializer class that implements the <see cref="IBufferFusionCacheSerializer"/> interface if the given <paramref name="innerSerializer"/> does.
/// </summary>
/// <param name="innerSerializer">The actual <see cref="IFusionCacheSerializer"/> used if and when chaos does not happen.</param>
/// <param name="logger">The logger to use, or <see langword="null"/>.</param>
public static ChaosSerializer Create(IFusionCacheSerializer innerSerializer, ILogger<ChaosSerializer>? logger = null)
{
return innerSerializer is IBufferFusionCacheSerializer bufferSerializer
? new ChaosBufferSerializer(bufferSerializer, logger)
: new ChaosSerializer(innerSerializer, logger);
}

/// <inheritdoc/>
public byte[] Serialize<T>(T? obj)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
using System.Buffers;
using System.Runtime.CompilerServices;
using MemoryPack;
using ZiggyCreatures.Caching.Fusion.Internals;
using ZiggyCreatures.Caching.Fusion.Internals.Distributed;
Expand All @@ -10,7 +11,7 @@ namespace ZiggyCreatures.Caching.Fusion.Serialization.CysharpMemoryPack;
/// An implementation of <see cref="IFusionCacheSerializer"/> which uses Cysharp's MemoryPack serializer.
/// </summary>
public class FusionCacheCysharpMemoryPackSerializer
: IFusionCacheSerializer
: IFusionCacheSerializer, IBufferFusionCacheSerializer
{
/// <summary>
/// The options class for the <see cref="FusionCacheCysharpMemoryPackSerializer"/> class.
Expand Down Expand Up @@ -55,7 +56,8 @@ public FusionCacheCysharpMemoryPackSerializer(Options? options)
public byte[] Serialize<T>(T? obj)
{
var buffer = new ArrayPoolBufferWriter();
MemoryPackWriter<ArrayPoolBufferWriter> writer = new(ref buffer, MemoryPackWriterOptionalStatePool.Rent(_serializerOptions));
using var writerState = MemoryPackWriterOptionalStatePool.Rent(_serializerOptions);
MemoryPackWriter<ArrayPoolBufferWriter> writer = new(ref buffer, writerState);
try
{
MemoryPackSerializer.Serialize(ref writer, obj);
Expand All @@ -67,27 +69,56 @@ public byte[] Serialize<T>(T? obj)
}
}

/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Serialize<T>(T? obj, IBufferWriter<byte> destination)
{
MemoryPackSerializer.Serialize(destination, obj, _serializerOptions);
}

/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T? Deserialize<T>(byte[] data)
{
return MemoryPackSerializer.Deserialize<T?>(data, _serializerOptions);
}

/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T? Deserialize<T>(in ReadOnlySequence<byte> data)
{
return MemoryPackSerializer.Deserialize<T?>(in data, _serializerOptions);
}

/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask<byte[]> SerializeAsync<T>(T? obj, CancellationToken token = default)
{
return new ValueTask<byte[]>(Serialize(obj));
}

/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask SerializeAsync<T>(T? obj, IBufferWriter<byte> destination, CancellationToken token = default)
{
Serialize(obj, destination);
return new ValueTask();
}

/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask<T?> DeserializeAsync<T>(byte[] data, CancellationToken token = default)
{
return new ValueTask<T?>(Deserialize<T>(data));
}

/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask<T?> DeserializeAsync<T>(ReadOnlySequence<byte> data, CancellationToken token = default)
{
return new ValueTask<T?>(Deserialize<T>(in data));
}

/// <inheritdoc />
public override string ToString() => $"{GetType().Name}";
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
using System.Buffers;
using System.Runtime.CompilerServices;
using MessagePack;
using MessagePack.Resolvers;

Expand All @@ -10,7 +11,7 @@ namespace ZiggyCreatures.Caching.Fusion.Serialization.NeueccMessagePack;
/// An implementation of <see cref="IFusionCacheSerializer"/> which uses Neuecc's famous MessagePack serializer.
/// </summary>
public class FusionCacheNeueccMessagePackSerializer
: IFusionCacheSerializer
: IFusionCacheSerializer, IBufferFusionCacheSerializer
{
/// <summary>
/// The options class for the <see cref="FusionCacheNeueccMessagePackSerializer"/> class.
Expand Down Expand Up @@ -54,13 +55,27 @@ public byte[] Serialize<T>(T? obj)
return writer.ToArray();
}

/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Serialize<T>(T? obj, IBufferWriter<byte> destination)
{
MessagePackSerializer.Serialize(destination, obj, _serializerOptions);
}

/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T? Deserialize<T>(byte[] data)
{
return MessagePackSerializer.Deserialize<T?>(data.AsMemory(), _serializerOptions);
}

/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T? Deserialize<T>(in ReadOnlySequence<byte> data)
{
return MessagePackSerializer.Deserialize<T?>(data, _serializerOptions);
}

/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask<byte[]> SerializeAsync<T>(T? obj, CancellationToken token = default)
Expand All @@ -69,6 +84,15 @@ public ValueTask<byte[]> SerializeAsync<T>(T? obj, CancellationToken token = def
return new ValueTask<byte[]>(Serialize(obj));
}

/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask SerializeAsync<T>(T? obj, IBufferWriter<byte> destination, CancellationToken token = default)
{
// PER @neuecc 'S SUGGESTION: AVOID AWAITING ON A MEMORY STREAM
Serialize(obj, destination);
return new ValueTask();
}

/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask<T?> DeserializeAsync<T>(byte[] data, CancellationToken token = default)
Expand All @@ -77,6 +101,14 @@ public ValueTask<byte[]> SerializeAsync<T>(T? obj, CancellationToken token = def
return new ValueTask<T?>(Deserialize<T>(data));
}

/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask<T?> DeserializeAsync<T>(ReadOnlySequence<byte> data, CancellationToken token = default)
{
// PER @neuecc 'S SUGGESTION: AVOID AWAITING ON A MEMORY STREAM
return new ValueTask<T?>(Deserialize<T>(in data));
}

/// <inheritdoc />
public override string ToString() => $"{GetType().Name}";
}
Loading
Loading