Quellcode durchsuchen

fix db locking errors

Luke Pulverenti vor 8 Jahren
Ursprung
Commit
a9a808a9c4

+ 4 - 4
Emby.Server.Implementations/Activity/ActivityRepository.cs

@@ -52,9 +52,9 @@ namespace Emby.Server.Implementations.Activity
                 throw new ArgumentNullException("entry");
             }
 
-            using (var connection = CreateConnection())
+            using (WriteLock.Write())
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     connection.RunInTransaction(db =>
                     {
@@ -80,9 +80,9 @@ namespace Emby.Server.Implementations.Activity
 
         public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit)
         {
-            using (var connection = CreateConnection(true))
+            using (WriteLock.Read())
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
                     var commandText = BaseActivitySelectText;
                     var whereClauses = new List<string>();

+ 101 - 13
Emby.Server.Implementations/Data/BaseSqliteRepository.cs

@@ -20,27 +20,34 @@ namespace Emby.Server.Implementations.Data
         {
             Logger = logger;
 
-            WriteLock = AllowLockRecursion ?
-              new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion) :
-              new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
+            WriteLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
         }
 
-        protected virtual bool AllowLockRecursion
+        protected TransactionMode TransactionMode
         {
-            get { return false; }
+            get { return TransactionMode.Immediate; }
         }
 
-        protected TransactionMode TransactionMode
+        protected TransactionMode ReadTransactionMode
         {
-            get { return TransactionMode.Immediate; }
+            get { return TransactionMode.Deferred; }
         }
 
+        internal static int ThreadSafeMode { get; set; }
+
         static BaseSqliteRepository()
         {
             SQLite3.EnableSharedCache = false;
 
             int rc = raw.sqlite3_config(raw.SQLITE_CONFIG_MEMSTATUS, 0);
             //CheckOk(rc);
+
+            rc = raw.sqlite3_config(raw.SQLITE_CONFIG_MULTITHREAD, 1);
+            //CheckOk(rc);
+
+            rc = raw.sqlite3_enable_shared_cache(1);
+
+            ThreadSafeMode = raw.sqlite3_threadsafe();
         }
 
         private static bool _versionLogged;
@@ -61,16 +68,19 @@ namespace Emby.Server.Implementations.Data
             if (isReadOnly)
             {
                 //Logger.Info("Opening read connection");
+                //connectionFlags = ConnectionFlags.ReadOnly;
+                connectionFlags = ConnectionFlags.Create;
+                connectionFlags |= ConnectionFlags.ReadWrite;
             }
             else
             {
                 //Logger.Info("Opening write connection");
+                connectionFlags = ConnectionFlags.Create;
+                connectionFlags |= ConnectionFlags.ReadWrite;
             }
 
-            connectionFlags = ConnectionFlags.Create;
-            connectionFlags |= ConnectionFlags.ReadWrite;
-            connectionFlags |= ConnectionFlags.SharedCached;
-            //connectionFlags |= ConnectionFlags.NoMutex;
+            //connectionFlags |= ConnectionFlags.SharedCached;
+            connectionFlags |= ConnectionFlags.NoMutex;
 
             var db = SQLite3.Open(DbFilePath, connectionFlags, null);
 
@@ -114,7 +124,8 @@ namespace Emby.Server.Implementations.Data
                     db.ExecuteAll(string.Join(";", queries.ToArray()));
                 }
             }
-            else*/ if (queries.Count > 0)
+            else*/
+            if (queries.Count > 0)
             {
                 db.ExecuteAll(string.Join(";", queries.ToArray()));
             }
@@ -122,6 +133,26 @@ namespace Emby.Server.Implementations.Data
             return db;
         }
 
+        public IStatement PrepareStatement(IDatabaseConnection connection, string sql)
+        {
+            return connection.PrepareStatement(sql);
+        }
+
+        public IStatement PrepareStatementSafe(IDatabaseConnection connection, string sql)
+        {
+            return connection.PrepareStatement(sql);
+        }
+
+        public List<IStatement> PrepareAll(IDatabaseConnection connection, string sql)
+        {
+            return connection.PrepareAll(sql).ToList();
+        }
+
+        public List<IStatement> PrepareAllSafe(IDatabaseConnection connection, string sql)
+        {
+            return connection.PrepareAll(sql).ToList();
+        }
+
         protected void RunDefaultInitialization(IDatabaseConnection db)
         {
             var queries = new List<string>
@@ -288,12 +319,69 @@ namespace Emby.Server.Implementations.Data
             }
         }
 
