diff --git a/src/Orchard.Web/Modules/Orchard.OutputCache/Models/CacheItem.cs b/src/Orchard.Web/Modules/Orchard.OutputCache/Models/CacheItem.cs
index dd0c1d0b3..555a19d6c 100644
--- a/src/Orchard.Web/Modules/Orchard.OutputCache/Models/CacheItem.cs
+++ b/src/Orchard.Web/Modules/Orchard.OutputCache/Models/CacheItem.cs
@@ -3,6 +3,9 @@
namespace Orchard.OutputCache.Models {
[Serializable]
public class CacheItem {
+ // used for serialization compatibility
+ public static readonly string Version = "1";
+
public DateTime CachedOnUtc { get; set; }
public int Duration { get; set; }
public int GraceTime { get; set; }
diff --git a/src/Orchard.Web/Modules/Orchard.Redis/OutputCache/RedisOutputCacheStorageProvider.cs b/src/Orchard.Web/Modules/Orchard.Redis/OutputCache/RedisOutputCacheStorageProvider.cs
index daa6ae902..8b1edbc3d 100644
--- a/src/Orchard.Web/Modules/Orchard.Redis/OutputCache/RedisOutputCacheStorageProvider.cs
+++ b/src/Orchard.Web/Modules/Orchard.Redis/OutputCache/RedisOutputCacheStorageProvider.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using Newtonsoft.Json;
using Orchard.Environment.Configuration;
using Orchard.Environment.Extensions;
using Orchard.Logging;
@@ -10,11 +9,23 @@ using Orchard.OutputCache.Models;
using Orchard.OutputCache.Services;
using Orchard.Redis.Extensions;
using StackExchange.Redis;
+using System.Runtime.Serialization.Formatters.Binary;
+using System.IO;
+using System.IO.Compression;
namespace Orchard.Redis.OutputCache {
[OrchardFeature("Orchard.Redis.OutputCache")]
[OrchardSuppressDependency("Orchard.OutputCache.Services.DefaultCacheStorageProvider")]
+ ///
+ /// This implementation stores a instance to a Redis server.
+ /// The item is serialized using a and GZipped. We rely
+ /// on compression at this level of the implementation as other output cache providers
+ /// might not want to rely on it, or transform the data to binary. The content is compressed
+ /// as HTML pages can be consequent, like several hundreds of KB, and the network be clogged.
+ /// To prevent versioning issues with serialized data, the Redis keys contain the
+ /// property.
+ ///
public class RedisOutputCacheStorageProvider : IOutputCacheStorageProvider {
private readonly ShellSettings _shellSettings;
@@ -43,12 +54,19 @@ namespace Orchard.Redis.OutputCache {
}
public void Set(string key, CacheItem cacheItem) {
+ if(cacheItem == null) {
+ throw new ArgumentNullException("cacheItem");
+ }
+
if (cacheItem.ValidFor <= 0) {
return;
}
- var value = JsonConvert.SerializeObject(cacheItem);
- Database.StringSet(GetLocalizedKey(key), value, TimeSpan.FromSeconds(cacheItem.ValidFor));
+ using (var decompressedStream = Serialize(cacheItem)) {
+ using (var compressedStream = Compress(decompressedStream)) {
+ Database.StringSet(GetLocalizedKey(key), compressedStream.ToArray(), TimeSpan.FromSeconds(cacheItem.ValidFor));
+ }
+ }
}
public void Remove(string key) {
@@ -60,12 +78,21 @@ namespace Orchard.Redis.OutputCache {
}
public CacheItem GetCacheItem(string key) {
- string value = Database.StringGet(GetLocalizedKey(key));
- if (String.IsNullOrEmpty(value)) {
+ var value = Database.StringGet(GetLocalizedKey(key));
+
+ if (value.IsNullOrEmpty) {
return null;
}
- return JsonConvert.DeserializeObject(value);
+ using (var compressedStream = new MemoryStream(value)) {
+ if(compressedStream.Length == 0) {
+ return null;
+ }
+
+ using(var decompressedStream = Decompress(compressedStream)) {
+ return Deserialize(decompressedStream);
+ }
+ }
}
public IEnumerable GetCacheItems(int skip, int count) {
@@ -88,7 +115,7 @@ namespace Orchard.Redis.OutputCache {
/// The key to localized.
/// A localized key based on the tenant name.
private string GetLocalizedKey(string key) {
- return _shellSettings.Name + ":OutputCache:" + key;
+ return _shellSettings.Name + ":OC:" + CacheItem.Version + ":" + key;
}
///
@@ -111,5 +138,37 @@ namespace Orchard.Redis.OutputCache {
return _keysCache;
}
+
+ private static MemoryStream Serialize(CacheItem item) {
+ BinaryFormatter binaryFormatter = new BinaryFormatter();
+ var memoryStream = new MemoryStream();
+ binaryFormatter.Serialize(memoryStream, item);
+ memoryStream.Seek(0, SeekOrigin.Begin);
+ return memoryStream;
+ }
+
+ private static CacheItem Deserialize(Stream stream) {
+ BinaryFormatter binaryFormatter = new BinaryFormatter();
+ var result = (CacheItem)binaryFormatter.Deserialize(stream);
+ return result;
+ }
+
+ private static MemoryStream Compress(Stream stream) {
+ var compressedStream = new MemoryStream();
+ using (var compressionStream = new GZipStream(compressedStream, CompressionMode.Compress)) {
+ stream.CopyTo(compressionStream);
+ return compressedStream;
+ }
+ }
+
+ private static Stream Decompress(Stream stream) {
+ var decompressedStream = new MemoryStream();
+ using (GZipStream decompressionStream = new GZipStream(stream, CompressionMode.Decompress)) {
+ decompressionStream.CopyTo(decompressedStream);
+ decompressedStream.Seek(0, SeekOrigin.Begin);
+ return decompressedStream;
+ }
+
+ }
}
}
\ No newline at end of file