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

Support compression after serialization, fix #173 #319

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@ See [benchmarks results](https://github.com/MichaCo/CacheManager/blob/dev/Benchm
* **System.Web.Caching** based (included in the Web package)
* **Serialization** can now be configured.
Serialization is only needed in distributed caches. If no additional serialization package is installed and configured, Binary serialization will be used (if available)
After serialization the content can be compressed
The following are the currently available serialization options:
* Binary (build in if the full CLR is being used)
* **Json** based on the popular Newtonsoft.Json library
* **Json** with Gzip compression
* **Bond** based on Microsoft.Bond supporting all three available variants
* **DataContract** based on System.Runtime.Serialization library supporting binary, Json & Json with Gzip compression
* **DataContract** based on System.Runtime.Serialization library supporting binary, Json
* **Protocol Buffer** Google's protobuf. The package uses Mark's [protobuf-net](https://github.com/mgravell/protobuf-net) implementation.
* **Update values with lock or transaction** for distributed caches.
The interfaced provides a simple update method which internally ensures you work with the latest version.
Expand Down
8 changes: 7 additions & 1 deletion src/CacheManager.Core/CacheManagerConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ public CacheManagerConfiguration()
/// <value>The serializer activator.</value>
public Type SerializerType { get; set; }

/// <summary>
/// Gets or sets if after serializing/before deserializing should compress/decompress the data.
/// </summary>
/// <value>Should compress.</value>
public bool ShouldCompress { get; set; }

/// <summary>
/// Gets or sets additional arguments which should be used instantiating the serializer.
/// </summary>
Expand Down Expand Up @@ -127,4 +133,4 @@ public override string ToString()
return $"{Name}: {string.Join(", ", CacheHandleConfigurations)}";
}
}
}
}
18 changes: 18 additions & 0 deletions src/CacheManager.Core/Configuration/CacheManagerSection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ public sealed class CacheManagerHandleCollection : ConfigurationElementCollectio
private const string BackplaneNameKey = "backplaneName";
private const string BackplaneTypeKey = "backplaneType";
private const string SerializerTypeKey = "serializerType";
private const string ShouldCompressKey = "shouldCompress";
private const string EnablePerformanceCountersKey = "enablePerformanceCounters";
private const string EnableStatisticsKey = "enableStatistics";
private const string MaxRetriesKey = "maxRetries";
Expand Down Expand Up @@ -385,6 +386,23 @@ public string SerializerType
}
}

/// <summary>
/// Gets or sets if should compress after serialization.
/// </summary>
/// <value>The type of the serializer.</value>
[ConfigurationProperty(ShouldCompressKey, IsRequired = false)]
public bool ShouldCompress
{
get
{
return (bool)this[ShouldCompressKey];
}
set
{
this[ShouldCompressKey] = value;
}
}

/// <summary>
/// Gets or sets a value indicating whether performance counters should be enabled.
/// </summary>
Expand Down
11 changes: 11 additions & 0 deletions src/CacheManager.Core/Configuration/ConfigurationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@ internal static CacheManagerConfiguration LoadFromSection(CacheManagerSection se
cfg.BackplaneConfigurationKey = managerCfg.BackplaneName;
}

cfg.ShouldCompress = managerCfg.ShouldCompress;

// build serializer if set
if (!string.IsNullOrWhiteSpace(managerCfg.SerializerType))
{
Expand Down Expand Up @@ -712,6 +714,15 @@ public ConfigurationBuilderCachePart WithRetryTimeout(int timeoutMillis)
return this;
}

/// <summary>
/// Adds compression after serialization
/// </summary>
public ConfigurationBuilderCachePart WithCompression()
{
Configuration.ShouldCompress = true;
return this;
}

