1
0
mirror of https://github.com/mxpv/podsync.git synced 2024-05-11 05:55:04 +00:00

Rework Redis serialization

This commit is contained in:
Maksym Pavlenko
2017-01-06 17:47:42 -08:00
parent 9164833f4b
commit ab206fb09d
4 changed files with 91 additions and 55 deletions

View File

@@ -1,7 +1,11 @@
namespace Podsync.Services
using Podsync.Services.Resolver;
namespace Podsync.Services
{
public static class Constants
{
public const int DefaultPageSize = 50;
public const ResolveType DefaultFormat = ResolveType.VideoHigh;
}
}

View File

@@ -3,11 +3,11 @@ using Podsync.Services.Resolver;
namespace Podsync.Services.Storage
{
public struct FeedMetadata
public class FeedMetadata
{
public Provider Provider { get; set; }
public LinkType LinkType { get; set; }
public LinkType Type { get; set; }
public string Id { get; set; }
@@ -16,5 +16,12 @@ namespace Podsync.Services.Storage
public int PageSize { get; set; }
public override string ToString() => $"{Provider} ({LinkType}) {Id}";
// Workaround for backward compatibility
public LinkType LinkType
{
get { return Type; }
set { Type = value; }
}
}
}

View File

@@ -1,13 +1,14 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using HashidsNet;
using Microsoft.Extensions.Options;
using Podsync.Services.Links;
using Podsync.Services.Resolver;
using StackExchange.Redis;
namespace Podsync.Services.Storage
@@ -18,16 +19,6 @@ namespace Podsync.Services.Storage
private const string IdSalt = "65fce519433f4218aa0cee6394225eea";
private const int IdLength = 4;
// Store all fields manually for backward compatibility with existing implementation
private const string ProviderField = "provider";
private const string TypeField = "type";
private const string IdField = "id";
private const string QualityField = "quality";
private const string PageSizeField = "pageSize";
private const ResolveType DefaultQuality = ResolveType.VideoHigh;
private const int DefaultPageSize = 50;
private static readonly IHashids HashIds = new Hashids(IdSalt, IdLength);
private readonly string _cs;
@@ -85,13 +76,21 @@ namespace Podsync.Services.Storage
{
var id = await MakeId();
if (await Db.KeyExistsAsync(id))
{
throw new InvalidOperationException("Failed to generate feed id");
}
await Db.HashSetAsync(id, new[]
{
new HashEntry(ProviderField, metadata.Provider.ToString()),
new HashEntry(TypeField, metadata.LinkType.ToString()),
new HashEntry(IdField, metadata.Id),
new HashEntry(QualityField, metadata.Quality.ToString()),
new HashEntry(PageSizeField, metadata.PageSize),
// V1
new HashEntry(nameof(metadata.Provider), metadata.Provider.ToString()),
new HashEntry(nameof(metadata.Type), metadata.Type.ToString()),
new HashEntry(nameof(metadata.Id), metadata.Id),
// V2
new HashEntry(nameof(metadata.Quality), metadata.Quality.ToString()),
new HashEntry(nameof(metadata.PageSize), metadata.PageSize),
});
await Db.KeyExpireAsync(id, TimeSpan.FromDays(1));
@@ -116,42 +115,71 @@ namespace Podsync.Services.Storage
throw new KeyNotFoundException("Invaid key");
}
var metadata = new FeedMetadata
{
Id = entries.Single(x => x.Name == IdField).Value,
LinkType = ToEnum<LinkType>(entries.Single(x => x.Name == TypeField)),
Provider = ToEnum<Provider>(entries.Single(x => x.Name == ProviderField)),
};
var metadata = new FeedMetadata();
if (entries.Length > 3)
{
metadata.Quality = ToEnum<ResolveType>(entries.Single(x => x.Name == QualityField));
metadata.PageSize = (int)entries.Single(x => x.Name == PageSizeField).Value;
}
else
{
// Set default values
metadata.Quality = DefaultQuality;
metadata.PageSize = DefaultPageSize;
}
// V1
SetProperty(metadata, x => x.Id, entries);
SetProperty(metadata, x => x.Type, entries);
SetProperty(metadata, x => x.Provider, entries);
// V2
SetProperty(metadata, x => x.Quality, entries, Constants.DefaultFormat);
SetProperty(metadata, x => x.PageSize, entries, Constants.DefaultPageSize);
return metadata;
}
public Task ResetCounter()
{
return Db.KeyDeleteAsync(IdKey);
}
public async Task<string> MakeId()
{
var id = await Db.StringIncrementAsync(IdKey);
return HashIds.EncodeLong(id);
}
private static T ToEnum<T>(HashEntry key)
private static void SetProperty<T, P>(T target, Expression<Func<T, P>> memberLamda, HashEntry[] entries)
{
return (T)Enum.Parse(typeof(T), key.Value, true);
SetProperty(target, memberLamda, entries, default(P), true);
}
private static void SetProperty<T, P>(T target, Expression<Func<T, P>> memberLamda, HashEntry[] entries, P fallback, bool throwIfMissing = false)
{
var memberExpression = memberLamda.Body as MemberExpression;
// Get property name via reflection
var entryName = memberExpression?.Member?.Name;
if (string.IsNullOrEmpty(entryName))
{
throw new InvalidOperationException("Wrong property expression");
}
P value;
// RedisValue is value type
if (entries.Any(x => string.Equals(x.Name, entryName, StringComparison.OrdinalIgnoreCase)))
{
var entry = entries.Single(x => string.Equals(x.Name, entryName, StringComparison.OrdinalIgnoreCase));
var propertyType = typeof(P);
if (propertyType.GetTypeInfo().IsEnum)
{
value = (P)Enum.Parse(propertyType, entry.Value);
}
else
{
value = (P)Convert.ChangeType(entry.Value, propertyType);
}
}
else
{
if (throwIfMissing)
{
throw new InvalidDataException("Missing mandatory property");
}
value = fallback;
}
var property = memberExpression.Member as PropertyInfo;
property?.SetValue(target, value);
}
}
}

View File

@@ -29,17 +29,11 @@ namespace Podsync.Tests.Services.Storage
{
const int idCount = 50;
try
{
var results = new string[idCount];
Parallel.For(0, results.Length, (i, _) => results[i] = _storage.MakeId().GetAwaiter().GetResult());
Assert.Equal(results.Length, results.Distinct().Count());
}
finally
{
_storage.ResetCounter();
}
var results = new string[idCount];
Parallel.For(0, results.Length, (i, _) => results[i] = _storage.MakeId().GetAwaiter().GetResult());
Assert.Equal(results.Length, results.Distinct().Count());
}
[Fact]
@@ -49,7 +43,9 @@ namespace Podsync.Tests.Services.Storage
{
Id = "123",
LinkType = LinkType.Channel,
Provider = Provider.Vimeo
Provider = Provider.Vimeo,
PageSize = 45
};
var id = await _storage.Save(feed);
@@ -61,6 +57,7 @@ namespace Podsync.Tests.Services.Storage
Assert.Equal(feed.Id, loaded.Id);
Assert.Equal(feed.LinkType, loaded.LinkType);
Assert.Equal(feed.Provider, loaded.Provider);
Assert.Equal(45, loaded.PageSize);
}
[Fact]