+        public class DummyToken : IDisposable
+        {
+            public void Dispose()
+            {
+            }
+        }
+
         public static IDisposable Read(this ReaderWriterLockSlim obj)
         {
-            return new ReadLockToken(obj);
+            //if (BaseSqliteRepository.ThreadSafeMode > 0)
+            //{
+            //    return new DummyToken();
+            //}
+            return new WriteLockToken(obj);
         }
         public static IDisposable Write(this ReaderWriterLockSlim obj)
         {
+            //if (BaseSqliteRepository.ThreadSafeMode > 0)
+            //{
+            //    return new DummyToken();
+            //}
+            return new WriteLockToken(obj);
+        }
+    }
+
+    public static class SemaphpreSlimExtensions
+    {
+        private sealed class WriteLockToken : IDisposable
+        {
+            private SemaphoreSlim _sync;
+            public WriteLockToken(SemaphoreSlim sync)
+            {
+                _sync = sync;
+                var task = sync.WaitAsync();
+                Task.WaitAll(task);
+            }
+            public void Dispose()
+            {
+                if (_sync != null)
+                {
+                    _sync.Release();
+                    _sync = null;
+                }
+            }
+        }
+
+        public class DummyToken : IDisposable
+        {
+            public void Dispose()
+            {
+            }
+        }
+
+        public static IDisposable Read(this SemaphoreSlim obj)
+        {
+            return Write(obj);
+        }
+        public static IDisposable Write(this SemaphoreSlim obj)
+        {
+            //if (BaseSqliteRepository.ThreadSafeMode > 0)
+            //{
+            //    return new DummyToken();
+            //}
             return new WriteLockToken(obj);
         }
     }

+ 8 - 8
Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs

@@ -88,9 +88,9 @@ namespace Emby.Server.Implementations.Data
 
             cancellationToken.ThrowIfCancellationRequested();
 
