Wip support for compressing/decompressing nucache documents on a per property basis. Option for compressing the properties in sql/nucache.db. Option for immediate/lazy decompression of properties. Mapping support for shorter property alias.
TODO: config file for property map TODO: HasValue and IsValue on propertyvalueconverterbase
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
{
|
||||
public class AppSettingsNucachePropertyMapFactory : INucachePropertyOptionsFactory
|
||||
{
|
||||
public NucachePropertyOptions GetNucachePropertyOptions()
|
||||
{
|
||||
NucachePropertyOptions options = new NucachePropertyOptions
|
||||
{
|
||||
PropertyMap = GetPropertyMap(),
|
||||
LZ4CompressionLevel = K4os.Compression.LZ4.LZ4Level.L10_OPT,
|
||||
MinimumCompressibleStringLength = null
|
||||
};
|
||||
return options;
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, (NucachePropertyCompressionLevel compress, NucachePropertyDecompressionLevel decompressionLevel, string mappedAlias)> GetPropertyMap()
|
||||
{
|
||||
var propertyMap = new Dictionary<string, (NucachePropertyCompressionLevel compress, NucachePropertyDecompressionLevel decompressionLevel, string mappedAlias)>();
|
||||
// TODO: Use xml/json/c# to define map
|
||||
var propertyDictionarySerializerMap = ConfigurationManager.AppSettings["Umbraco.Web.PublishedCache.NuCache.PropertySerializationMap"];
|
||||
if (!string.IsNullOrWhiteSpace(propertyDictionarySerializerMap))
|
||||
{
|
||||
//propertyAlias,CompressionLevel,DecompressionLevel,mappedAlias;
|
||||
propertyDictionarySerializerMap.Split(';')
|
||||
.Select(x =>
|
||||
{
|
||||
var y = x.Split(',');
|
||||
(string alias, NucachePropertyCompressionLevel compressionLevel, NucachePropertyDecompressionLevel decompressionLevel, string mappedAlias) v = (y[0],
|
||||
(NucachePropertyCompressionLevel)System.Enum.Parse(typeof(NucachePropertyCompressionLevel), y[1]),
|
||||
(NucachePropertyDecompressionLevel)System.Enum.Parse(typeof(NucachePropertyDecompressionLevel), y[2]),
|
||||
y[3]
|
||||
);
|
||||
return v;
|
||||
})
|
||||
.ToList().ForEach(x => propertyMap.Add(x.alias, (x.compressionLevel, x.decompressionLevel, x.mappedAlias)));
|
||||
}
|
||||
return propertyMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,17 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
{
|
||||
class ContentDataSerializer : ISerializer<ContentData>
|
||||
{
|
||||
public ContentDataSerializer(IDictionaryOfPropertyDataSerializer dictionaryOfPropertyDataSerializer = null)
|
||||
{
|
||||
_dictionaryOfPropertyDataSerializer = dictionaryOfPropertyDataSerializer;
|
||||
if(_dictionaryOfPropertyDataSerializer == null)
|
||||
{
|
||||
_dictionaryOfPropertyDataSerializer = PropertiesSerializer;
|
||||
}
|
||||
}
|
||||
private static readonly DictionaryOfPropertyDataSerializer PropertiesSerializer = new DictionaryOfPropertyDataSerializer();
|
||||
private static readonly DictionaryOfCultureVariationSerializer CultureVariationsSerializer = new DictionaryOfCultureVariationSerializer();
|
||||
private readonly IDictionaryOfPropertyDataSerializer _dictionaryOfPropertyDataSerializer;
|
||||
|
||||
public ContentData ReadFrom(Stream stream)
|
||||
{
|
||||
@@ -19,7 +28,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
VersionDate = PrimitiveSerializer.DateTime.ReadFrom(stream),
|
||||
WriterId = PrimitiveSerializer.Int32.ReadFrom(stream),
|
||||
TemplateId = PrimitiveSerializer.Int32.ReadFrom(stream),
|
||||
Properties = PropertiesSerializer.ReadFrom(stream), // TODO: We don't want to allocate empty arrays
|
||||
Properties = _dictionaryOfPropertyDataSerializer.ReadFrom(stream), // TODO: We don't want to allocate empty arrays
|
||||
CultureInfos = CultureVariationsSerializer.ReadFrom(stream) // TODO: We don't want to allocate empty arrays
|
||||
};
|
||||
}
|
||||
@@ -36,7 +45,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
{
|
||||
PrimitiveSerializer.Int32.WriteTo(value.TemplateId.Value, stream);
|
||||
}
|
||||
PropertiesSerializer.WriteTo(value.Properties, stream);
|
||||
_dictionaryOfPropertyDataSerializer.WriteTo(value.Properties, stream);
|
||||
CultureVariationsSerializer.WriteTo(value.CultureInfos, stream);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,17 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
{
|
||||
internal class ContentNodeKitSerializer : ISerializer<ContentNodeKit>
|
||||
{
|
||||
static readonly ContentDataSerializer DataSerializer = new ContentDataSerializer();
|
||||
public ContentNodeKitSerializer(ContentDataSerializer contentDataSerializer = null)
|
||||
{
|
||||
_contentDataSerializer = contentDataSerializer;
|
||||
if(_contentDataSerializer == null)
|
||||
{
|
||||
_contentDataSerializer = DefaultDataSerializer;
|
||||
}
|
||||
}
|
||||
static readonly ContentDataSerializer DefaultDataSerializer = new ContentDataSerializer();
|
||||
private readonly ContentDataSerializer _contentDataSerializer;
|
||||
|
||||
//static readonly ListOfIntSerializer ChildContentIdsSerializer = new ListOfIntSerializer();
|
||||
|
||||
public ContentNodeKit ReadFrom(Stream stream)
|
||||
@@ -26,10 +36,10 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
};
|
||||
var hasDraft = PrimitiveSerializer.Boolean.ReadFrom(stream);
|
||||
if (hasDraft)
|
||||
kit.DraftData = DataSerializer.ReadFrom(stream);
|
||||
kit.DraftData = _contentDataSerializer.ReadFrom(stream);
|
||||
var hasPublished = PrimitiveSerializer.Boolean.ReadFrom(stream);
|
||||
if (hasPublished)
|
||||
kit.PublishedData = DataSerializer.ReadFrom(stream);
|
||||
kit.PublishedData = _contentDataSerializer.ReadFrom(stream);
|
||||
return kit;
|
||||
}
|
||||
|
||||
@@ -47,11 +57,11 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
|
||||
PrimitiveSerializer.Boolean.WriteTo(value.DraftData != null, stream);
|
||||
if (value.DraftData != null)
|
||||
DataSerializer.WriteTo(value.DraftData, stream);
|
||||
_contentDataSerializer.WriteTo(value.DraftData, stream);
|
||||
|
||||
PrimitiveSerializer.Boolean.WriteTo(value.PublishedData != null, stream);
|
||||
if (value.PublishedData != null)
|
||||
DataSerializer.WriteTo(value.PublishedData, stream);
|
||||
_contentDataSerializer.WriteTo(value.PublishedData, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
{
|
||||
internal class DictionaryOfPropertyDataSerializer : SerializerBase, ISerializer<IDictionary<string, PropertyData[]>>
|
||||
internal class DictionaryOfPropertyDataSerializer : SerializerBase, ISerializer<IDictionary<string, PropertyData[]>>, IDictionaryOfPropertyDataSerializer
|
||||
{
|
||||
public IDictionary<string, PropertyData[]> ReadFrom(Stream stream)
|
||||
{
|
||||
|
||||
@@ -6,10 +6,10 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
{
|
||||
internal class BTree
|
||||
{
|
||||
public static BPlusTree<int, ContentNodeKit> GetTree(string filepath, bool exists)
|
||||
public static BPlusTree<int, ContentNodeKit> GetTree(string filepath, bool exists, ContentDataSerializer contentDataSerializer = null)
|
||||
{
|
||||
var keySerializer = new PrimitiveSerializer();
|
||||
var valueSerializer = new ContentNodeKitSerializer();
|
||||
var valueSerializer = new ContentNodeKitSerializer(contentDataSerializer);
|
||||
var options = new BPlusTree<int, ContentNodeKit>.OptionsV2(keySerializer, valueSerializer)
|
||||
{
|
||||
CreateFile = exists ? CreatePolicy.IfNeeded : CreatePolicy.Always,
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
{
|
||||
internal interface IDictionaryOfPropertyDataSerializer
|
||||
{
|
||||
IDictionary<string, PropertyData[]> ReadFrom(Stream stream);
|
||||
void WriteTo(IDictionary<string, PropertyData[]> value, Stream stream);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
{
|
||||
public interface INucachePropertyOptionsFactory
|
||||
{
|
||||
NucachePropertyOptions GetNucachePropertyOptions();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using K4os.Compression.LZ4;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
{
|
||||
public class LazyCompressedString
|
||||
{
|
||||
private byte[] _bytes;
|
||||
private string _str;
|
||||
|
||||
public LazyCompressedString(byte[] bytes)
|
||||
{
|
||||
_bytes = bytes;
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return LazyInitializer.EnsureInitialized(ref _str, () =>
|
||||
{
|
||||
var str = Encoding.UTF8.GetString(LZ4Pickler.Unpickle(_bytes));
|
||||
_bytes = null;
|
||||
return str;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using CSharpTest.Net.Serialization;
|
||||
using Umbraco.Core;
|
||||
using System.Text;
|
||||
using K4os.Compression.LZ4;
|
||||
|
||||
namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
{
|
||||
/// <summary>
|
||||
/// If/where to compress custom properties for nucache
|
||||
/// </summary>
|
||||
public enum NucachePropertyCompressionLevel
|
||||
{
|
||||
None = 0,
|
||||
SQLDatabase = 1,
|
||||
NucacheDatabase = 2
|
||||
}
|
||||
/// <summary>
|
||||
/// If/where to decompress custom properties for nucache
|
||||
/// </summary>
|
||||
public enum NucachePropertyDecompressionLevel
|
||||
{
|
||||
NotCompressed = 0,
|
||||
Immediate = 1,
|
||||
Lazy = 2
|
||||
}
|
||||
|
||||
|
||||
internal class Lz4DictionaryOfPropertyDataSerializer : SerializerBase, ISerializer<IDictionary<string, PropertyData[]>>, IDictionaryOfPropertyDataSerializer
|
||||
{
|
||||
private readonly IReadOnlyDictionary<string, (NucachePropertyCompressionLevel compress, NucachePropertyDecompressionLevel decompressionLevel, string mappedAlias)> _compressProperties;
|
||||
private readonly LZ4Level _compressionLevel;
|
||||
private readonly long? _minimumStringLengthForCompression;
|
||||
private readonly IReadOnlyDictionary<string, (NucachePropertyCompressionLevel compress, NucachePropertyDecompressionLevel decompressionLevel, string mappedAlias)> _uncompressProperties;
|
||||
|
||||
|
||||
public Lz4DictionaryOfPropertyDataSerializer(NucachePropertyOptions nucachePropertyOptions)
|
||||
{
|
||||
_compressProperties = nucachePropertyOptions.PropertyMap.ToList().ToDictionary(x => string.Intern(x.Value.mappedAlias), x => (x.Value.compress,x.Value.decompressionLevel, string.Intern(x.Value.mappedAlias)));
|
||||
_minimumStringLengthForCompression = nucachePropertyOptions.MinimumCompressibleStringLength;
|
||||
_uncompressProperties = _compressProperties.ToList().ToDictionary(x => x.Value.mappedAlias, x => (x.Value.compress, x.Value.decompressionLevel, x.Value.mappedAlias));
|
||||
|
||||
_compressionLevel = nucachePropertyOptions.LZ4CompressionLevel;
|
||||
}
|
||||
|
||||
|
||||
public IDictionary<string, PropertyData[]> ReadFrom(Stream stream)
|
||||
{
|
||||
var dict = new Dictionary<string, PropertyData[]>(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
// read properties count
|
||||
var pcount = PrimitiveSerializer.Int32.ReadFrom(stream);
|
||||
|
||||
// read each property
|
||||
for (var i = 0; i < pcount; i++)
|
||||
{
|
||||
|
||||
// read property alias
|
||||
var alias = PrimitiveSerializer.String.ReadFrom(stream);
|
||||
var map = GetDeSerializationMap(alias);
|
||||
var key = string.Intern(map.MappedAlias ?? alias);
|
||||
|
||||
// read values count
|
||||
var vcount = PrimitiveSerializer.Int32.ReadFrom(stream);
|
||||
|
||||
// create pdata and add to the dictionary
|
||||
var pdatas = new List<PropertyData>();
|
||||
|
||||
// for each value, read and add to pdata
|
||||
for (var j = 0; j < vcount; j++)
|
||||
{
|
||||
var pdata = new PropertyData();
|
||||
pdatas.Add(pdata);
|
||||
|
||||
// everything that can be null is read/written as object
|
||||
// even though - culture and segment should never be null here, as 'null' represents
|
||||
// the 'current' value, and string.Empty should be used to represent the invariant or
|
||||
// neutral values - PropertyData throws when getting nulls, so falling back to
|
||||
// string.Empty here - what else?
|
||||
pdata.Culture = ReadStringObject(stream, true) ?? string.Empty;
|
||||
pdata.Segment = ReadStringObject(stream, true) ?? string.Empty;
|
||||
pdata.Value = ReadObject(stream);
|
||||
|
||||
var decompressionLevel = NucachePropertyDecompressionLevel.Immediate;
|
||||
if ((map.Compress.Equals(NucachePropertyCompressionLevel.NucacheDatabase) || map.Compress.Equals(NucachePropertyCompressionLevel.SQLDatabase))
|
||||
&& pdata.Value != null && pdata.Value is byte[] byteArrayValue)
|
||||
{
|
||||
//Compressed string
|
||||
switch (decompressionLevel)
|
||||
{
|
||||
case NucachePropertyDecompressionLevel.Lazy:
|
||||
pdata.Value = new LazyCompressedString(byteArrayValue);
|
||||
break;
|
||||
case NucachePropertyDecompressionLevel.NotCompressed:
|
||||
break;//Shouldn't be any not compressed
|
||||
case NucachePropertyDecompressionLevel.Immediate:
|
||||
default:
|
||||
pdata.Value = Encoding.UTF8.GetString(LZ4Pickler.Unpickle(byteArrayValue));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dict[key] = pdatas.ToArray();
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
public void WriteTo(IDictionary<string, PropertyData[]> value, Stream stream)
|
||||
{
|
||||
// write properties count
|
||||
PrimitiveSerializer.Int32.WriteTo(value.Count, stream);
|
||||
|
||||
// write each property
|
||||
foreach (var (alias, values) in value)
|
||||
{
|
||||
var map = GetSerializationMap(alias);
|
||||
|
||||
// write alias
|
||||
PrimitiveSerializer.String.WriteTo(map.MappedAlias ?? alias, stream);
|
||||
|
||||
// write values count
|
||||
PrimitiveSerializer.Int32.WriteTo(values.Length, stream);
|
||||
|
||||
// write each value
|
||||
foreach (var pdata in values)
|
||||
{
|
||||
// everything that can be null is read/written as object
|
||||
// even though - culture and segment should never be null here,
|
||||
// see note in ReadFrom() method above
|
||||
WriteObject(pdata.Culture ?? string.Empty, stream);
|
||||
WriteObject(pdata.Segment ?? string.Empty, stream);
|
||||
|
||||
//Only compress strings
|
||||
if (pdata.Value is string stringValue && pdata.Value != null && map.Compress.Equals(NucachePropertyCompressionLevel.NucacheDatabase)
|
||||
&& (_minimumStringLengthForCompression == null
|
||||
|| !_minimumStringLengthForCompression.HasValue
|
||||
|| stringValue.Length > _minimumStringLengthForCompression.Value))
|
||||
{
|
||||
var stringBytes = Encoding.UTF8.GetBytes(stringValue);
|
||||
var compressedBytes = LZ4Pickler.Pickle(stringBytes, _compressionLevel);
|
||||
WriteObject(compressedBytes, stream);
|
||||
}
|
||||
WriteObject(pdata.Value, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
private readonly (NucachePropertyCompressionLevel Compress, NucachePropertyDecompressionLevel decompressionLevel, string MappedAlias) DEFAULT_MAP =(NucachePropertyCompressionLevel.None, NucachePropertyDecompressionLevel.NotCompressed, null);
|
||||
public (NucachePropertyCompressionLevel Compress, NucachePropertyDecompressionLevel decompressionLevel, string MappedAlias) GetSerializationMap(string propertyAlias)
|
||||
{
|
||||
if (_compressProperties == null)
|
||||
{
|
||||
return DEFAULT_MAP;
|
||||
}
|
||||
if (_compressProperties.TryGetValue(propertyAlias, out (NucachePropertyCompressionLevel compress, NucachePropertyDecompressionLevel decompressionLevel, string mappedAlias) map))
|
||||
{
|
||||
return map;
|
||||
}
|
||||
|
||||
return DEFAULT_MAP;
|
||||
}
|
||||
public (NucachePropertyCompressionLevel Compress, NucachePropertyDecompressionLevel decompressionLevel, string MappedAlias) GetDeSerializationMap(string propertyAlias)
|
||||
{
|
||||
if (_uncompressProperties == null)
|
||||
{
|
||||
return DEFAULT_MAP;
|
||||
}
|
||||
if (_uncompressProperties.TryGetValue(propertyAlias, out (NucachePropertyCompressionLevel compress, NucachePropertyDecompressionLevel decompressionLevel, string mappedAlias) map))
|
||||
{
|
||||
return map;
|
||||
}
|
||||
return DEFAULT_MAP;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,20 @@
|
||||
using MessagePack;
|
||||
using K4os.Compression.LZ4;
|
||||
using MessagePack;
|
||||
using MessagePack.Formatters;
|
||||
using MessagePack.Resolvers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
{
|
||||
internal class MsgPackContentNestedDataSerializer : IContentNestedDataByteSerializer
|
||||
{
|
||||
private MessagePackSerializerOptions _options;
|
||||
private readonly NucachePropertyOptions _propertyOptions;
|
||||
|
||||
public MsgPackContentNestedDataSerializer()
|
||||
public MsgPackContentNestedDataSerializer(NucachePropertyOptions propertyOptions = null)
|
||||
{
|
||||
var defaultOptions = ContractlessStandardResolver.Options;
|
||||
|
||||
@@ -30,6 +34,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
_options = defaultOptions
|
||||
.WithResolver(resolver)
|
||||
.WithCompression(MessagePackCompression.Lz4BlockArray);
|
||||
_propertyOptions = propertyOptions ?? new NucachePropertyOptions();
|
||||
}
|
||||
|
||||
public string ToJson(string serialized)
|
||||
@@ -48,10 +53,42 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
|
||||
public string Serialize(ContentNestedData nestedData)
|
||||
{
|
||||
Optimize(nestedData);
|
||||
|
||||
var bin = MessagePackSerializer.Serialize(nestedData, _options);
|
||||
return Convert.ToBase64String(bin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compress properties and map property names to shorter names
|
||||
/// </summary>
|
||||
/// <param name="nestedData"></param>
|
||||
private void Optimize(ContentNestedData nestedData)
|
||||
{
|
||||
if (_propertyOptions.PropertyMap != null && _propertyOptions.PropertyMap.Any())
|
||||
{
|
||||
foreach (var map in _propertyOptions.PropertyMap)
|
||||
{
|
||||
if (map.Value.compress.Equals(NucachePropertyCompressionLevel.SQLDatabase))
|
||||
{
|
||||
if (nestedData.PropertyData.TryGetValue(map.Key, out PropertyData[] properties))
|
||||
{
|
||||
foreach (var property in properties.Where(x => x.Value != null && x.Value is string))
|
||||
{
|
||||
property.Value = LZ4Pickler.Pickle(Encoding.UTF8.GetBytes(property.Value as string), _propertyOptions.LZ4CompressionLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (map.Value.mappedAlias != null && !map.Key.Equals(map.Value.mappedAlias)
|
||||
&& nestedData.PropertyData.Remove(map.Key) && nestedData.PropertyData.TryGetValue(map.Key, out PropertyData[] properties2))
|
||||
{
|
||||
nestedData.PropertyData.Remove(map.Key);
|
||||
nestedData.PropertyData.Add(map.Value.mappedAlias, properties2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ContentNestedData DeserializeBytes(byte[] data) => MessagePackSerializer.Deserialize<ContentNestedData>(data, _options);
|
||||
|
||||
public byte[] SerializeBytes(ContentNestedData nestedData) => MessagePackSerializer.Serialize(nestedData, _options);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
{
|
||||
public class NucachePropertyOptions
|
||||
{
|
||||
public IReadOnlyDictionary<string, (NucachePropertyCompressionLevel compress, NucachePropertyDecompressionLevel decompressionLevel,
|
||||
string mappedAlias)> PropertyMap
|
||||
{ get; set; } = new Dictionary<string, (NucachePropertyCompressionLevel compress,
|
||||
NucachePropertyDecompressionLevel decompressionLevel, string mappedAlias)>();
|
||||
|
||||
public K4os.Compression.LZ4.LZ4Level LZ4CompressionLevel { get; set; } = K4os.Compression.LZ4.LZ4Level.L00_FAST;
|
||||
|
||||
public long? MinimumCompressibleStringLength { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
private const char PrefixDouble = 'B';
|
||||
private const char PrefixDateTime = 'D';
|
||||
private const char PrefixByte = 'O';
|
||||
private const char PrefixByteArray = 'A';
|
||||
|
||||
protected string ReadString(Stream stream) => PrimitiveSerializer.String.ReadFrom(stream);
|
||||
protected int ReadInt(Stream stream) => PrimitiveSerializer.Int32.ReadFrom(stream);
|
||||
@@ -23,6 +24,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
protected float ReadFloat(Stream stream) => PrimitiveSerializer.Float.ReadFrom(stream);
|
||||
protected double ReadDouble(Stream stream) => PrimitiveSerializer.Double.ReadFrom(stream);
|
||||
protected DateTime ReadDateTime(Stream stream) => PrimitiveSerializer.DateTime.ReadFrom(stream);
|
||||
protected byte[] ReadByteArray(Stream stream) => PrimitiveSerializer.Bytes.ReadFrom(stream);
|
||||
|
||||
private T? ReadObject<T>(Stream stream, char t, Func<Stream, T> read)
|
||||
where T : struct
|
||||
@@ -39,7 +41,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
var type = PrimitiveSerializer.Char.ReadFrom(stream);
|
||||
if (type == PrefixNull) return null;
|
||||
if (type != PrefixString)
|
||||
throw new NotSupportedException($"Cannot deserialize type '{type}', expected 'S'.");
|
||||
throw new NotSupportedException($"Cannot deserialize type '{type}', expected '{PrefixString}'.");
|
||||
return intern
|
||||
? string.Intern(PrimitiveSerializer.String.ReadFrom(stream))
|
||||
: PrimitiveSerializer.String.ReadFrom(stream);
|
||||
@@ -51,6 +53,16 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
protected double? ReadDoubleObject(Stream stream) => ReadObject(stream, PrefixDouble, ReadDouble);
|
||||
protected DateTime? ReadDateTimeObject(Stream stream) => ReadObject(stream, PrefixDateTime, ReadDateTime);
|
||||
|
||||
protected byte[] ReadByteArrayObject(Stream stream) // required 'cos byte[] is not a struct
|
||||
{
|
||||
var type = PrimitiveSerializer.Char.ReadFrom(stream);
|
||||
if (type == PrefixNull) return null;
|
||||
if (type != PrefixByteArray)
|
||||
throw new NotSupportedException($"Cannot deserialize type '{type}', expected '{PrefixByteArray}'.");
|
||||
return PrimitiveSerializer.Bytes.ReadFrom(stream);
|
||||
}
|
||||
|
||||
|
||||
protected object ReadObject(Stream stream)
|
||||
=> ReadObject(PrimitiveSerializer.Char.ReadFrom(stream), stream);
|
||||
|
||||
@@ -81,6 +93,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
return PrimitiveSerializer.Double.ReadFrom(stream);
|
||||
case PrefixDateTime:
|
||||
return PrimitiveSerializer.DateTime.ReadFrom(stream);
|
||||
case PrefixByteArray:
|
||||
return PrimitiveSerializer.Bytes.ReadFrom(stream);
|
||||
default:
|
||||
throw new NotSupportedException($"Cannot deserialize unknown type '{type}'.");
|
||||
}
|
||||
@@ -137,6 +151,11 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
|
||||
PrimitiveSerializer.Char.WriteTo(PrefixUInt32, stream);
|
||||
PrimitiveSerializer.UInt32.WriteTo(uInt32Value, stream);
|
||||
}
|
||||
else if (value is byte[] byteArrayValue)
|
||||
{
|
||||
PrimitiveSerializer.Char.WriteTo(PrefixByteArray, stream);
|
||||
PrimitiveSerializer.Bytes.WriteTo(byteArrayValue, stream);
|
||||
}
|
||||
else
|
||||
throw new NotSupportedException("Value type " + value.GetType().FullName + " cannot be serialized.");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Configuration;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Web.PublishedCache.NuCache.DataSource;
|
||||
@@ -15,11 +17,26 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
|
||||
if (serializer == "MsgPack")
|
||||
{
|
||||
composition.Register<IContentNestedDataSerializer, MsgPackContentNestedDataSerializer>();
|
||||
var propertyDictionarySerializer = ConfigurationManager.AppSettings["Umbraco.Web.PublishedCache.NuCache.DictionaryOfPropertiesSerializer"];
|
||||
if (propertyDictionarySerializer == "LZ4Map")
|
||||
{
|
||||
composition.Register<INucachePropertyOptionsFactory, AppSettingsNucachePropertyMapFactory>();
|
||||
composition.Register(factory =>
|
||||
{
|
||||
var lz4Serializer = factory.GetInstance<Lz4DictionaryOfPropertyDataSerializer>();
|
||||
return new ContentDataSerializer(lz4Serializer);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
composition.Register(factory => new ContentDataSerializer(new DictionaryOfPropertyDataSerializer()));
|
||||
}
|
||||
composition.Register<IContentNestedDataSerializer, MsgPackContentNestedDataSerializer>();
|
||||
}
|
||||
else
|
||||
{
|
||||
composition.Register<IContentNestedDataSerializer, JsonContentNestedDataSerializer>();
|
||||
composition.Register(factory => new ContentDataSerializer(new DictionaryOfPropertyDataSerializer()));
|
||||
}
|
||||
|
||||
// register the NuCache database data source
|
||||
@@ -34,5 +51,6 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
// TODO: no NuCache health check yet
|
||||
//composition.HealthChecks().Add<NuCacheIntegrityHealthCheck>();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
private readonly IDefaultCultureAccessor _defaultCultureAccessor;
|
||||
private readonly UrlSegmentProviderCollection _urlSegmentProviders;
|
||||
private readonly IContentNestedDataSerializer _contentNestedDataSerializer;
|
||||
private readonly ContentDataSerializer _contentDataSerializer;
|
||||
|
||||
// volatile because we read it with no lock
|
||||
private volatile bool _isReady;
|
||||
@@ -83,7 +84,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
IDataSource dataSource, IGlobalSettings globalSettings,
|
||||
IEntityXmlSerializer entitySerializer,
|
||||
IPublishedModelFactory publishedModelFactory,
|
||||
UrlSegmentProviderCollection urlSegmentProviders, IContentNestedDataSerializer contentNestedDataSerializer)
|
||||
UrlSegmentProviderCollection urlSegmentProviders, IContentNestedDataSerializer contentNestedDataSerializer, ContentDataSerializer contentDataSerializer = null)
|
||||
: base(publishedSnapshotAccessor, variationContextAccessor)
|
||||
{
|
||||
//if (Interlocked.Increment(ref _singletonCheck) > 1)
|
||||
@@ -101,6 +102,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
_globalSettings = globalSettings;
|
||||
_urlSegmentProviders = urlSegmentProviders;
|
||||
_contentNestedDataSerializer = contentNestedDataSerializer;
|
||||
_contentDataSerializer = contentDataSerializer;
|
||||
|
||||
// we need an Xml serializer here so that the member cache can support XPath,
|
||||
// for members this is done by navigating the serialized-to-xml member
|
||||
@@ -182,8 +184,8 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
_localMediaDbExists = File.Exists(localMediaDbPath);
|
||||
|
||||
// if both local databases exist then GetTree will open them, else new databases will be created
|
||||
_localContentDb = BTree.GetTree(localContentDbPath, _localContentDbExists);
|
||||
_localMediaDb = BTree.GetTree(localMediaDbPath, _localMediaDbExists);
|
||||
_localContentDb = BTree.GetTree(localContentDbPath, _localContentDbExists, _contentDataSerializer);
|
||||
_localMediaDb = BTree.GetTree(localMediaDbPath, _localMediaDbExists, _contentDataSerializer);
|
||||
|
||||
_logger.Info<PublishedSnapshotService>("Registered with MainDom, localContentDbExists? {LocalContentDbExists}, localMediaDbExists? {LocalMediaDbExists}", _localContentDbExists, _localMediaDbExists);
|
||||
}
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
<PackageReference Include="ImageProcessor">
|
||||
<Version>2.7.0.100</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="K4os.Compression.LZ4">
|
||||
<Version>1.1.11</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="LightInject" Version="5.4.0" />
|
||||
<PackageReference Include="LightInject.Annotation" Version="1.1.0" />
|
||||
<PackageReference Include="LightInject.Mvc" Version="2.0.0" />
|
||||
@@ -248,9 +251,15 @@
|
||||
<Compile Include="Compose\NestedContentPropertyComposer.cs" />
|
||||
<Compile Include="PropertyEditors\ParameterEditors\MultipleMediaPickerParameterEditor.cs" />
|
||||
<Compile Include="PropertyEditors\RichTextEditorPastedImages.cs" />
|
||||
<Compile Include="PublishedCache\NuCache\DataSource\AppSettingsNucachePropertyMapFactory.cs" />
|
||||
<Compile Include="PublishedCache\NuCache\DataSource\IContentNestedDataSerializer.cs" />
|
||||
<Compile Include="PublishedCache\NuCache\DataSource\IDictionaryOfPropertyDataSerializer.cs" />
|
||||
<Compile Include="PublishedCache\NuCache\DataSource\INucachePropertyOptionsFactory.cs" />
|
||||
<Compile Include="PublishedCache\NuCache\DataSource\JsonContentNestedDataSerializer.cs" />
|
||||
<Compile Include="PublishedCache\NuCache\DataSource\LazyCompressedString.cs" />
|
||||
<Compile Include="PublishedCache\NuCache\DataSource\MsgPackContentNestedDataSerializer.cs" />
|
||||
<Compile Include="PublishedCache\NuCache\DataSource\Lz4DictionaryOfPropertyDataSerializer.cs" />
|
||||
<Compile Include="PublishedCache\NuCache\DataSource\NucachePropertyOptions.cs" />
|
||||
<Compile Include="PublishedCache\NuCache\PublishedSnapshotServiceOptions.cs" />
|
||||
<Compile Include="PublishedCache\NuCache\Snap\GenObj.cs" />
|
||||
<Compile Include="PublishedCache\NuCache\Snap\GenRef.cs" />
|
||||
|
||||
Reference in New Issue
Block a user