migrate to new user data db

This commit is contained in:
Luke Pulverenti 2013-09-27 11:23:27 -04:00
parent c643dd072e
commit ce3e881c10
8 changed files with 226 additions and 18 deletions

View File

@ -22,7 +22,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Class BaseItem
/// </summary>
public abstract class BaseItem : IHasProviderIds
public abstract class BaseItem : IHasProviderIds, ILibraryItem
{
/// <summary>
/// MusicAlbums in the library that are the soundtrack for this item

View File

@ -0,0 +1,28 @@
using System;
namespace MediaBrowser.Controller.Entities
{
/// <summary>
/// Interface ILibraryItem
/// </summary>
public interface ILibraryItem
{
/// <summary>
/// Gets the name.
/// </summary>
/// <value>The name.</value>
string Name { get; }
/// <summary>
/// Gets the id.
/// </summary>
/// <value>The id.</value>
Guid Id { get; }
/// <summary>
/// Gets the path.
/// </summary>
/// <value>The path.</value>
string Path { get; }
}
}

View File

@ -14,6 +14,12 @@ namespace MediaBrowser.Controller.Entities
/// <value>The user id.</value>
public Guid UserId { get; set; }
/// <summary>
/// Gets or sets the key.
/// </summary>
/// <value>The key.</value>
public string Key { get; set; }
/// <summary>
/// The _rating
/// </summary>

View File

@ -90,6 +90,7 @@
<Compile Include="Entities\GameSystem.cs" />
<Compile Include="Entities\IByReferenceItem.cs" />
<Compile Include="Entities\IItemByName.cs" />
<Compile Include="Entities\ILibraryItem.cs" />
<Compile Include="Entities\LinkedChild.cs" />
<Compile Include="Entities\MusicVideo.cs" />
<Compile Include="Library\ILibraryPostScanTask.cs" />

View File

@ -37,6 +37,13 @@
<Reference Include="Alchemy">
<HintPath>..\packages\Alchemy.2.2.1\lib\net40\Alchemy.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.OrmLite, Version=3.9.60.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.OrmLite.Sqlite32.3.9.63\lib\net40\ServiceStack.OrmLite.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.OrmLite.SqliteNET">
<HintPath>..\packages\ServiceStack.OrmLite.Sqlite32.3.9.63\lib\net40\ServiceStack.OrmLite.SqliteNET.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
@ -155,6 +162,7 @@
<Compile Include="Persistence\SqliteExtensions.cs" />
<Compile Include="Persistence\SqliteNotificationsRepository.cs" />
<Compile Include="Persistence\TypeMapper.cs" />
<Compile Include="Persistence\UserDataMigration.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Providers\ImageSaver.cs" />
<Compile Include="Providers\ProviderManager.cs" />

View File

@ -16,13 +16,13 @@ namespace MediaBrowser.Server.Implementations.Persistence
public class SqliteUserDataRepository : IUserDataRepository
{
private readonly ILogger _logger;
private readonly ConcurrentDictionary<string, UserItemData> _userData = new ConcurrentDictionary<string, UserItemData>();
private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1);
private SQLiteConnection _connection;
/// <summary>
/// Gets the name of the repository
/// </summary>
@ -75,20 +75,28 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// <returns>Task.</returns>
public async Task Initialize()
{
var dbFile = Path.Combine(_appPaths.DataPath, "userdata.db");
var dbFile = Path.Combine(_appPaths.DataPath, "userdata_v2.db");
_connection = await SqliteExtensions.ConnectToDb(dbFile).ConfigureAwait(false);
string[] queries = {
"create table if not exists userdata (key nvarchar, userId GUID, data BLOB)",
"create table if not exists userdata (key nvarchar, userId GUID, rating float null, played bit, playCount int, isFavorite bit, playbackPositionTicks bigint, lastPlayedDate datetime null)",
"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"
};
_connection.RunQueries(queries, _logger);
var oldFile = Path.Combine(_appPaths.DataPath, "userdata.db");
if (File.Exists(oldFile))
{
await UserDataMigration.Migrate(oldFile, _connection, _logger, _jsonSerializer).ConfigureAwait(false);
}
}
/// <summary>
@ -167,10 +175,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
cancellationToken.ThrowIfCancellationRequested();
var serialized = _jsonSerializer.SerializeToBytes(userData);
cancellationToken.ThrowIfCancellationRequested();
await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false);
SQLiteTransaction transaction = null;
@ -181,11 +185,18 @@ namespace MediaBrowser.Server.Implementations.Persistence
using (var cmd = _connection.CreateCommand())
{
cmd.CommandText = "replace into userdata (key, userId, data) values (@1, @2, @3)";
cmd.AddParam("@1", key);
cmd.AddParam("@2", userId);
cmd.AddParam("@3", serialized);
cmd.CommandText = "replace into userdata (key, userId, rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate) values (@key, @userId, @rating,@played,@playCount,@isFavorite,@playbackPositionTicks,@lastPlayedDate)";
cmd.AddParam("@key", key);
cmd.AddParam("@userId", userId);
cmd.AddParam("@rating", userData.Rating);
cmd.AddParam("@played", userData.Played);
cmd.AddParam("@playCount", userData.PlayCount);
cmd.AddParam("@isFavorite", userData.IsFavorite);
cmd.AddParam("@playbackPositionTicks", userData.PlaybackPositionTicks);
cmd.AddParam("@lastPlayedDate", userData.LastPlayedDate);
cmd.Transaction = transaction;
await cmd.ExecuteNonQueryAsync(cancellationToken);
@ -259,7 +270,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
using (var cmd = _connection.CreateCommand())
{
cmd.CommandText = "select data from userdata where key = @key and userId=@userId";
cmd.CommandText = "select rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate from userdata where key = @key and userId=@userId";
var idParam = cmd.Parameters.Add("@key", DbType.String);
idParam.Value = key;
@ -267,18 +278,34 @@ namespace MediaBrowser.Server.Implementations.Persistence
var userIdParam = cmd.Parameters.Add("@userId", DbType.Guid);
userIdParam.Value = userId;
var userData = new UserItemData
{
UserId = userId,
Key = key
};
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
{
if (reader.Read())
{
using (var stream = reader.GetMemoryStream(0))
if (!reader.IsDBNull(0))
{
return _jsonSerializer.DeserializeFromStream<UserItemData>(stream);
userData.Rating = reader.GetDouble(0);
}
userData.Played = reader.GetBoolean(1);
userData.PlayCount = reader.GetInt32(2);
userData.IsFavorite = reader.GetBoolean(3);
userData.PlaybackPositionTicks = reader.GetInt64(4);
if (!reader.IsDBNull(5))
{
userData.LastPlayedDate = reader.GetDateTime(5);
}
}
}
return new UserItemData();
return userData;
}
}