/// <summary>
/// Sets the update mode of the cache.
/// <para>If nothing is set, the default will be <c>CacheUpdateMode.None</c>.</para>
Expand Down
8 changes: 7 additions & 1 deletion src/CacheManager.Core/ICacheManagerConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ public interface IReadOnlyCacheManagerConfiguration
/// <value>The retry timeout.</value>
int RetryTimeout { get; }

/// <summary>
/// Gets or sets if after serializing/before deserializing should compress/decompress the data.
/// </summary>
/// <value>Should compress.</value>
bool ShouldCompress { get; }

/// <summary>
/// Gets the factory method for a cache serializer.
/// </summary>
Expand All @@ -119,4 +125,4 @@ public interface IReadOnlyCacheManagerConfiguration
/// <see cref="UpdateMode"/>
CacheUpdateMode UpdateMode { get; }
}
}
}
3 changes: 2 additions & 1 deletion src/CacheManager.Core/Internal/CacheReflectionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ internal static ICacheSerializer CreateSerializer(ICacheManagerConfiguration con
args = configuration.SerializerTypeArguments.Concat(args).ToArray();
}

return (ICacheSerializer)CreateInstance(configuration.SerializerType, args);
var serializer = (ICacheSerializer)CreateInstance(configuration.SerializerType, args);
return configuration.ShouldCompress ? new CompressionSerializer(serializer) : serializer;
}

return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,50 +1,59 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CacheManager.Core.Utility;
using Newtonsoft.Json;

