removed excess hashing in providers and made user data key-based

This commit is contained in:
Luke Pulverenti
2013-04-13 14:02:30 -04:00
parent 6688d35e65
commit 785deff188
54 changed files with 512 additions and 678 deletions

View File

@@ -101,6 +101,8 @@ namespace MediaBrowser.Server.Implementations.Library
/// </summary>
private readonly IUserManager _userManager;
private readonly IUserDataRepository _userDataRepository;
/// <summary>
/// Gets or sets the configuration manager.
/// </summary>
@@ -136,12 +138,14 @@ namespace MediaBrowser.Server.Implementations.Library
/// <param name="taskManager">The task manager.</param>
/// <param name="userManager">The user manager.</param>
/// <param name="configurationManager">The configuration manager.</param>
public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager)
/// <param name="userDataRepository">The user data repository.</param>
public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataRepository userDataRepository)
{
_logger = logger;
_taskManager = taskManager;
_userManager = userManager;
ConfigurationManager = configurationManager;
_userDataRepository = userDataRepository;
ByReferenceItems = new ConcurrentDictionary<Guid, BaseItem>();
ConfigurationManager.ConfigurationUpdated += ConfigurationUpdated;
@@ -903,6 +907,7 @@ namespace MediaBrowser.Server.Implementations.Library
userComparer.User = user;
userComparer.UserManager = _userManager;
userComparer.UserDataRepository = _userDataRepository;
return userComparer;
}

View File

@@ -1,6 +1,7 @@
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
@@ -86,20 +87,14 @@ namespace MediaBrowser.Server.Implementations.Library
/// </summary>
private readonly ILogger _logger;
private readonly IUserDataRepository _userDataRepository;
/// <summary>
/// Gets or sets the configuration manager.
/// </summary>
/// <value>The configuration manager.</value>
private IServerConfigurationManager ConfigurationManager { get; set; }
private readonly ConcurrentDictionary<string, Task<UserItemData>> _userData = new ConcurrentDictionary<string, Task<UserItemData>>();
/// <summary>
/// Gets the active user data repository
/// </summary>
/// <value>The user data repository.</value>
public IUserDataRepository UserDataRepository { get; set; }
/// <summary>
/// Gets the active user repository
/// </summary>
@@ -321,13 +316,13 @@ namespace MediaBrowser.Server.Implementations.Library
var connection = _activeConnections.GetOrAdd(key, keyName => new ClientConnectionInfo
{
UserId = userId,
UserId = userId.ToString(),
Client = clientType,
DeviceName = deviceName,
DeviceId = deviceId
});
connection.UserId = userId;
connection.UserId = userId.ToString();
return connection;
}
@@ -591,12 +586,14 @@ namespace MediaBrowser.Server.Implementations.Library
UpdateNowPlayingItemId(user, clientType, deviceId, deviceName, item, positionTicks);
var key = item.GetUserDataKey();
if (positionTicks.HasValue)
{
var data = await GetUserData(user.Id, item.UserDataId).ConfigureAwait(false);
var data = await _userDataRepository.GetUserData(user.Id, key).ConfigureAwait(false);
UpdatePlayState(item, data, positionTicks.Value, false);
await SaveUserData(user.Id, item.UserDataId, data, CancellationToken.None).ConfigureAwait(false);
await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false);
}
EventHelper.QueueEventIfNotNull(PlaybackProgress, this, new PlaybackProgressEventArgs
@@ -631,7 +628,9 @@ namespace MediaBrowser.Server.Implementations.Library
RemoveNowPlayingItemId(user, clientType, deviceId, deviceName, item);
var data = await GetUserData(user.Id, item.UserDataId).ConfigureAwait(false);
var key = item.GetUserDataKey();
var data = await _userDataRepository.GetUserData(user.Id, key).ConfigureAwait(false);
if (positionTicks.HasValue)
{
@@ -644,7 +643,7 @@ namespace MediaBrowser.Server.Implementations.Library
data.Played = true;
}
await SaveUserData(user.Id, item.UserDataId, data, CancellationToken.None).ConfigureAwait(false);
await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false);
EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackProgressEventArgs
{
@@ -703,59 +702,5 @@ namespace MediaBrowser.Server.Implementations.Library
data.LastPlayedDate = DateTime.UtcNow;
}
}
/// <summary>
/// Saves display preferences for an item
/// </summary>
/// <param name="userId">The user id.</param>
/// <param name="userDataId">The user data id.</param>
/// <param name="userData">The user data.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public async Task SaveUserData(Guid userId, Guid userDataId, UserItemData userData, CancellationToken cancellationToken)
{
var key = userId + userDataId.ToString();
try
{
await UserDataRepository.SaveUserData(userId, userDataId, userData, cancellationToken).ConfigureAwait(false);
var newValue = Task.FromResult(userData);
// Once it succeeds, put it into the dictionary to make it available to everyone else
_userData.AddOrUpdate(key, newValue, delegate { return newValue; });
}
catch (Exception ex)
{
_logger.ErrorException("Error saving user data", ex);
throw;
}
}
/// <summary>
/// Gets the user data.
/// </summary>
/// <param name="userId">The user id.</param>
/// <param name="userDataId">The user data id.</param>
/// <returns>Task{UserItemData}.</returns>
public Task<UserItemData> GetUserData(Guid userId, Guid userDataId)
{
var key = userId + userDataId.ToString();
return _userData.GetOrAdd(key, keyName => RetrieveUserData(userId, userDataId));
}
/// <summary>
/// Retrieves the user data.
/// </summary>
/// <param name="userId">The user id.</param>
/// <param name="userDataId">The user data id.</param>
/// <returns>Task{UserItemData}.</returns>
private async Task<UserItemData> RetrieveUserData(Guid userId, Guid userDataId)
{
var userdata = await UserDataRepository.GetUserData(userId, userDataId).ConfigureAwait(false);
return userdata ?? new UserItemData();
}
}
}