View File

@ -0,0 +1,137 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Persistence
{
public static class UserDataMigration
{
/// <summary>
/// Migrates the specified old file.
/// </summary>
/// <param name="oldFile">The old file.</param>
/// <param name="newDatabase">The new database.</param>
/// <param name="logger">The logger.</param>
/// <param name="json">The json.</param>
/// <returns>Task.</returns>
public static async Task Migrate(string oldFile, IDbConnection newDatabase, ILogger logger, IJsonSerializer json)
{
var oldDb = await SqliteExtensions.ConnectToDb(oldFile).ConfigureAwait(false);
using (oldDb)
{
IDbTransaction transaction = null;
var data = GetAllUserData(oldDb, json).ToList();
try
{
transaction = newDatabase.BeginTransaction();
foreach (var userdata in data)
{
PersistUserData(userdata, newDatabase, transaction);
}
transaction.Commit();
}
catch (OperationCanceledException)
{
if (transaction != null)
{
transaction.Rollback();
}
throw;
}
catch (Exception e)
{
logger.ErrorException("Failed to save user data:", e);
if (transaction != null)
{
transaction.Rollback();
}
throw;
}
finally
{
if (transaction != null)
{
transaction.Dispose();
}
}
}
File.Move(oldFile, Path.Combine(Path.GetDirectoryName(oldFile), "userdata_v1.db"));
}
/// <summary>
/// Gets all user data.
/// </summary>
/// <param name="oldDatabase">The old database.</param>
/// <param name="jsonSerializer">The json serializer.</param>
/// <returns>IEnumerable{UserItemData}.</returns>
private static IEnumerable<UserItemData> GetAllUserData(IDbConnection oldDatabase, IJsonSerializer jsonSerializer)
{
using (var cmd = oldDatabase.CreateCommand())
{
cmd.CommandText = "select userId,key,data from userdata";
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
{
while (reader.Read())
{
var userId = reader.GetGuid(0);
var key = reader.GetString(1);
using (var stream = reader.GetMemoryStream(2))
{
var userData = jsonSerializer.DeserializeFromStream<UserItemData>(stream);
userData.UserId = userId;
userData.Key = key;
yield return userData;
}
}
}
}
}
/// <summary>
/// Persists the user data.
/// </summary>
/// <param name="userData">The user data.</param>
/// <param name="database">The database.</param>
/// <param name="transaction">The transaction.</param>
private static void PersistUserData(UserItemData userData, IDbConnection database, IDbTransaction transaction)
{
using (var cmd = database.CreateCommand())
{
cmd.CommandText = "replace into userdata (key, userId, rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate) values (@key, @userId, @rating,@played,@playCount,@isFavorite,@playbackPositionTicks,@lastPlayedDate)";
cmd.Parameters.Add(cmd, "@key", DbType.String).Value = userData.Key;
cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userData.UserId;
cmd.Parameters.Add(cmd, "@rating", DbType.Double).Value = userData.Rating;
cmd.Parameters.Add(cmd, "@played", DbType.Boolean).Value = userData.Played;
cmd.Parameters.Add(cmd, "@playCount", DbType.Int32).Value = userData.PlayCount;
cmd.Parameters.Add(cmd, "@isFavorite", DbType.Boolean).Value = userData.IsFavorite;
cmd.Parameters.Add(cmd, "@playbackPositionTicks", DbType.Int64).Value = userData.PlaybackPositionTicks;
cmd.Parameters.Add(cmd, "@lastPlayedDate", DbType.DateTime).Value = userData.LastPlayedDate;
cmd.Transaction = transaction;
cmd.ExecuteNonQuery();
}
}
}
}

View File

@ -9,6 +9,7 @@
<package id="ServiceStack" version="3.9.62" targetFramework="net45" />
<package id="ServiceStack.Api.Swagger" version="3.9.59" targetFramework="net45" />
<package id="ServiceStack.Common" version="3.9.62" targetFramework="net45" />
<package id="ServiceStack.OrmLite.Sqlite32" version="3.9.63" targetFramework="net45" />
<package id="ServiceStack.OrmLite.SqlServer" version="3.9.43" targetFramework="net45" />
<package id="ServiceStack.Redis" version="3.9.43" targetFramework="net45" />
<package id="ServiceStack.Text" version="3.9.62" targetFramework="net45" />