Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve serialization handling #217

Merged
merged 8 commits into from
Apr 9, 2023
Merged
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
Expand Up @@ -23,9 +23,9 @@ public class CacheAlternatives_File_Benchmark : BaseBenchmark

public CacheAlternatives_File_Benchmark()
{
CacheTowerNewtonsoftJson = new CacheStack(new[] { new FileCacheLayer(new(DirectoryPath, NewtonsoftJsonCacheSerializer.Instance)) }, Array.Empty<ICacheExtension>());
CacheTowerSystemTextJson = new CacheStack(new[] { new FileCacheLayer(new(DirectoryPath, SystemTextJsonCacheSerializer.Instance)) }, Array.Empty<ICacheExtension>());
CacheTowerProtobuf = new CacheStack(new[] { new FileCacheLayer(new(DirectoryPath, ProtobufCacheSerializer.Instance)) }, Array.Empty<ICacheExtension>());
CacheTowerNewtonsoftJson = new CacheStack(null, new[] { new FileCacheLayer(new(DirectoryPath, NewtonsoftJsonCacheSerializer.Instance)) }, Array.Empty<ICacheExtension>());
CacheTowerSystemTextJson = new CacheStack(null, new[] { new FileCacheLayer(new(DirectoryPath, SystemTextJsonCacheSerializer.Instance)) }, Array.Empty<ICacheExtension>());
CacheTowerProtobuf = new CacheStack(null, new[] { new FileCacheLayer(new(DirectoryPath, ProtobufCacheSerializer.Instance)) }, Array.Empty<ICacheExtension>());
}

private static void CleanupFileSystem()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class CacheAlternatives_Memory_Benchmark : BaseBenchmark