View File

@@ -88,12 +88,6 @@ namespace MediaBrowser.Server.Implementations.Providers
Task.Run(() => ValidateCurrentlyRunningProviders());
}
/// <summary>
/// Gets or sets the supported providers key.
/// </summary>
/// <value>The supported providers key.</value>
private Guid SupportedProvidersKey { get; set; }
/// <summary>
/// Adds the metadata providers.
/// </summary>
@@ -103,6 +97,11 @@ namespace MediaBrowser.Server.Implementations.Providers
MetadataProviders = providers.OrderBy(e => e.Priority).ToArray();
}
/// <summary>
/// The _supported providers key
/// </summary>
private readonly Guid _supportedProvidersKey = "SupportedProviders".GetMD5();
/// <summary>
/// Runs all metadata providers for an entity, and returns true or false indicating if at least one was refreshed and requires persistence
/// </summary>
@@ -126,19 +125,14 @@ namespace MediaBrowser.Server.Implementations.Providers
BaseProviderInfo supportedProvidersInfo;
if (SupportedProvidersKey == Guid.Empty)
{
SupportedProvidersKey = "SupportedProviders".GetMD5();
}
var supportedProvidersValue = string.Join("+", supportedProviders.Select(i => i.GetType().Name));
var providersChanged = false;
var supportedProvidersHash = string.Join("+", supportedProviders.Select(i => i.GetType().Name)).GetMD5();
bool providersChanged = false;
item.ProviderData.TryGetValue(SupportedProvidersKey, out supportedProvidersInfo);
item.ProviderData.TryGetValue(_supportedProvidersKey, out supportedProvidersInfo);
if (supportedProvidersInfo != null)
{
// Force refresh if the supported providers have changed
providersChanged = force = force || supportedProvidersInfo.FileSystemStamp != supportedProvidersHash;
providersChanged = force = force || !string.Equals(supportedProvidersInfo.FileSystemStamp, supportedProvidersValue);
// If providers have changed, clear provider info and update the supported providers hash
if (providersChanged)
@@ -150,7 +144,7 @@ namespace MediaBrowser.Server.Implementations.Providers
if (providersChanged)
{
supportedProvidersInfo.FileSystemStamp = supportedProvidersHash;
supportedProvidersInfo.FileSystemStamp = supportedProvidersValue;
}
if (force) item.ClearMetaValues();
@@ -206,7 +200,7 @@ namespace MediaBrowser.Server.Implementations.Providers
if (providersChanged)
{
item.ProviderData[SupportedProvidersKey] = supportedProvidersInfo;
item.ProviderData[_supportedProvidersKey] = supportedProvidersInfo;
}
return result || providersChanged;

View File

@@ -1,5 +1,6 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
@@ -23,6 +24,12 @@ namespace MediaBrowser.Server.Implementations.Sorting
/// <value>The user manager.</value>
public IUserManager UserManager { get; set; }
/// <summary>
/// Gets or sets the user data repository.
/// </summary>
/// <value>The user data repository.</value>
public IUserDataRepository UserDataRepository { get; set; }
/// <summary>
/// Compares the specified x.
/// </summary>
@@ -41,7 +48,7 @@ namespace MediaBrowser.Server.Implementations.Sorting
/// <returns>DateTime.</returns>
private DateTime GetDate(BaseItem x)
{
var userdata = UserManager.GetUserData(User.Id, x.UserDataId).Result;
var userdata = UserDataRepository.GetUserData(User.Id, x.GetUserDataKey()).Result;
if (userdata != null && userdata.LastPlayedDate.HasValue)
{

View File

@@ -1,5 +1,6 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
@@ -34,7 +35,7 @@ namespace MediaBrowser.Server.Implementations.Sorting
/// <returns>DateTime.</returns>
private int GetValue(BaseItem x)
{
var userdata = UserManager.GetUserData(User.Id, x.UserDataId).Result;
var userdata = UserDataRepository.GetUserData(User.Id, x.GetUserDataKey()).Result;
return userdata == null ? 0 : userdata.PlayCount;
}
@@ -48,6 +49,12 @@ namespace MediaBrowser.Server.Implementations.Sorting
get { return ItemSortBy.PlayCount; }
}
/// <summary>
/// Gets or sets the user data repository.
/// </summary>
/// <value>The user data repository.</value>
public IUserDataRepository UserDataRepository { get; set; }
/// <summary>
/// Gets or sets the user manager.
/// </summary>

View File

@@ -14,7 +14,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// <summary>
/// Class SQLiteDisplayPreferencesRepository
/// </summary>
class SQLiteDisplayPreferencesRepository : SqliteRepository, IDisplayPreferencesRepository
public class SQLiteDisplayPreferencesRepository : SqliteRepository, IDisplayPreferencesRepository
{
/// <summary>
/// The repository name

View File

@@ -4,6 +4,7 @@ using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using System;
using System.Collections.Concurrent;
using System.Data;
using System.IO;
using System.Threading;
@@ -16,6 +17,8 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// </summary>
public class SQLiteUserDataRepository : SqliteRepository, IUserDataRepository
{
private readonly ConcurrentDictionary<string, Task<UserItemData>> _userData = new ConcurrentDictionary<string, Task<UserItemData>>();
/// <summary>
/// The repository name
/// </summary>
@@ -45,9 +48,6 @@ namespace MediaBrowser.Server.Implementations.Sqlite
}
}
/// <summary>
/// The _protobuf serializer
/// </summary>
private readonly IJsonSerializer _jsonSerializer;
/// <summary>
@@ -90,8 +90,8 @@ namespace MediaBrowser.Server.Implementations.Sqlite
string[] queries = {
"create table if not exists userdata (id GUID, userId GUID, data BLOB)",
"create unique index if not exists userdataindex on userdata (id, userId)",
"create table if not exists userdata (key nvarchar, userId GUID, data BLOB)",
"create unique index if not exists userdataindex on userdata (key, userId)",
"create table if not exists schema_version (table_name primary key, version)",
//pragmas
"pragma temp_store = memory"
@@ -104,20 +104,18 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// Saves the user data.
/// </summary>
/// <param name="userId">The user id.</param>
/// <param name="userDataId">The user data id.</param>
/// <param name="key">The key.</param>
/// <param name="userData">The user data.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException">
/// userData
/// <exception cref="System.ArgumentNullException">userData
/// or
/// cancellationToken
/// or
/// userId
/// or
/// userDataId
/// </exception>
public async Task SaveUserData(Guid userId, Guid userDataId, UserItemData userData, CancellationToken cancellationToken)
/// userDataId</exception>
public async Task SaveUserData(Guid userId, string key, UserItemData userData, CancellationToken cancellationToken)
{
if (userData == null)
{
@@ -131,20 +129,49 @@ namespace MediaBrowser.Server.Implementations.Sqlite
{
throw new ArgumentNullException("userId");
}
if (userDataId == Guid.Empty)
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException("userDataId");
throw new ArgumentNullException("key");
}
cancellationToken.ThrowIfCancellationRequested();
try
{
await PersistUserData(userId, key, userData, cancellationToken).ConfigureAwait(false);
var newValue = Task.FromResult(userData);
// Once it succeeds, put it into the dictionary to make it available to everyone else
_userData.AddOrUpdate(key, newValue, delegate { return newValue; });
}
catch (Exception ex)
{
Logger.ErrorException("Error saving user data", ex);
throw;
}
}
/// <summary>
/// Persists the user data.
/// </summary>
/// <param name="userId">The user id.</param>
/// <param name="key">The key.</param>
/// <param name="userData">The user data.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public async Task PersistUserData(Guid userId, string key, UserItemData userData, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var serialized = _jsonSerializer.SerializeToBytes(userData);
cancellationToken.ThrowIfCancellationRequested();
var cmd = connection.CreateCommand();
cmd.CommandText = "replace into userdata (id, userId, data) values (@1, @2, @3)";
cmd.AddParam("@1", userDataId);
cmd.CommandText = "replace into userdata (key, userId, data) values (@1, @2, @3)";
cmd.AddParam("@1", key);
cmd.AddParam("@2", userId);
cmd.AddParam("@3", serialized);
@@ -174,29 +201,40 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// Gets the user data.
/// </summary>
/// <param name="userId">The user id.</param>
/// <param name="userDataId">The user data id.</param>
/// <param name="key">The key.</param>
/// <returns>Task{UserItemData}.</returns>
/// <exception cref="System.ArgumentNullException">
/// userId
/// or
/// userDataId
/// key
/// </exception>
public async Task<UserItemData> GetUserData(Guid userId, Guid userDataId)
public Task<UserItemData> GetUserData(Guid userId, string key)
{
if (userId == Guid.Empty)
{
throw new ArgumentNullException("userId");
}
if (userDataId == Guid.Empty)
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException("userDataId");
throw new ArgumentNullException("key");
}
return _userData.GetOrAdd(key, keyName => RetrieveUserData(userId, key));
}
/// <summary>
/// Retrieves the user data.
/// </summary>
/// <param name="userId">The user id.</param>
/// <param name="key">The key.</param>
/// <returns>Task{UserItemData}.</returns>
private async Task<UserItemData> RetrieveUserData(Guid userId, string key)
{
var cmd = connection.CreateCommand();
cmd.CommandText = "select data from userdata where id = @id and userId=@userId";
cmd.CommandText = "select data from userdata where key = @key and userId=@userId";
var idParam = cmd.Parameters.Add("@id", DbType.Guid);
idParam.Value = userDataId;
var idParam = cmd.Parameters.Add("@key", DbType.Guid);
idParam.Value = key;
var userIdParam = cmd.Parameters.Add("@userId", DbType.Guid);
userIdParam.Value = userId;
@@ -212,7 +250,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
}
}
return null;
return new UserItemData();
}
}
}