namespace CacheManager.Serialization.Json
namespace CacheManager.Core.Internal
{
/// <summary>
/// Implements the <c>ICacheSerializer</c> contract using <c>Newtonsoft.Json</c> and the <see cref="GZipStream "/> loseless compression.
/// Wrapper for other serializer to add compression capabilities
/// </summary>
public class GzJsonCacheSerializer : JsonCacheSerializer
public class CompressionSerializer : ICacheSerializer
{
/// <summary>
/// Initializes a new instance of the <see cref="GzJsonCacheSerializer"/> class.
/// The serializer that we used after decompression and before compression.
/// </summary>
public GzJsonCacheSerializer()
: base(new JsonSerializerSettings(), new JsonSerializerSettings())
{
}
public ICacheSerializer InternalSerializer { get; }

/// <summary>
/// Initializes a new instance of the <see cref="GzJsonCacheSerializer"/> class.
/// With this overload the settings for de-/serialization can be set independently.
/// Initializes a new instance of the <see cref="CompressionSerializer"/> class.
/// </summary>
/// <param name="serializationSettings">The settings which should be used during serialization.</param>
/// <param name="deserializationSettings">The settings which should be used during deserialization.</param>
public GzJsonCacheSerializer(JsonSerializerSettings serializationSettings, JsonSerializerSettings deserializationSettings)
: base(serializationSettings, deserializationSettings)
/// <param name="internalSerializer">Serializer that we used after decompression and before compression.</param>
public CompressionSerializer(ICacheSerializer internalSerializer)
{
this.InternalSerializer = internalSerializer;
}

/// <inheritdoc/>
public CacheItem<T> DeserializeCacheItem<T>(byte[] value, Type valueType)
{
return InternalSerializer.DeserializeCacheItem<T>(value, valueType);
}

/// <inheritdoc/>
public byte[] SerializeCacheItem<T>(CacheItem<T> value)
{
return InternalSerializer.SerializeCacheItem<T>(value);
}

/// <inheritdoc/>
public override object Deserialize(byte[] data, Type target)
public object Deserialize(byte[] data, Type target)
{
Guard.NotNull(data, nameof(data));
var compressedData = Decompression(data);

return base.Deserialize(compressedData, target);
return InternalSerializer.Deserialize(compressedData, target);
}

/// <inheritdoc/>
public override byte[] Serialize<T>(T value)
public byte[] Serialize<T>(T value)
{
Guard.NotNull(value, nameof(value));
var data = base.Serialize<T>(value);
var data = InternalSerializer.Serialize<T>(value);

return Compression(data);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ public static class MicrosoftConfigurationExtensions
private const string ConfigurationName = "name";
private const string ConfigurationType = "type";
private const string ConfigurationKnownType = "knownType";
private const string ShouldCompress = "shouldCompress";
private const string TypeJsonCacheSerializer = "CacheManager.Serialization.Json.JsonCacheSerializer, CacheManager.Serialization.Json";
private const string TypeGzJsonCacheSerializer = "CacheManager.Serialization.Json.GzJsonCacheSerializer, CacheManager.Serialization.Json";
private const string TypeProtobufCacheSerializer = "CacheManager.Serialization.ProtoBuf.ProtoBufSerializer, CacheManager.Serialization.ProtoBuf";
private const string TypeBondCompactBinarySerializer = "CacheManager.Serialization.Bond.BondCompactBinaryCacheSerializer, CacheManager.Serialization.Bond";
private const string TypeBondFastBinarySerializer = "CacheManager.Serialization.Bond.BondFastBinaryCacheSerializer, CacheManager.Serialization.Bond";
Expand All @@ -40,6 +40,9 @@ public static class MicrosoftConfigurationExtensions
private const string TypeRedisConfigurations = "CacheManager.Redis.RedisConfigurations, CacheManager.StackExchange.Redis";
private const string KnonwSerializerBinary = "binary";
private const string KnonwSerializerJson = "json";
/// <summary>
/// absolete, available for backward compatibility
/// </summary>
private const string KnonwSerializerGzJson = "gzjson";
private const string KnonwSerializerProto = "protobuf";
private const string KnonwSerializerBondCompact = "bondcompactbinary";
Expand Down Expand Up @@ -429,7 +432,6 @@ private static Type GetKnownLoggerFactoryType(string knownTypeName, string path)
private static void GetSerializerConfiguration(CacheManagerConfiguration managerConfiguration, IConfigurationSection configuration)
{
var serializerSection = configuration.GetSection(SerializerSection);

if (serializerSection.GetChildren().Count() == 0)
{
// no serializer
Expand All @@ -439,6 +441,8 @@ private static void GetSerializerConfiguration(CacheManagerConfiguration manager
var knownType = serializerSection[ConfigurationKnownType];
var type = serializerSection[ConfigurationType];

managerConfiguration.ShouldCompress = serializerSection.GetValue<bool>(ShouldCompress);

if (string.IsNullOrWhiteSpace(knownType) && string.IsNullOrWhiteSpace(type))
{
throw new InvalidOperationException(
Expand All @@ -447,6 +451,10 @@ private static void GetSerializerConfiguration(CacheManagerConfiguration manager

if (string.IsNullOrWhiteSpace(type))
{
if (knownType.ToLowerInvariant() == KnonwSerializerGzJson)
{
managerConfiguration.ShouldCompress = true;
}
managerConfiguration.SerializerType = GetKnownSerializerType(knownType, serializerSection.Path);
}
else
Expand All @@ -473,8 +481,9 @@ private static Type GetKnownSerializerType(string knownTypeName, string path)
case KnonwSerializerJson:
return Type.GetType(TypeJsonCacheSerializer, true);

// KnonwSerializerGzJson is absolete, instead we will create TypeJsonCacheSerializer and add compression wrapper to it
case KnonwSerializerGzJson:
return Type.GetType(TypeGzJsonCacheSerializer, true);
return Type.GetType(TypeJsonCacheSerializer, true);

case KnonwSerializerProto:
return Type.GetType(TypeProtobufCacheSerializer, true);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Runtime.Serialization;
using System;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using CacheManager.Serialization.DataContract;
using static CacheManager.Core.Utility.Guard;
Expand Down Expand Up @@ -56,17 +57,19 @@ public static ConfigurationBuilderCachePart WithDataContractJsonSerializer(this
/// <param name="part">The configuration part.</param>
/// <param name="serializerSettings">Settings for the serializer.</param>
/// <returns>The builder instance.</returns>
[Obsolete("Use .WithDataContractJsonSerializer().WithCompression() instead")]
public static ConfigurationBuilderCachePart WithDataContractGzJsonSerializer(this ConfigurationBuilderCachePart part, DataContractJsonSerializerSettings serializerSettings = null)
{
NotNull(part, nameof(part));

part.WithCompression();
if (serializerSettings == null)
{
return part.WithSerializer(typeof(DataContractGzJsonCacheSerializer));
return part.WithSerializer(typeof(DataContractJsonCacheSerializer));
}
else
{
return part.WithSerializer(typeof(DataContractGzJsonCacheSerializer), serializerSettings);
return part.WithSerializer(typeof(DataContractJsonCacheSerializer), serializerSettings);
}
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CacheManager.Serialization.Json;
using System;
using CacheManager.Serialization.Json;
using Newtonsoft.Json;
using static CacheManager.Core.Utility.Guard;

Expand Down Expand Up @@ -40,11 +41,12 @@ public static ConfigurationBuilderCachePart WithJsonSerializer(this Configuratio
/// </summary>
/// <param name="part">The configuration part.</param>
/// <returns>The builder instance.</returns>
[Obsolete("Use .WithJsonSerializer().WithCompression() instead")]
public static ConfigurationBuilderCachePart WithGzJsonSerializer(this ConfigurationBuilderCachePart part)
{
NotNull(part, nameof(part));

return part.WithSerializer(typeof(GzJsonCacheSerializer));
part.WithCompression();
return part.WithSerializer(typeof(JsonCacheSerializer));
}

/// <summary>
Expand All @@ -54,11 +56,12 @@ public static ConfigurationBuilderCachePart WithGzJsonSerializer(this Configurat
/// <param name="serializationSettings">The settings to be used during serialization.</param>
/// <param name="deserializationSettings">The settings to be used during deserialization.</param>
/// <returns>The builder instance.</returns>
[Obsolete("Use .WithJsonSerializer().WithCompression() instead")]
public static ConfigurationBuilderCachePart WithGzJsonSerializer(this ConfigurationBuilderCachePart part, JsonSerializerSettings serializationSettings, JsonSerializerSettings deserializationSettings)
{
NotNull(part, nameof(part));

return part.WithSerializer(typeof(GzJsonCacheSerializer), serializationSettings, deserializationSettings);
part.WithCompression();
return part.WithSerializer(typeof(JsonCacheSerializer), serializationSettings, deserializationSettings);
}
}
}
}
6 changes: 3 additions & 3 deletions test/CacheManager.Benchmarks/SerializationBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class SerializationBenchmark
private Queue<CacheItem<TestPoco>> _payload;
private BinaryCacheSerializer _binary = new BinaryCacheSerializer();
private JsonCacheSerializer _json = new JsonCacheSerializer();
private GzJsonCacheSerializer _jsonGz = new GzJsonCacheSerializer();
private CompressionSerializer _jsonWithCompression = new CompressionSerializer(new JsonCacheSerializer());
private ProtoBufSerializer _proto = new Serialization.ProtoBuf.ProtoBufSerializer();
private BondCompactBinaryCacheSerializer _bondBinary = new BondCompactBinaryCacheSerializer(18000);
private BondFastBinaryCacheSerializer _bondFastBinary = new BondFastBinaryCacheSerializer(18000);
Expand Down Expand Up @@ -100,8 +100,8 @@ public void JsonGzSerializer()
{
ExecRun((item) =>
{
var data = _jsonGz.SerializeCacheItem(item);
var result = _jsonGz.DeserializeCacheItem<TestPoco>(data, _pocoType);
var data = _jsonWithCompression.SerializeCacheItem(item);
var result = _jsonWithCompression.DeserializeCacheItem<TestPoco>(data, _pocoType);
if (result == null)
{
throw new Exception();
Expand Down
Loading