-            using (var connection = CreateConnection())
+            using (WriteLock.Write())
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     connection.RunInTransaction(db =>
                     {
@@ -132,9 +132,9 @@ namespace Emby.Server.Implementations.Data
 
             cancellationToken.ThrowIfCancellationRequested();
 
-            using (var connection = CreateConnection())
+            using (WriteLock.Write())
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     connection.RunInTransaction(db =>
                     {
@@ -164,9 +164,9 @@ namespace Emby.Server.Implementations.Data
 
             var guidId = displayPreferencesId.GetMD5();
 
-            using (var connection = CreateConnection(true))
+            using (WriteLock.Read())
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
                     using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where id = @id and userId=@userId and client=@client"))
                     {
@@ -198,9 +198,9 @@ namespace Emby.Server.Implementations.Data
         {
             var list = new List<DisplayPreferences>();
 
-            using (var connection = CreateConnection(true))
+            using (WriteLock.Read())
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
                     using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where userId=@userId"))
                     {

+ 10 - 10
Emby.Server.Implementations/Data/SqliteFileOrganizationRepository.cs

@@ -52,9 +52,9 @@ namespace Emby.Server.Implementations.Data
 
             cancellationToken.ThrowIfCancellationRequested();
 
-            using (var connection = CreateConnection())
+            using (WriteLock.Write())
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     connection.RunInTransaction(db =>
                     {
@@ -92,9 +92,9 @@ namespace Emby.Server.Implementations.Data
                 throw new ArgumentNullException("id");
             }
 
-            using (var connection = CreateConnection())
+            using (WriteLock.Write())
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     connection.RunInTransaction(db =>
                     {
@@ -110,9 +110,9 @@ namespace Emby.Server.Implementations.Data
 
         public async Task DeleteAll()
         {
-            using (var connection = CreateConnection())
+            using (WriteLock.Write())
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     connection.RunInTransaction(db =>
                     {
@@ -131,9 +131,9 @@ namespace Emby.Server.Implementations.Data
                 throw new ArgumentNullException("query");
             }
 
-            using (var connection = CreateConnection(true))
+            using (WriteLock.Read())
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
                     var commandText = "SELECT ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths from FileOrganizerResults";
 
@@ -182,9 +182,9 @@ namespace Emby.Server.Implementations.Data
                 throw new ArgumentNullException("id");
             }
 
-            using (var connection = CreateConnection(true))
+            using (WriteLock.Read())
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
                     using (var statement = connection.PrepareStatement("select ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths from FileOrganizerResults where ResultId=@ResultId"))
                     {

+ 103 - 94
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -99,14 +99,6 @@ namespace Emby.Server.Implementations.Data
             DbFilePath = Path.Combine(_config.ApplicationPaths.DataPath, "library.db");
         }
 
-        protected override bool AllowLockRecursion
-        {
-            get
-            {
-                return true;
-            }
-        }
-
         private const string ChaptersTableName = "Chapters2";
 
         protected override int? CacheSize
@@ -387,7 +379,7 @@ namespace Emby.Server.Implementations.Data
 
             userDataRepo.Initialize(WriteLock);
 
-            _backgroundConnection = CreateConnection(true);
+            //_backgroundConnection = CreateConnection(true);
 
             _shrinkMemoryTimer = _timerFactory.Create(OnShrinkMemoryTimerCallback, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(30));
         }
@@ -396,9 +388,9 @@ namespace Emby.Server.Implementations.Data
         {
             try
             {
-                using (var connection = CreateConnection())
+                lock (WriteLock)
                 {
-                    using (WriteLock.Write())
+                    using (var connection = CreateConnection())
                     {
                         connection.RunQueries(new string[]
                         {
@@ -682,19 +674,23 @@ namespace Emby.Server.Implementations.Data
 
             CheckDisposed();
 
-            var tuples = new List<Tuple<BaseItem, List<Guid>>>();
+            var tuples = new List<Tuple<BaseItem, List<Guid>, BaseItem, string>>();
             foreach (var item in items)
             {
                 var ancestorIds = item.SupportsAncestors ?
                     item.GetAncestorIds().Distinct().ToList() :
                     null;
 
-                tuples.Add(new Tuple<BaseItem, List<Guid>>(item, ancestorIds));
+                var topParent = item.GetTopParent();
+
+                var userdataKey = item.GetUserDataKeys().FirstOrDefault();
+
+                tuples.Add(new Tuple<BaseItem, List<Guid>, BaseItem, string>(item, ancestorIds, topParent, userdataKey));
             }
 
-            using (var connection = CreateConnection())
+            lock (WriteLock)
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     connection.RunInTransaction(db =>
                     {
@@ -704,11 +700,11 @@ namespace Emby.Server.Implementations.Data
             }
         }
 
-        private void SaveItemsInTranscation(IDatabaseConnection db, List<Tuple<BaseItem, List<Guid>>> tuples)
+        private void SaveItemsInTranscation(IDatabaseConnection db, List<Tuple<BaseItem, List<Guid>, BaseItem, string>> tuples)
         {
             var requiresReset = false;
 
-            var statements = db.PrepareAll(string.Join(";", new string[]
+            var statements = PrepareAll(db, string.Join(";", new string[]
             {
                 GetSaveItemCommandText(),
                 "delete from AncestorIds where ItemId=@ItemId",
@@ -729,8 +725,10 @@ namespace Emby.Server.Implementations.Data
                             }
 
                             var item = tuple.Item1;
+                            var topParent = tuple.Item3;
+                            var userDataKey = tuple.Item4;
 
-                            SaveItem(item, saveItemStatement);
+                            SaveItem(item, topParent, userDataKey, saveItemStatement);
                             //Logger.Debug(_saveItemCommand.CommandText);
 
                             if (item.SupportsAncestors)
@@ -747,7 +745,7 @@ namespace Emby.Server.Implementations.Data
             }
         }
 
-        private void SaveItem(BaseItem item, IStatement saveItemStatement)
+        private void SaveItem(BaseItem item, BaseItem topParent, string userDataKey, IStatement saveItemStatement)
         {
             saveItemStatement.TryBind("@guid", item.Id);
             saveItemStatement.TryBind("@type", item.GetType().FullName);
@@ -840,7 +838,7 @@ namespace Emby.Server.Implementations.Data
                 saveItemStatement.TryBindNull("@Genres");
             }
 
-            saveItemStatement.TryBind("@InheritedParentalRatingValue", item.GetInheritedParentalRatingValue() ?? 0);
+            saveItemStatement.TryBind("@InheritedParentalRatingValue", item.InheritedParentalRatingValue);
 
             saveItemStatement.TryBind("@SortName", item.SortName);
             saveItemStatement.TryBind("@RunTimeTicks", item.RunTimeTicks);
@@ -922,7 +920,6 @@ namespace Emby.Server.Implementations.Data
 
             saveItemStatement.TryBind("@UnratedType", item.GetBlockUnratedType().ToString());
 
-            var topParent = item.GetTopParent();
             if (topParent != null)
             {
                 //Logger.Debug("Item {0} has top parent {1}", item.Id, topParent.Id);
@@ -957,7 +954,7 @@ namespace Emby.Server.Implementations.Data
             saveItemStatement.TryBind("@CriticRating", item.CriticRating);
             saveItemStatement.TryBind("@CriticRatingSummary", item.CriticRatingSummary);
 
-            var inheritedTags = item.GetInheritedTags();
+            var inheritedTags = item.InheritedTags;
             if (inheritedTags.Count > 0)
             {
                 saveItemStatement.TryBind("@InheritedTags", string.Join("|", inheritedTags.ToArray()));
@@ -976,7 +973,7 @@ namespace Emby.Server.Implementations.Data
                 saveItemStatement.TryBind("@CleanName", GetCleanValue(item.Name));
             }
 
-            saveItemStatement.TryBind("@PresentationUniqueKey", item.GetPresentationUniqueKey());
+            saveItemStatement.TryBind("@PresentationUniqueKey", item.PresentationUniqueKey);
             saveItemStatement.TryBind("@SlugName", item.SlugName);
             saveItemStatement.TryBind("@OriginalTitle", item.OriginalTitle);
 
@@ -1013,7 +1010,14 @@ namespace Emby.Server.Implementations.Data
                 saveItemStatement.TryBindNull("@SeriesName");
             }
 
-            saveItemStatement.TryBind("@UserDataKey", item.GetUserDataKeys().FirstOrDefault());
+            if (string.IsNullOrWhiteSpace(userDataKey))
+            {
+                saveItemStatement.TryBindNull("@UserDataKey");
+            }
+            else
+            {
+                saveItemStatement.TryBind("@UserDataKey", userDataKey);
+            }
 
             var episode = item as Episode;
             if (episode != null)
@@ -1253,11 +1257,11 @@ namespace Emby.Server.Implementations.Data
 
             CheckDisposed();
             //Logger.Info("Retrieving item {0}", id.ToString("N"));
-            using (var connection = CreateConnection(true))
+            lock (WriteLock)
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
-                    using (var statement = connection.PrepareStatement("select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems where guid = @guid"))
+                    using (var statement = PrepareStatementSafe(connection, "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems where guid = @guid"))
                     {
                         statement.TryBind("@guid", id);
 
@@ -2079,11 +2083,11 @@ namespace Emby.Server.Implementations.Data
 
             var list = new List<ChapterInfo>();
 
-            using (var connection = CreateConnection(true))
+            lock (WriteLock)
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
-                    using (var statement = connection.PrepareStatement("select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId order by ChapterIndex asc"))
+                    using (var statement = PrepareStatementSafe(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId order by ChapterIndex asc"))
                     {
                         statement.TryBind("@ItemId", id);
 
@@ -2113,11 +2117,11 @@ namespace Emby.Server.Implementations.Data
                 throw new ArgumentNullException("id");
             }
 
-            using (var connection = CreateConnection(true))
+            lock (WriteLock)
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
-                    using (var statement = connection.PrepareStatement("select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId and ChapterIndex=@ChapterIndex"))
+                    using (var statement = PrepareStatementSafe(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId and ChapterIndex=@ChapterIndex"))
                     {
                         statement.TryBind("@ItemId", id);
                         statement.TryBind("@ChapterIndex", index);
@@ -2194,16 +2198,16 @@ namespace Emby.Server.Implementations.Data
 
             var index = 0;
 
-            using (var connection = CreateConnection())
+            lock (WriteLock)
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     connection.RunInTransaction(db =>
                     {
                         // First delete chapters
                         db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", id.ToGuidParamValue());
 
-                        using (var saveChapterStatement = db.PrepareStatement("replace into " + ChaptersTableName + " (ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath, ImageDateModified) values (@ItemId, @ChapterIndex, @StartPositionTicks, @Name, @ImagePath, @ImageDateModified)"))
+                        using (var saveChapterStatement = PrepareStatement(db, "replace into " + ChaptersTableName + " (ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath, ImageDateModified) values (@ItemId, @ChapterIndex, @StartPositionTicks, @Name, @ImagePath, @ImageDateModified)"))
                         {
                             foreach (var chapter in chapters)
                             {
@@ -2488,11 +2492,11 @@ namespace Emby.Server.Implementations.Data
                 }
             }
 
-            using (var connection = CreateConnection(true))
+            lock (WriteLock)
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
-                    using (var statement = connection.PrepareStatement(commandText))
+                    using (var statement = PrepareStatementSafe(connection, commandText))
                     {
                         if (EnableJoinUserData(query))
                         {
@@ -2513,9 +2517,9 @@ namespace Emby.Server.Implementations.Data
                             }
                         }
                     }
-
-                    LogQueryTime("GetItemList", commandText, now);
                 }
+
+                LogQueryTime("GetItemList", commandText, now);
             }
 
             // Hack for right now since we currently don't support filtering out these duplicates within a query
@@ -2683,11 +2687,11 @@ namespace Emby.Server.Implementations.Data
                 statementTexts.Add(commandText);
             }
 
-            using (var connection = CreateConnection(true))
+            lock (WriteLock)
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
-                    var statements = connection.PrepareAll(string.Join(";", statementTexts.ToArray()))
+                    var statements = PrepareAllSafe(connection, string.Join(";", statementTexts.ToArray()))
                         .ToList();
 
                     if (!isReturningZeroItems)
@@ -2902,11 +2906,11 @@ namespace Emby.Server.Implementations.Data
 
             var list = new List<Guid>();
 
-            using (var connection = CreateConnection(true))
+            lock (WriteLock)
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
-                    using (var statement = connection.PrepareStatement(commandText))
+                    using (var statement = PrepareStatementSafe(connection, commandText))
                     {
                         if (EnableJoinUserData(query))
                         {
@@ -2923,11 +2927,11 @@ namespace Emby.Server.Implementations.Data
                             list.Add(row[0].ReadGuid());
                         }
                     }
+                }
 
-                    LogQueryTime("GetItemList", commandText, now);
+                LogQueryTime("GetItemList", commandText, now);
 
-                    return list;
-                }
+                return list;
             }
         }
 
@@ -2973,11 +2977,11 @@ namespace Emby.Server.Implementations.Data
 
             var list = new List<Tuple<Guid, string>>();
 
-            using (var connection = CreateConnection(true))
+            lock (WriteLock)
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
-                    using (var statement = connection.PrepareStatement(commandText))
+                    using (var statement = PrepareStatementSafe(connection, commandText))
                     {
                         if (EnableJoinUserData(query))
                         {
@@ -2999,11 +3003,11 @@ namespace Emby.Server.Implementations.Data
                             list.Add(new Tuple<Guid, string>(id, path));
                         }
                     }
+                }
 
-                    LogQueryTime("GetItemIdsWithPath", commandText, now);
+                LogQueryTime("GetItemIdsWithPath", commandText, now);
 
-                    return list;
-                }
+                return list;
             }
         }
 
@@ -3087,13 +3091,13 @@ namespace Emby.Server.Implementations.Data
                 statementTexts.Add(commandText);
             }
 
-            using (var connection = CreateConnection(true))
+            lock (WriteLock)
             {
-                var statements = connection.PrepareAll(string.Join(";", statementTexts.ToArray()))
-                    .ToList();
-
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
+                    var statements = PrepareAllSafe(connection, string.Join(";", statementTexts.ToArray()))
+                        .ToList();
+
                     var totalRecordCount = 0;
 
                     if (!isReturningZeroItems)
@@ -4457,9 +4461,9 @@ namespace Emby.Server.Implementations.Data
 
             var commandText = "select Guid,InheritedTags,(select group_concat(Tags, '|') from TypedBaseItems where (guid=outer.guid) OR (guid in (Select AncestorId from AncestorIds where ItemId=Outer.guid))) as NewInheritedTags from typedbaseitems as Outer where NewInheritedTags <> InheritedTags";
 
-            using (var connection = CreateConnection())
+            lock (WriteLock)
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     foreach (var row in connection.Query(commandText))
                     {
@@ -4476,7 +4480,7 @@ namespace Emby.Server.Implementations.Data
                     }
 
                     // write lock here
-                    using (var statement = connection.PrepareStatement("Update TypedBaseItems set InheritedTags=@InheritedTags where Guid=@Guid"))
+                    using (var statement = PrepareStatement(connection, "Update TypedBaseItems set InheritedTags=@InheritedTags where Guid=@Guid"))
                     {
                         foreach (var item in newValues)
                         {
@@ -4531,9 +4535,9 @@ namespace Emby.Server.Implementations.Data
 
             CheckDisposed();
 
-            using (var connection = CreateConnection())
+            lock (WriteLock)
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     connection.RunInTransaction(db =>
                     {
@@ -4561,7 +4565,7 @@ namespace Emby.Server.Implementations.Data
 
         private void ExecuteWithSingleParam(IDatabaseConnection db, string query, byte[] value)
         {
-            using (var statement = db.PrepareStatement(query))
+            using (var statement = PrepareStatement(db, query))
             {
                 statement.TryBind("@Id", value);
 
@@ -4591,11 +4595,11 @@ namespace Emby.Server.Implementations.Data
 
             var list = new List<string>();
 
-            using (var connection = CreateConnection(true))
+            lock (WriteLock)
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
-                    using (var statement = connection.PrepareStatement(commandText))
+                    using (var statement = PrepareStatementSafe(connection, commandText))
                     {
                         // Run this again to bind the params
                         GetPeopleWhereClauses(query, statement);
@@ -4605,8 +4609,8 @@ namespace Emby.Server.Implementations.Data
                             list.Add(row.GetString(0));
                         }
                     }
-                    return list;
                 }
+                return list;
             }
         }
 
@@ -4632,11 +4636,11 @@ namespace Emby.Server.Implementations.Data
 
             var list = new List<PersonInfo>();
 
-            using (var connection = CreateConnection(true))
+            lock (WriteLock)
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
-                    using (var statement = connection.PrepareStatement(commandText))
+                    using (var statement = PrepareStatementSafe(connection, commandText))
                     {
                         // Run this again to bind the params
                         GetPeopleWhereClauses(query, statement);
@@ -4847,15 +4851,18 @@ namespace Emby.Server.Implementations.Data
 
             commandText += " Group By CleanValue";
 
-            using (var connection = CreateConnection(true))
+            lock (WriteLock)
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
-                    foreach (var row in connection.Query(commandText))
+                    using (var statement = PrepareStatementSafe(connection, commandText))
                     {
-                        if (!row.IsDBNull(0))
+                        foreach (var row in statement.ExecuteQuery())
                         {
-                            list.Add(row.GetString(0));
+                            if (!row.IsDBNull(0))
+                            {
+                                list.Add(row.GetString(0));
+                            }
                         }
                     }
                 }
@@ -5023,11 +5030,11 @@ namespace Emby.Server.Implementations.Data
                 statementTexts.Add(countText);
             }
 
-            using (var connection = CreateConnection(true))
+            lock (WriteLock)
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
-                    var statements = connection.PrepareAll(string.Join(";", statementTexts.ToArray())).ToList();
+                    var statements = PrepareAllSafe(connection, string.Join(";", statementTexts.ToArray())).ToList();
 
                     if (!isReturningZeroItems)
                     {
@@ -5208,7 +5215,7 @@ namespace Emby.Server.Implementations.Data
             // First delete 
             db.Execute("delete from ItemValues where ItemId=@Id", itemId.ToGuidParamValue());
 
-            using (var statement = db.PrepareStatement("insert into ItemValues (ItemId, Type, Value, CleanValue) values (@ItemId, @Type, @Value, @CleanValue)"))
+            using (var statement = PrepareStatement(db, "insert into ItemValues (ItemId, Type, Value, CleanValue) values (@ItemId, @Type, @Value, @CleanValue)"))
             {
                 foreach (var pair in values)
                 {
@@ -5246,16 +5253,17 @@ namespace Emby.Server.Implementations.Data
 
             CheckDisposed();
 
-            using (var connection = CreateConnection())
+            lock (WriteLock)
             {
-                using (WriteLock.Write())
-                { // First delete 
+                using (var connection = CreateConnection())
+                {
+                    // First delete 
                     // "delete from People where ItemId=?"
                     connection.Execute("delete from People where ItemId=?", itemId.ToGuidParamValue());
 
                     var listIndex = 0;
 
-                    using (var statement = connection.PrepareStatement(
+                    using (var statement = PrepareStatement(connection,
                                 "insert into People (ItemId, Name, Role, PersonType, SortOrder, ListOrder) values (@ItemId, @Name, @Role, @PersonType, @SortOrder, @ListOrder)"))
                     {
                         foreach (var person in people)
@@ -5332,11 +5340,11 @@ namespace Emby.Server.Implementations.Data
 
             cmdText += " order by StreamIndex ASC";
 
-            using (var connection = CreateConnection(true))
+            lock (WriteLock)
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
-                    using (var statement = connection.PrepareStatement(cmdText))
+                    using (var statement = PrepareStatementSafe(connection, cmdText))
                     {
                         statement.TryBind("@ItemId", query.ItemId.ToGuidParamValue());
 
@@ -5377,13 +5385,14 @@ namespace Emby.Server.Implementations.Data
 
             cancellationToken.ThrowIfCancellationRequested();
 
-            using (var connection = CreateConnection())
+            lock (WriteLock)
             {
-                using (WriteLock.Write())
-                {     // First delete chapters
+                using (var connection = CreateConnection())
+                {
+                    // First delete chapters
                     connection.Execute("delete from mediastreams where ItemId=@ItemId", id.ToGuidParamValue());
 
-                    using (var statement = connection.PrepareStatement(string.Format("replace into mediastreams ({0}) values ({1})",
+                    using (var statement = PrepareStatement(connection, string.Format("replace into mediastreams ({0}) values ({1})",
                                 string.Join(",", _mediaStreamSaveColumns),
                                 string.Join(",", _mediaStreamSaveColumns.Select(i => "@" + i).ToArray()))))
                     {

+ 8 - 8
Emby.Server.Implementations/Data/SqliteUserDataRepository.cs

@@ -188,9 +188,9 @@ namespace Emby.Server.Implementations.Data
         {
             cancellationToken.ThrowIfCancellationRequested();
 
-            using (var connection = CreateConnection())
+            lock (WriteLock)
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     connection.RunInTransaction(db =>
                     {
@@ -259,9 +259,9 @@ namespace Emby.Server.Implementations.Data
         {
             cancellationToken.ThrowIfCancellationRequested();
 
-            using (var connection = CreateConnection())
+            lock (WriteLock)
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     connection.RunInTransaction(db =>
                     {
@@ -296,9 +296,9 @@ namespace Emby.Server.Implementations.Data
                 throw new ArgumentNullException("key");
             }
 
-            using (var connection = CreateConnection(true))
+            lock (WriteLock)
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
                     using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from userdata where key =@Key and userId=@UserId"))
                     {
@@ -349,9 +349,9 @@ namespace Emby.Server.Implementations.Data
 
             var list = new List<UserItemData>();
 
-            using (var connection = CreateConnection())
+            lock (WriteLock)
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection())
                 {
                     using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from userdata where userId=@UserId"))
                     {

+ 6 - 6
Emby.Server.Implementations/Data/SqliteUserRepository.cs

@@ -85,9 +85,9 @@ namespace Emby.Server.Implementations.Data
 
             cancellationToken.ThrowIfCancellationRequested();
 
-            using (var connection = CreateConnection())
+            using (WriteLock.Write())
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     connection.RunInTransaction(db =>
                     {
@@ -110,9 +110,9 @@ namespace Emby.Server.Implementations.Data
         {
             var list = new List<User>();
 
-            using (var connection = CreateConnection(true))
+            using (WriteLock.Read())
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
                     foreach (var row in connection.Query("select guid,data from users"))
                     {
@@ -148,9 +148,9 @@ namespace Emby.Server.Implementations.Data
 
             cancellationToken.ThrowIfCancellationRequested();
 
-            using (var connection = CreateConnection())
+            using (WriteLock.Write())
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     connection.RunInTransaction(db =>
                     {

+ 18 - 18
Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs

@@ -51,24 +51,24 @@ namespace Emby.Server.Implementations.Notifications
         {
             var result = new NotificationResult();
 
-            using (var connection = CreateConnection(true))
-            {
-                using (WriteLock.Read())
-                {
-                    var clauses = new List<string>();
-                    var paramList = new List<object>();
+            var clauses = new List<string>();
+            var paramList = new List<object>();
 
-                    if (query.IsRead.HasValue)
-                    {
-                        clauses.Add("IsRead=?");
-                        paramList.Add(query.IsRead.Value);
-                    }
+            if (query.IsRead.HasValue)
+            {
+                clauses.Add("IsRead=?");
+                paramList.Add(query.IsRead.Value);
+            }
 
-                    clauses.Add("UserId=?");
-                    paramList.Add(query.UserId.ToGuidParamValue());
+            clauses.Add("UserId=?");
+            paramList.Add(query.UserId.ToGuidParamValue());
 
-                    var whereClause = " where " + string.Join(" And ", clauses.ToArray());
+            var whereClause = " where " + string.Join(" And ", clauses.ToArray());
 
+            using (var connection = CreateConnection(true))
+            {
+                lock (WriteLock)
+                {
                     result.TotalRecordCount = connection.Query("select count(Id) from Notifications" + whereClause, paramList.ToArray()).SelectScalarInt().First();
 
                     var commandText = string.Format("select Id,UserId,Date,Name,Description,Url,Level,IsRead,Category,RelatedId from Notifications{0} order by IsRead asc, Date desc", whereClause);
@@ -108,7 +108,7 @@ namespace Emby.Server.Implementations.Notifications
 
             using (var connection = CreateConnection(true))
             {
-                using (WriteLock.Read())
+                lock (WriteLock)
                 {
                     using (var statement = connection.PrepareStatement("select Level from Notifications where UserId=@UserId and IsRead=@IsRead"))
                     {
@@ -225,7 +225,7 @@ namespace Emby.Server.Implementations.Notifications
 
             using (var connection = CreateConnection())
             {
-                using (WriteLock.Write())
+                lock (WriteLock)
                 {
                     connection.RunInTransaction(conn =>
                     {
@@ -288,7 +288,7 @@ namespace Emby.Server.Implementations.Notifications
 
             using (var connection = CreateConnection())
             {
-                using (WriteLock.Write())
+                lock (WriteLock)
                 {
                     connection.RunInTransaction(conn =>
                     {
@@ -310,7 +310,7 @@ namespace Emby.Server.Implementations.Notifications
 
             using (var connection = CreateConnection())
             {
-                using (WriteLock.Write())
+                lock (WriteLock)
                 {
                     connection.RunInTransaction(conn =>
                     {

+ 6 - 6
Emby.Server.Implementations/Security/AuthenticationRepository.cs

@@ -66,9 +66,9 @@ namespace Emby.Server.Implementations.Security
 
             cancellationToken.ThrowIfCancellationRequested();
 
-            using (var connection = CreateConnection())
+            using (WriteLock.Write())
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     connection.RunInTransaction(db =>
                     {
@@ -202,9 +202,9 @@ namespace Emby.Server.Implementations.Security
 
             var list = new List<AuthenticationInfo>();
 
-            using (var connection = CreateConnection(true))
+            using (WriteLock.Read())
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
                     using (var statement = connection.PrepareStatement(commandText))
                     {
@@ -241,9 +241,9 @@ namespace Emby.Server.Implementations.Security
                 throw new ArgumentNullException("id");
             }
 
-            using (var connection = CreateConnection(true))
+            using (WriteLock.Read())
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
                     var commandText = BaseSelectText + " where Id=@Id";
 

+ 4 - 4
Emby.Server.Implementations/Social/SharingRepository.cs

@@ -52,9 +52,9 @@ namespace Emby.Server.Implementations.Social
                 throw new ArgumentNullException("info.Id");
             }
 
-            using (var connection = CreateConnection())
+            using (WriteLock.Write())
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     connection.RunInTransaction(db =>
                     {
@@ -77,9 +77,9 @@ namespace Emby.Server.Implementations.Social
                 throw new ArgumentNullException("id");
             }
 
-            using (var connection = CreateConnection(true))
+            using (WriteLock.Read())
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
                     var commandText = "select Id, ItemId, UserId, ExpirationDate from Shares where id = ?";
 

+ 37 - 37
Emby.Server.Implementations/Sync/SyncRepository.cs

@@ -105,9 +105,9 @@ namespace Emby.Server.Implementations.Sync
                 throw new ArgumentNullException("id");
             }
 
-            using (var connection = CreateConnection(true))
+            using (WriteLock.Read())
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
                     var commandText = BaseJobSelectText + " where Id=?";
                     var paramList = new List<object>();
@@ -216,9 +216,9 @@ namespace Emby.Server.Implementations.Sync
 
             CheckDisposed();
 
-            using (var connection = CreateConnection())
+            using (WriteLock.Write())
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     string commandText;
                     var paramList = new List<object>();
@@ -277,9 +277,9 @@ namespace Emby.Server.Implementations.Sync
 
             CheckDisposed();
 
-            using (var connection = CreateConnection())
+            using (WriteLock.Write())
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     connection.RunInTransaction(conn =>
                     {
@@ -299,9 +299,9 @@ namespace Emby.Server.Implementations.Sync
 
             CheckDisposed();
 
-            using (var connection = CreateConnection(true))
+            using (WriteLock.Read())
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
                     var commandText = BaseJobSelectText;
                     var paramList = new List<object>();
@@ -399,9 +399,9 @@ namespace Emby.Server.Implementations.Sync
 
             var guid = new Guid(id);
 
-            using (var connection = CreateConnection(true))
+            using (WriteLock.Read())
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
                     var commandText = BaseJobItemSelectText + " where Id=?";
                     var paramList = new List<object>();
@@ -425,9 +425,9 @@ namespace Emby.Server.Implementations.Sync
                 throw new ArgumentNullException("query");
             }
 
-            using (var connection = CreateConnection(true))
+            using (WriteLock.Read())
             {
-                using (WriteLock.Read())
+                using (var connection = CreateConnection(true))
                 {
                     var commandText = baseSelectText;
                     var paramList = new List<object>();
@@ -505,41 +505,41 @@ namespace Emby.Server.Implementations.Sync
 
             var now = DateTime.UtcNow;
 
-            using (var connection = CreateConnection(true))
+            using (WriteLock.Read())
             {
-                var commandText = "select ItemId,Status,Progress from SyncJobItems";
-                var whereClauses = new List<string>();
-
-                if (!string.IsNullOrWhiteSpace(query.TargetId))
+                using (var connection = CreateConnection(true))
                 {
-                    whereClauses.Add("TargetId=@TargetId");
-                }
+                    var commandText = "select ItemId,Status,Progress from SyncJobItems";
+                    var whereClauses = new List<string>();
 
-                if (query.Statuses.Length > 0)
-                {
-                    var statuses = string.Join(",", query.Statuses.Select(i => "'" + i.ToString() + "'").ToArray());
+                    if (!string.IsNullOrWhiteSpace(query.TargetId))
+                    {
+                        whereClauses.Add("TargetId=@TargetId");
+                    }
 
-                    whereClauses.Add(string.Format("Status in ({0})", statuses));
-                }
+                    if (query.Statuses.Length > 0)
+                    {
+                        var statuses = string.Join(",", query.Statuses.Select(i => "'" + i.ToString() + "'").ToArray());
 
-                if (whereClauses.Count > 0)
-                {
-                    commandText += " where " + string.Join(" AND ", whereClauses.ToArray());
-                }
+                        whereClauses.Add(string.Format("Status in ({0})", statuses));
+                    }
+
+                    if (whereClauses.Count > 0)
+                    {
+                        commandText += " where " + string.Join(" AND ", whereClauses.ToArray());
+                    }
 
-                var statementTexts = new List<string>
+                    var statementTexts = new List<string>
                 {
                     commandText
                 };
 
-                commandText = commandText
-                    .Replace("select ItemId,Status,Progress from SyncJobItems", "select ItemIds,Status,Progress from SyncJobs")
-                    .Replace("'Synced'", "'Completed','CompletedWithError'");
+                    commandText = commandText
+                        .Replace("select ItemId,Status,Progress from SyncJobItems", "select ItemIds,Status,Progress from SyncJobs")
+                        .Replace("'Synced'", "'Completed','CompletedWithError'");
 
-                statementTexts.Add(commandText);
+                    statementTexts.Add(commandText);
 
-                using (WriteLock.Read())
-                {
                     var statements = connection.PrepareAll(string.Join(";", statementTexts.ToArray()))
                         .ToList();
 
@@ -692,9 +692,9 @@ namespace Emby.Server.Implementations.Sync
 
             CheckDisposed();
 
-            using (var connection = CreateConnection())
+            using (WriteLock.Write())
             {
-                using (WriteLock.Write())
+                using (var connection = CreateConnection())
                 {
                     string commandText;
 

+ 1 - 5
MediaBrowser.Controller/Entities/TV/Season.cs

@@ -48,11 +48,7 @@ namespace MediaBrowser.Controller.Entities.TV
         [IgnoreDataMember]
         public override Guid? DisplayParentId
         {
-            get
-            {
-                var series = Series;
-                return series == null ? ParentId : series.Id;
-            }
+            get { return SeriesId; }
         }
 
         [IgnoreDataMember]