This commit is contained in:
Eric Reed 2013-05-21 12:13:57 -04:00
commit f4d846dc77
7 changed files with 166 additions and 227 deletions

View File

@ -279,7 +279,6 @@ namespace MediaBrowser.Controller.Providers.TV
n = n.SelectSingleNode("./BannerPath"); n = n.SelectSingleNode("./BannerPath");
if (n != null) if (n != null)
{ {
if (season.BackdropImagePaths == null) season.BackdropImagePaths = new List<string>();
season.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(season, TVUtils.BannerUrl + n.InnerText, "backdrop" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false)); season.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(season, TVUtils.BannerUrl + n.InnerText, "backdrop" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false));
} }
} }

View File

@ -525,7 +525,7 @@ namespace MediaBrowser.Controller.Providers.TV
if (series.BackdropImagePaths.Count < ConfigurationManager.Configuration.MaxBackdrops) if (series.BackdropImagePaths.Count < ConfigurationManager.Configuration.MaxBackdrops)
{ {
var bdNo = 0; var bdNo = series.BackdropImagePaths.Count;
var xmlNodeList = images.SelectNodes("//Banner[BannerType='fanart']"); var xmlNodeList = images.SelectNodes("//Banner[BannerType='fanart']");
if (xmlNodeList != null) if (xmlNodeList != null)
{ {

View File

@ -33,18 +33,6 @@ namespace MediaBrowser.Server.Implementations.Sqlite
} }
} }
/// <summary>
/// Gets a value indicating whether [enable delayed commands].
/// </summary>
/// <value><c>true</c> if [enable delayed commands]; otherwise, <c>false</c>.</value>
protected override bool EnableDelayedCommands
{
get
{
return false;
}
}
/// <summary> /// <summary>
/// The _json serializer /// The _json serializer
/// </summary> /// </summary>
@ -132,13 +120,13 @@ namespace MediaBrowser.Server.Implementations.Sqlite
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
using (var cmd = connection.CreateCommand()) using (var cmd = Connection.CreateCommand())
{ {
cmd.CommandText = "replace into displaypreferences (id, data) values (@1, @2)"; cmd.CommandText = "replace into displaypreferences (id, data) values (@1, @2)";
cmd.AddParam("@1", displayPreferences.Id); cmd.AddParam("@1", displayPreferences.Id);
cmd.AddParam("@2", serialized); cmd.AddParam("@2", serialized);
using (var tran = connection.BeginTransaction()) using (var tran = Connection.BeginTransaction())
{ {
try try
{ {
@ -174,7 +162,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
throw new ArgumentNullException("displayPreferencesId"); throw new ArgumentNullException("displayPreferencesId");
} }
var cmd = connection.CreateCommand(); var cmd = Connection.CreateCommand();
cmd.CommandText = "select data from displaypreferences where id = @id"; cmd.CommandText = "select data from displaypreferences where id = @id";
var idParam = cmd.Parameters.Add("@id", DbType.Guid); var idParam = cmd.Parameters.Add("@id", DbType.Guid);

View File

@ -8,6 +8,7 @@ using MediaBrowser.Server.Implementations.Reflection;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using System.Data.SQLite;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@ -53,6 +54,19 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// </summary> /// </summary>
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
/// <summary>
/// The _save item command
/// </summary>
private SQLiteCommand _saveItemCommand;
/// <summary>
/// The _delete children command
/// </summary>
private SQLiteCommand _deleteChildrenCommand;
/// <summary>
/// The _save children command
/// </summary>
private SQLiteCommand _saveChildrenCommand;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SQLiteUserDataRepository" /> class. /// Initializes a new instance of the <see cref="SQLiteUserDataRepository" /> class.
/// </summary> /// </summary>
@ -100,6 +114,8 @@ namespace MediaBrowser.Server.Implementations.Sqlite
}; };
RunQueries(queries); RunQueries(queries);
PrepareStatements();
} }
//cascade delete triggers //cascade delete triggers
@ -116,6 +132,39 @@ namespace MediaBrowser.Server.Implementations.Sqlite
DELETE FROM children WHERE children.child = old.child; DELETE FROM children WHERE children.child = old.child;
END"; END";
/// <summary>
/// The _write lock
/// </summary>
private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1,1);
/// <summary>
/// Prepares the statements.
/// </summary>
private void PrepareStatements()
{
_saveItemCommand = new SQLiteCommand
{
CommandText = "replace into items (guid, obj_type, data) values (@1, @2, @3)"
};
_saveItemCommand.Parameters.Add(new SQLiteParameter("@1"));
_saveItemCommand.Parameters.Add(new SQLiteParameter("@2"));
_saveItemCommand.Parameters.Add(new SQLiteParameter("@3"));
_deleteChildrenCommand = new SQLiteCommand
{
CommandText = "delete from children where guid = @guid"
};
_deleteChildrenCommand.Parameters.Add(new SQLiteParameter("@guid"));
_saveChildrenCommand = new SQLiteCommand
{
CommandText = "replace into children (guid, child) values (@guid, @child)"
};
_saveChildrenCommand.Parameters.Add(new SQLiteParameter("@guid"));
_saveChildrenCommand.Parameters.Add(new SQLiteParameter("@child"));
}
/// <summary> /// <summary>
/// Save a standard item in the repo /// Save a standard item in the repo
/// </summary> /// </summary>
@ -123,7 +172,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException">item</exception> /// <exception cref="System.ArgumentNullException">item</exception>
public Task SaveItem(BaseItem item, CancellationToken cancellationToken) public async Task SaveItem(BaseItem item, CancellationToken cancellationToken)
{ {
if (item == null) if (item == null)
{ {
@ -137,19 +186,51 @@ namespace MediaBrowser.Server.Implementations.Sqlite
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
return Task.Run(() => var serialized = _jsonSerializer.SerializeToBytes(item);
await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false);
SQLiteTransaction transaction = null;
try
{ {
var serialized = _jsonSerializer.SerializeToBytes(item); transaction = Connection.BeginTransaction();
cancellationToken.ThrowIfCancellationRequested(); _saveItemCommand.Parameters[0].Value = item.Id;
_saveItemCommand.Parameters[1].Value = item.GetType().FullName;
_saveItemCommand.Parameters[2].Value = serialized;
var cmd = connection.CreateCommand(); _saveItemCommand.Transaction = transaction;
cmd.CommandText = "replace into items (guid, obj_type, data) values (@1, @2, @3)";
cmd.AddParam("@1", item.Id); await _saveItemCommand.ExecuteNonQueryAsync(cancellationToken);
cmd.AddParam("@2", item.GetType().FullName);
cmd.AddParam("@3", serialized); transaction.Commit();
QueueCommand(cmd); }
}); catch (OperationCanceledException)
{
if (transaction != null)
{
transaction.Rollback();
}
}
catch (Exception e)
{
Logger.ErrorException("Failed to save item:", e);
if (transaction != null)
{
transaction.Rollback();
}
}
finally
{
if (transaction != null)
{
transaction.Dispose();
}
_writeLock.Release();
}
} }
/// <summary> /// <summary>
@ -157,6 +238,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// </summary> /// </summary>
/// <param name="id">The id.</param> /// <param name="id">The id.</param>
/// <returns>BaseItem.</returns> /// <returns>BaseItem.</returns>
/// <exception cref="System.ArgumentNullException">id</exception>
/// <exception cref="System.ArgumentException"></exception> /// <exception cref="System.ArgumentException"></exception>
public BaseItem GetItem(Guid id) public BaseItem GetItem(Guid id)
{ {
@ -189,6 +271,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// </summary> /// </summary>
/// <param name="id">The id.</param> /// <param name="id">The id.</param>
/// <returns>BaseItem.</returns> /// <returns>BaseItem.</returns>
/// <exception cref="System.ArgumentNullException">id</exception>
/// <exception cref="System.ArgumentException"></exception> /// <exception cref="System.ArgumentException"></exception>
protected BaseItem RetrieveItemInternal(Guid id) protected BaseItem RetrieveItemInternal(Guid id)
{ {
@ -197,7 +280,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
throw new ArgumentNullException("id"); throw new ArgumentNullException("id");
} }
using (var cmd = connection.CreateCommand()) using (var cmd = Connection.CreateCommand())
{ {
cmd.CommandText = "select obj_type,data from items where guid = @guid"; cmd.CommandText = "select obj_type,data from items where guid = @guid";
var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); var guidParam = cmd.Parameters.Add("@guid", DbType.Guid);
@ -240,7 +323,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
using (var cmd = connection.CreateCommand()) using (var cmd = Connection.CreateCommand())
{ {
cmd.CommandText = "select obj_type,data from items where guid in (select child from children where guid = @guid)"; cmd.CommandText = "select obj_type,data from items where guid in (select child from children where guid = @guid)";
var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); var guidParam = cmd.Parameters.Add("@guid", DbType.Guid);
@ -281,7 +364,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException">id</exception> /// <exception cref="System.ArgumentNullException">id</exception>
public Task SaveChildren(Guid id, IEnumerable<BaseItem> children, CancellationToken cancellationToken) public async Task SaveChildren(Guid id, IEnumerable<BaseItem> children, CancellationToken cancellationToken)
{ {
if (id == Guid.Empty) if (id == Guid.Empty)
{ {
@ -300,27 +383,57 @@ namespace MediaBrowser.Server.Implementations.Sqlite
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
return Task.Run(() => await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false);
SQLiteTransaction transaction = null;
try
{ {
var cmd = connection.CreateCommand(); transaction = Connection.BeginTransaction();
cmd.CommandText = "delete from children where guid = @guid"; // Delete exising children
cmd.AddParam("@guid", id); _deleteChildrenCommand.Parameters[0].Value = id;
_deleteChildrenCommand.Transaction = transaction;
QueueCommand(cmd); await _deleteChildrenCommand.ExecuteNonQueryAsync(cancellationToken);
// Save new children
foreach (var child in children) foreach (var child in children)
{ {
var guid = child.Id; _saveChildrenCommand.Transaction = transaction;
cmd = connection.CreateCommand();
cmd.AddParam("@guid", id); _saveChildrenCommand.Parameters[0].Value = id;
cmd.CommandText = "replace into children (guid, child) values (@guid, @child)"; _saveChildrenCommand.Parameters[1].Value = child.Id;
var childParam = cmd.Parameters.Add("@child", DbType.Guid);
childParam.Value = guid; await _saveChildrenCommand.ExecuteNonQueryAsync(cancellationToken);
QueueCommand(cmd);
} }
});
transaction.Commit();
}
catch (OperationCanceledException)
{
if (transaction != null)
{
transaction.Rollback();
}
}
catch (Exception e)
{
Logger.ErrorException("Failed to save item:", e);
if (transaction != null)
{
transaction.Rollback();
}
}
finally
{
if (transaction != null)
{
transaction.Dispose();
}
_writeLock.Release();
}
} }
/// <summary> /// <summary>

View File

@ -18,24 +18,11 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// <summary> /// <summary>
/// The db file name /// The db file name
/// </summary> /// </summary>
protected string dbFileName; protected string DbFileName;
/// <summary> /// <summary>
/// The connection /// The connection
/// </summary> /// </summary>
protected SQLiteConnection connection; protected SQLiteConnection Connection;
/// <summary>
/// The delayed commands
/// </summary>
protected ConcurrentQueue<SQLiteCommand> delayedCommands = new ConcurrentQueue<SQLiteCommand>();
/// <summary>
/// The flush interval
/// </summary>
private const int FlushInterval = 2000;
/// <summary>
/// The flush timer
/// </summary>
private Timer FlushTimer;
/// <summary> /// <summary>
/// Gets the logger. /// Gets the logger.
@ -43,18 +30,6 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// <value>The logger.</value> /// <value>The logger.</value>
protected ILogger Logger { get; private set; } protected ILogger Logger { get; private set; }
/// <summary>
/// Gets a value indicating whether [enable delayed commands].
/// </summary>
/// <value><c>true</c> if [enable delayed commands]; otherwise, <c>false</c>.</value>
protected virtual bool EnableDelayedCommands
{
get
{
return true;
}
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SqliteRepository" /> class. /// Initializes a new instance of the <see cref="SqliteRepository" /> class.
/// </summary> /// </summary>
@ -83,7 +58,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
throw new ArgumentNullException("dbPath"); throw new ArgumentNullException("dbPath");
} }
dbFileName = dbPath; DbFileName = dbPath;
var connectionstr = new SQLiteConnectionStringBuilder var connectionstr = new SQLiteConnectionStringBuilder
{ {
PageSize = 4096, PageSize = 4096,
@ -93,15 +68,9 @@ namespace MediaBrowser.Server.Implementations.Sqlite
JournalMode = SQLiteJournalModeEnum.Memory JournalMode = SQLiteJournalModeEnum.Memory
}; };
connection = new SQLiteConnection(connectionstr.ConnectionString); Connection = new SQLiteConnection(connectionstr.ConnectionString);
await connection.OpenAsync().ConfigureAwait(false); await Connection.OpenAsync().ConfigureAwait(false);
if (EnableDelayedCommands)
{
// Run once
FlushTimer = new Timer(Flush, null, TimeSpan.FromMilliseconds(FlushInterval), TimeSpan.FromMilliseconds(-1));
}
} }
/// <summary> /// <summary>
@ -117,11 +86,11 @@ namespace MediaBrowser.Server.Implementations.Sqlite
throw new ArgumentNullException("queries"); throw new ArgumentNullException("queries");
} }
using (var tran = connection.BeginTransaction()) using (var tran = Connection.BeginTransaction())
{ {
try try
{ {
using (var cmd = connection.CreateCommand()) using (var cmd = Connection.CreateCommand())
{ {
foreach (var query in queries) foreach (var query in queries)
{ {
@ -165,26 +134,15 @@ namespace MediaBrowser.Server.Implementations.Sqlite
{ {
lock (_disposeLock) lock (_disposeLock)
{ {
if (connection != null) if (Connection != null)
{ {
if (EnableDelayedCommands) if (Connection.IsOpen())
{ {
FlushOnDispose(); Connection.Close();
} }
if (connection.IsOpen()) Connection.Dispose();
{ Connection = null;
connection.Close();
}
connection.Dispose();
connection = null;
}
if (FlushTimer != null)
{
FlushTimer.Dispose();
FlushTimer = null;
} }
} }
} }
@ -195,101 +153,6 @@ namespace MediaBrowser.Server.Implementations.Sqlite
} }
} }
/// <summary>
/// Flushes the on dispose.
/// </summary>
private void FlushOnDispose()
{
// If we're not already flushing, do it now
if (!_isFlushing)
{
Flush(null);
}
// Don't dispose in the middle of a flush
while (_isFlushing)
{
Thread.Sleep(25);
}
}
/// <summary>
/// Queues the command.
/// </summary>
/// <param name="cmd">The CMD.</param>
/// <exception cref="System.ArgumentNullException">cmd</exception>
protected void QueueCommand(SQLiteCommand cmd)
{
if (cmd == null)
{
throw new ArgumentNullException("cmd");
}
delayedCommands.Enqueue(cmd);
}
/// <summary>
/// The is flushing
/// </summary>
private bool _isFlushing;
/// <summary>
/// Flushes the specified sender.
/// </summary>
/// <param name="sender">The sender.</param>
private void Flush(object sender)
{
// Cannot call Count on a ConcurrentQueue since it's an O(n) operation
// Use IsEmpty instead
if (delayedCommands.IsEmpty)
{
FlushTimer.Change(TimeSpan.FromMilliseconds(FlushInterval), TimeSpan.FromMilliseconds(-1));
return;
}
if (_isFlushing)
{
return;
}
_isFlushing = true;
var numCommands = 0;
using (var tran = connection.BeginTransaction())
{
try
{
while (!delayedCommands.IsEmpty)
{
SQLiteCommand command;
delayedCommands.TryDequeue(out command);
command.Connection = connection;
command.Transaction = tran;
command.ExecuteNonQuery();
command.Dispose();
numCommands++;
}
tran.Commit();
}
catch (Exception e)
{
Logger.ErrorException("Failed to commit transaction.", e);
tran.Rollback();
}
}
Logger.Debug("SQL Delayed writer executed " + numCommands + " commands");
FlushTimer.Change(TimeSpan.FromMilliseconds(FlushInterval), TimeSpan.FromMilliseconds(-1));
_isFlushing = false;
}
/// <summary> /// <summary>
/// Executes the command. /// Executes the command.
/// </summary> /// </summary>
@ -303,11 +166,11 @@ namespace MediaBrowser.Server.Implementations.Sqlite
throw new ArgumentNullException("cmd"); throw new ArgumentNullException("cmd");
} }
using (var tran = connection.BeginTransaction()) using (var tran = Connection.BeginTransaction())
{ {
try try
{ {
cmd.Connection = connection; cmd.Connection = Connection;
cmd.Transaction = tran; cmd.Transaction = tran;
await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);

View File

@ -36,18 +36,6 @@ namespace MediaBrowser.Server.Implementations.Sqlite
} }
} }
/// <summary>
/// Gets a value indicating whether [enable delayed commands].
/// </summary>
/// <value><c>true</c> if [enable delayed commands]; otherwise, <c>false</c>.</value>
protected override bool EnableDelayedCommands
{
get
{
return false;
}
}
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
/// <summary> /// <summary>
@ -184,14 +172,14 @@ namespace MediaBrowser.Server.Implementations.Sqlite
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
using (var cmd = connection.CreateCommand()) using (var cmd = Connection.CreateCommand())
{ {
cmd.CommandText = "replace into userdata (key, userId, data) values (@1, @2, @3)"; cmd.CommandText = "replace into userdata (key, userId, data) values (@1, @2, @3)";
cmd.AddParam("@1", key); cmd.AddParam("@1", key);
cmd.AddParam("@2", userId); cmd.AddParam("@2", userId);
cmd.AddParam("@3", serialized); cmd.AddParam("@3", serialized);
using (var tran = connection.BeginTransaction()) using (var tran = Connection.BeginTransaction())
{ {
try try
{ {
@ -247,7 +235,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// <returns>Task{UserItemData}.</returns> /// <returns>Task{UserItemData}.</returns>
private async Task<UserItemData> RetrieveUserData(Guid userId, string key) private async Task<UserItemData> RetrieveUserData(Guid userId, string key)
{ {
using (var cmd = connection.CreateCommand()) using (var cmd = Connection.CreateCommand())
{ {
cmd.CommandText = "select data from userdata where key = @key and userId=@userId"; cmd.CommandText = "select data from userdata where key = @key and userId=@userId";

View File

@ -45,18 +45,6 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// </summary> /// </summary>
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
/// <summary>
/// Gets a value indicating whether [enable delayed commands].
/// </summary>
/// <value><c>true</c> if [enable delayed commands]; otherwise, <c>false</c>.</value>
protected override bool EnableDelayedCommands
{
get
{
return false;
}
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SQLiteUserDataRepository" /> class. /// Initializes a new instance of the <see cref="SQLiteUserDataRepository" /> class.
/// </summary> /// </summary>
@ -127,13 +115,13 @@ namespace MediaBrowser.Server.Implementations.Sqlite
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
using (var cmd = connection.CreateCommand()) using (var cmd = Connection.CreateCommand())
{ {
cmd.CommandText = "replace into users (guid, data) values (@1, @2)"; cmd.CommandText = "replace into users (guid, data) values (@1, @2)";
cmd.AddParam("@1", user.Id); cmd.AddParam("@1", user.Id);
cmd.AddParam("@2", serialized); cmd.AddParam("@2", serialized);
using (var tran = connection.BeginTransaction()) using (var tran = Connection.BeginTransaction())
{ {
try try
{ {
@ -162,7 +150,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// <returns>IEnumerable{User}.</returns> /// <returns>IEnumerable{User}.</returns>
public IEnumerable<User> RetrieveAllUsers() public IEnumerable<User> RetrieveAllUsers()
{ {
using (var cmd = connection.CreateCommand()) using (var cmd = Connection.CreateCommand())
{ {
cmd.CommandText = "select data from users"; cmd.CommandText = "select data from users";
@ -201,7 +189,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
using (var cmd = connection.CreateCommand()) using (var cmd = Connection.CreateCommand())
{ {
cmd.CommandText = "delete from users where guid=@guid"; cmd.CommandText = "delete from users where guid=@guid";
var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); var guidParam = cmd.Parameters.Add("@guid", DbType.Guid);