public CacheAlternatives_Memory_Benchmark()
{
CacheTower = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>());
CacheTower = new CacheStack(null, new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>());
CacheManager = CacheFactory.Build<string>(b =>
{
b.WithMicrosoftMemoryCacheHandle();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class CacheAlternatives_Memory_Parallel_Benchmark : BaseBenchmark

public CacheAlternatives_Memory_Parallel_Benchmark()
{
CacheTower = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>());
CacheTower = new CacheStack(null, new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>());
CacheManager = CacheFactory.Build<string>(b =>
{
b.WithMicrosoftMemoryCacheHandle();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class CacheAlternatives_Redis_Benchmark : BaseBenchmark

public CacheAlternatives_Redis_Benchmark()
{
CacheTower = new CacheStack(new[] { new RedisCacheLayer(RedisHelper.GetConnection(), new RedisCacheLayerOptions(ProtobufCacheSerializer.Instance)) }, Array.Empty<ICacheExtension>());
CacheTower = new CacheStack(null, new[] { new RedisCacheLayer(RedisHelper.GetConnection(), new RedisCacheLayerOptions(ProtobufCacheSerializer.Instance)) }, Array.Empty<ICacheExtension>());
CacheManager = CacheFactory.Build<ProtobufCacheItem>(b =>
{
b.WithRedisConfiguration("redisLocal", "localhost:6379,ssl=false");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class CacheAlternatives_Redis_Parallel_Benchmark : BaseBenchmark

public CacheAlternatives_Redis_Parallel_Benchmark()
{
CacheTower = new CacheStack(new[] { new RedisCacheLayer(RedisHelper.GetConnection(), new RedisCacheLayerOptions(ProtobufCacheSerializer.Instance)) }, Array.Empty<ICacheExtension>());
CacheTower = new CacheStack(null, new[] { new RedisCacheLayer(RedisHelper.GetConnection(), new RedisCacheLayerOptions(ProtobufCacheSerializer.Instance)) }, Array.Empty<ICacheExtension>());
CacheManager = CacheFactory.Build<ProtobufCacheItem>(b =>
{
b.WithRedisConfiguration("redisLocal", "localhost:6379,ssl=false");
Expand Down
22 changes: 11 additions & 11 deletions benchmarks/CacheTower.Benchmarks/CacheStackBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public ConfigSettings()
[Benchmark]
public async Task Set()
{
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
await using (var cacheStack = new CacheStack(null, new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
{
for (var i = 0; i < WorkIterations; i++)
{
Expand All @@ -45,7 +45,7 @@ public async Task Set()
[Benchmark]
public async Task Set_TwoLayers()
{
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer(), new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
await using (var cacheStack = new CacheStack(null, new[] { new MemoryCacheLayer(), new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
{
for (var i = 0; i < WorkIterations; i++)
{
Expand All @@ -56,7 +56,7 @@ public async Task Set_TwoLayers()
[Benchmark]
public async Task Evict()
{
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
await using (var cacheStack = new CacheStack(null, new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
{
for (var i = 0; i < WorkIterations; i++)
{
Expand All @@ -68,7 +68,7 @@ public async Task Evict()
[Benchmark]
public async Task Evict_TwoLayers()
{
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer(), new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
await using (var cacheStack = new CacheStack(null, new[] { new MemoryCacheLayer(), new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
{
for (var i = 0; i < WorkIterations; i++)
{
Expand All @@ -80,7 +80,7 @@ public async Task Evict_TwoLayers()
[Benchmark]
public async Task Cleanup()
{
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
await using (var cacheStack = new CacheStack(null, new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
{
for (var i = 0; i < WorkIterations; i++)
{
Expand All @@ -92,7 +92,7 @@ public async Task Cleanup()
[Benchmark]
public async Task Cleanup_TwoLayers()
{
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer(), new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
await using (var cacheStack = new CacheStack(null, new[] { new MemoryCacheLayer(), new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
{
for (var i = 0; i < WorkIterations; i++)
{
Expand All @@ -104,7 +104,7 @@ public async Task Cleanup_TwoLayers()
[Benchmark]
public async Task GetMiss()
{
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
await using (var cacheStack = new CacheStack(null, new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
{
for (var i = 0; i < WorkIterations; i++)
{
Expand All @@ -115,7 +115,7 @@ public async Task GetMiss()
[Benchmark]
public async Task GetHit()
{
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
await using (var cacheStack = new CacheStack(null, new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
{
await cacheStack.SetAsync("GetHit", 15, TimeSpan.FromDays(1));

Expand All @@ -128,7 +128,7 @@ public async Task GetHit()
[Benchmark]
public async Task GetOrSet_NeverStale()
{
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
await using (var cacheStack = new CacheStack(null, new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
{
for (var i = 0; i < WorkIterations; i++)
{
Expand All @@ -142,7 +142,7 @@ await cacheStack.GetOrSetAsync<int>("GetOrSet", (old) =>
[Benchmark]
public async Task GetOrSet_AlwaysStale()
{
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
await using (var cacheStack = new CacheStack(null, new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
{
for (var i = 0; i < WorkIterations; i++)
{
Expand All @@ -156,7 +156,7 @@ await cacheStack.GetOrSetAsync<int>("GetOrSet", (old) =>
[Benchmark]
public async Task GetOrSet_UnderLoad()
{
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
await using (var cacheStack = new CacheStack(null, new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
{
await cacheStack.SetAsync("GetOrSet", new CacheEntry<int>(15, DateTime.UtcNow.AddDays(-1)));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public ConfigSettings()
protected virtual void SetupBenchmark() { }
protected virtual void CleanupBenchmark() { }

protected static CacheStack CacheStack { get; } = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>());
protected static CacheStack CacheStack { get; } = new CacheStack(null, new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>());

[GlobalSetup]
public void Setup()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using System.Text;
using Newtonsoft.Json;

Expand Down Expand Up @@ -32,16 +33,30 @@ public NewtonsoftJsonCacheSerializer(JsonSerializerSettings settings)
/// <inheritdoc />
public void Serialize<T>(Stream stream, T? value)
{
using var streamWriter = new StreamWriter(stream, Encoding.UTF8, 1024, true);
using var jsonWriter = new JsonTextWriter(streamWriter);
serializer.Serialize(jsonWriter, value);
try
{
using var streamWriter = new StreamWriter(stream, Encoding.UTF8, 1024, true);
using var jsonWriter = new JsonTextWriter(streamWriter);
serializer.Serialize(jsonWriter, value);
}
catch (Exception ex)
{
throw new CacheSerializationException("A serialization error has occurred when serializing with Newtonsoft.Json", ex);
}
}

/// <inheritdoc />
public T? Deserialize<T>(Stream stream)
{
using var streamReader = new StreamReader(stream, Encoding.UTF8, false, 1024);
using var jsonReader = new JsonTextReader(streamReader);
return serializer.Deserialize<T>(jsonReader);
try
{
using var streamReader = new StreamReader(stream, Encoding.UTF8, false, 1024);
using var jsonReader = new JsonTextReader(streamReader);
return serializer.Deserialize<T>(jsonReader);
}
catch (Exception ex)
{
throw new CacheSerializationException("A serialization error has occurred when deserializing with Newtonsoft.Json", ex);
}
}
}
29 changes: 22 additions & 7 deletions src/CacheTower.Serializers.Protobuf/ProtobufCacheSerializer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using CacheTower.Providers.FileSystem;
using ProtoBuf;
using ProtoBuf.Meta;
Expand All @@ -18,7 +19,7 @@ public class ProtobufCacheSerializer : ICacheSerializer
{
static ProtobufCacheSerializer()
{
RuntimeTypeModel.Default.Add<ManifestEntry>()
RuntimeTypeModel.Default.Add<ManifestEntry>(applyDefaultBehaviour: false)
.Add(1, nameof(ManifestEntry.FileName))
.Add(2, nameof(ManifestEntry.Expiry));
}
Expand All @@ -36,7 +37,7 @@ static SerializerConfig()
{
if (typeof(ICacheEntry).IsAssignableFrom(typeof(T)))
{
RuntimeTypeModel.Default.Add(typeof(T))
RuntimeTypeModel.Default.Add(typeof(T), applyDefaultBehaviour: false)
.Add(1, nameof(CacheEntry<object>.Expiry))
.Add(2, nameof(CacheEntry<object>.Value));
}
Expand All @@ -49,15 +50,29 @@ public static void EnsureConfigured() { }
/// <inheritdoc />
public void Serialize<T>(Stream stream, T? value)
{
SerializerConfig<T>.EnsureConfigured();
Serializer.Serialize(stream, value);
try
{
SerializerConfig<T>.EnsureConfigured();
Serializer.Serialize(stream, value);
}
catch (Exception ex)
{
throw new CacheSerializationException("A serialization error has occurred when serializing with ProtoBuf", ex);
}
}

/// <inheritdoc />
public T? Deserialize<T>(Stream stream)
{
SerializerConfig<T>.EnsureConfigured();
return Serializer.Deserialize<T>(stream);
try
{
SerializerConfig<T>.EnsureConfigured();
return Serializer.Deserialize<T>(stream);
}
catch (Exception ex)
{
throw new CacheSerializationException("A serialization error has occurred when deserializing with ProtoBuf", ex);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using System.Text.Json;

namespace CacheTower.Serializers.SystemTextJson;
Expand Down Expand Up @@ -28,12 +29,26 @@ public SystemTextJsonCacheSerializer(JsonSerializerOptions options)
/// <inheritdoc/>
public void Serialize<T>(Stream stream, T? value)
{
JsonSerializer.Serialize(stream, value, options);
try
{
JsonSerializer.Serialize(stream, value, options);
}
catch (Exception ex)
{
throw new CacheSerializationException("A serialization error has occurred when serializing with System.Text.Json", ex);
}
}

/// <inheritdoc/>
public T? Deserialize<T>(Stream stream)
{
return JsonSerializer.Deserialize<T>(stream, options);
try
{
return JsonSerializer.Deserialize<T>(stream, options);
}
catch (Exception ex)
{
throw new CacheSerializationException("A serialization error has occurred when deserializing with System.Text.Json", ex);
}
}
}
35 changes: 0 additions & 35 deletions src/CacheTower/CacheContextActivators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,39 +36,4 @@ public void Dispose()
{
ServiceScope.Dispose();
}
}


internal class FuncCacheContextActivator<TContext> : ICacheContextActivator
{
private readonly Func<TContext> Resolver;

public FuncCacheContextActivator(Func<TContext> resolver)
{
Resolver = resolver;
}

public ICacheContextScope BeginScope()
{
return new FuncCacheContextScope<TContext>(Resolver);
}
}

internal class FuncCacheContextScope<TContext> : ICacheContextScope
{
private readonly Func<TContext> Resolver;

public FuncCacheContextScope(Func<TContext> resolver)
{
Resolver = resolver;
}

public object Resolve(Type type)
{
return Resolver()!;
}

public void Dispose()
{
}
}
9 changes: 7 additions & 2 deletions src/CacheTower/CacheEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,20 @@ public sealed record CacheEntry<T>(T? Value, DateTime Expiry) : ICacheEntry<T>
/// <summary>
/// The cached value.
/// </summary>
public T? Value { get; } = Value;
public T? Value { get; init; } = Value;

/// <summary>
/// The expiry date for the cache entry.
/// </summary>
public DateTime Expiry { get; } = new DateTime(
public DateTime Expiry { get; init; } = new DateTime(
Expiry.Year, Expiry.Month, Expiry.Day, Expiry.Hour, Expiry.Minute, Expiry.Second, DateTimeKind.Utc
);

/// <summary>
/// Creates a new <see cref="ICacheEntry"/> with a default value.
/// </summary>
public CacheEntry() : this(default, DateTime.MinValue) { }

/// <summary>
/// Creates a new <see cref="ICacheEntry"/> with the given <paramref name="value"/> and an expiry adjusted to the <paramref name="timeToLive"/>.
/// </summary>
Expand Down
Loading