diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-05-21 11:52:59 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-05-21 11:52:59 -0400 |
| commit | e01202030dcd16cd9c7c3327b4e411be7de02614 (patch) | |
| tree | 1f905a3471d6e2f9f6e241f491da5c51f2021e47 | |
| parent | 8682ee30dd8d1d654c79467bd5c0939c7339f214 (diff) | |
removed sql delayed writer in favor of prepared statements
7 files changed, 167 insertions, 228 deletions
diff --git a/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs b/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs index e9953d135..9b0bf824a 100644 --- a/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs +++ b/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs @@ -279,7 +279,6 @@ namespace MediaBrowser.Controller.Providers.TV n = n.SelectSingleNode("./BannerPath"); 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)); } } diff --git a/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs b/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs index f6a77026b..a50dda066 100644 --- a/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs +++ b/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs @@ -525,7 +525,7 @@ namespace MediaBrowser.Controller.Providers.TV if (series.BackdropImagePaths.Count < ConfigurationManager.Configuration.MaxBackdrops) { - var bdNo = 0; + var bdNo = series.BackdropImagePaths.Count; var xmlNodeList = images.SelectNodes("//Banner[BannerType='fanart']"); if (xmlNodeList != null) { diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs index 93bb174c6..93b246380 100644 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs @@ -34,18 +34,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> /// The _json serializer /// </summary> private readonly IJsonSerializer _jsonSerializer; @@ -132,13 +120,13 @@ namespace MediaBrowser.Server.Implementations.Sqlite cancellationToken.ThrowIfCancellationRequested(); - using (var cmd = connection.CreateCommand()) + using (var cmd = Connection.CreateCommand()) { cmd.CommandText = "replace into displaypreferences (id, data) values (@1, @2)"; cmd.AddParam("@1", displayPreferences.Id); cmd.AddParam("@2", serialized); - using (var tran = connection.BeginTransaction()) + using (var tran = Connection.BeginTransaction()) { try { @@ -174,7 +162,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite throw new ArgumentNullException("displayPreferencesId"); } - var cmd = connection.CreateCommand(); + var cmd = Connection.CreateCommand(); cmd.CommandText = "select data from displaypreferences where id = @id"; var idParam = cmd.Parameters.Add("@id", DbType.Guid); diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs index 62268c0c3..23ab10499 100644 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs @@ -8,6 +8,7 @@ using MediaBrowser.Server.Implementations.Reflection; using System; using System.Collections.Generic; using System.Data; +using System.Data.SQLite; using System.IO; using System.Linq; using System.Threading; @@ -54,6 +55,19 @@ namespace MediaBrowser.Server.Implementations.Sqlite 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> /// Initializes a new instance of the <see cref="SQLiteUserDataRepository" /> class. /// </summary> /// <param name="appPaths">The app paths.</param> @@ -100,6 +114,8 @@ namespace MediaBrowser.Server.Implementations.Sqlite }; RunQueries(queries); + + PrepareStatements(); } //cascade delete triggers @@ -117,13 +133,46 @@ namespace MediaBrowser.Server.Implementations.Sqlite 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> /// Save a standard item in the repo /// </summary> /// <param name="item">The item.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> /// <exception cref="System.ArgumentNullException">item</exception> - public Task SaveItem(BaseItem item, CancellationToken cancellationToken) + public async Task SaveItem(BaseItem item, CancellationToken cancellationToken) { if (item == null) { @@ -137,19 +186,51 @@ namespace MediaBrowser.Server.Implementations.Sqlite 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(); - cmd.CommandText = "replace into items (guid, obj_type, data) values (@1, @2, @3)"; - cmd.AddParam("@1", item.Id); - cmd.AddParam("@2", item.GetType().FullName); - cmd.AddParam("@3", serialized); - QueueCommand(cmd); - }); + _saveItemCommand.Transaction = transaction; + + await _saveItemCommand.ExecuteNonQueryAsync(cancellationToken); + + 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> @@ -157,6 +238,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite /// </summary> /// <param name="id">The id.</param> /// <returns>BaseItem.</returns> + /// <exception cref="System.ArgumentNullException">id</exception> /// <exception cref="System.ArgumentException"></exception> public BaseItem GetItem(Guid id) { @@ -189,6 +271,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite /// </summary> /// <param name="id">The id.</param> /// <returns>BaseItem.</returns> + /// <exception cref="System.ArgumentNullException">id</exception> /// <exception cref="System.ArgumentException"></exception> protected BaseItem RetrieveItemInternal(Guid id) { @@ -197,7 +280,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite 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"; var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); @@ -240,7 +323,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite 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)"; var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); @@ -281,7 +364,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> /// <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) { @@ -300,27 +383,57 @@ namespace MediaBrowser.Server.Implementations.Sqlite cancellationToken.ThrowIfCancellationRequested(); - return Task.Run(() => - { - var cmd = connection.CreateCommand(); + await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); + + SQLiteTransaction transaction = null; - cmd.CommandText = "delete from children where guid = @guid"; - cmd.AddParam("@guid", id); + try + { + transaction = Connection.BeginTransaction(); - QueueCommand(cmd); + // Delete exising children + _deleteChildrenCommand.Parameters[0].Value = id; + _deleteChildrenCommand.Transaction = transaction; + await _deleteChildrenCommand.ExecuteNonQueryAsync(cancellationToken); + // Save new children foreach (var child in children) { - var guid = child.Id; - cmd = connection.CreateCommand(); - cmd.AddParam("@guid", id); - cmd.CommandText = "replace into children (guid, child) values (@guid, @child)"; - var childParam = cmd.Parameters.Add("@child", DbType.Guid); - - childParam.Value = guid; - QueueCommand(cmd); + _saveChildrenCommand.Transaction = transaction; + + _saveChildrenCommand.Parameters[0].Value = id; + _saveChildrenCommand.Parameters[1].Value = child.Id; + + await _saveChildrenCommand.ExecuteNonQueryAsync(cancellationToken); + } + + 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> diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs index 6aeb63d9b..bd60c834f 100644 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs @@ -18,24 +18,11 @@ namespace MediaBrowser.Server.Implementations.Sqlite /// <summary> /// The db file name /// </summary> - protected string dbFileName; + protected string DbFileName; /// <summary> /// The connection /// </summary> - 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; + protected SQLiteConnection Connection; /// <summary> /// Gets the logger. @@ -44,18 +31,6 @@ namespace MediaBrowser.Server.Implementations.Sqlite 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> /// Initializes a new instance of the <see cref="SqliteRepository" /> class. /// </summary> /// <param name="logManager">The log manager.</param> @@ -83,7 +58,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite throw new ArgumentNullException("dbPath"); } - dbFileName = dbPath; + DbFileName = dbPath; var connectionstr = new SQLiteConnectionStringBuilder { PageSize = 4096, @@ -93,15 +68,9 @@ namespace MediaBrowser.Server.Implementations.Sqlite JournalMode = SQLiteJournalModeEnum.Memory }; - connection = new SQLiteConnection(connectionstr.ConnectionString); + Connection = new SQLiteConnection(connectionstr.ConnectionString); - await connection.OpenAsync().ConfigureAwait(false); - - if (EnableDelayedCommands) - { - // Run once - FlushTimer = new Timer(Flush, null, TimeSpan.FromMilliseconds(FlushInterval), TimeSpan.FromMilliseconds(-1)); - } + await Connection.OpenAsync().ConfigureAwait(false); } /// <summary> @@ -117,11 +86,11 @@ namespace MediaBrowser.Server.Implementations.Sqlite throw new ArgumentNullException("queries"); } - using (var tran = connection.BeginTransaction()) + using (var tran = Connection.BeginTransaction()) { try { - using (var cmd = connection.CreateCommand()) + using (var cmd = Connection.CreateCommand()) { foreach (var query in queries) { @@ -165,26 +134,15 @@ namespace MediaBrowser.Server.Implementations.Sqlite { lock (_disposeLock) { - if (connection != null) + if (Connection != null) { - if (EnableDelayedCommands) + if (Connection.IsOpen()) { - FlushOnDispose(); + Connection.Close(); } - if (connection.IsOpen()) - { - connection.Close(); - } - - connection.Dispose(); - connection = null; - } - - if (FlushTimer != null) - { - FlushTimer.Dispose(); - FlushTimer = null; + Connection.Dispose(); + Connection = null; } } } @@ -196,101 +154,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> /// Executes the command. /// </summary> /// <param name="cmd">The CMD.</param> @@ -303,11 +166,11 @@ namespace MediaBrowser.Server.Implementations.Sqlite throw new ArgumentNullException("cmd"); } - using (var tran = connection.BeginTransaction()) + using (var tran = Connection.BeginTransaction()) { try { - cmd.Connection = connection; + cmd.Connection = Connection; cmd.Transaction = tran; await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs index 443b15c10..d20b59035 100644 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs @@ -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; /// <summary> @@ -184,14 +172,14 @@ namespace MediaBrowser.Server.Implementations.Sqlite 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.AddParam("@1", key); cmd.AddParam("@2", userId); cmd.AddParam("@3", serialized); - using (var tran = connection.BeginTransaction()) + using (var tran = Connection.BeginTransaction()) { try { @@ -247,7 +235,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite /// <returns>Task{UserItemData}.</returns> 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"; diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs index eb5868932..baaf9d956 100644 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs @@ -46,18 +46,6 @@ namespace MediaBrowser.Server.Implementations.Sqlite 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> /// Initializes a new instance of the <see cref="SQLiteUserDataRepository" /> class. /// </summary> /// <param name="appPaths">The app paths.</param> @@ -127,13 +115,13 @@ namespace MediaBrowser.Server.Implementations.Sqlite cancellationToken.ThrowIfCancellationRequested(); - using (var cmd = connection.CreateCommand()) + using (var cmd = Connection.CreateCommand()) { cmd.CommandText = "replace into users (guid, data) values (@1, @2)"; cmd.AddParam("@1", user.Id); cmd.AddParam("@2", serialized); - using (var tran = connection.BeginTransaction()) + using (var tran = Connection.BeginTransaction()) { try { @@ -162,7 +150,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite /// <returns>IEnumerable{User}.</returns> public IEnumerable<User> RetrieveAllUsers() { - using (var cmd = connection.CreateCommand()) + using (var cmd = Connection.CreateCommand()) { cmd.CommandText = "select data from users"; @@ -201,7 +189,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite cancellationToken.ThrowIfCancellationRequested(); - using (var cmd = connection.CreateCommand()) + using (var cmd = Connection.CreateCommand()) { cmd.CommandText = "delete from users where guid=@guid"; var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); |
