From 858dadcdd1caadb5fa8cc13a02eb227098f39c3c Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 14 Apr 2023 13:43:56 +0200 Subject: [PATCH 1/6] POC sql connection pool --- .../ApplicationHost.cs | 6 +- .../Data/BaseSqliteRepository.cs | 115 +++++++++++++----- .../Data/ConnectionPool.cs | 68 +++++++++++ .../Data/ManagedConnection.cs | 13 +- .../Data/SqliteItemRepository.cs | 9 +- .../Data/SqliteUserDataRepository.cs | 38 ++---- 6 files changed, 174 insertions(+), 75 deletions(-) create mode 100644 Emby.Server.Implementations/Data/ConnectionPool.cs diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 080c44829..7969577bc 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -627,6 +627,9 @@ namespace Emby.Server.Implementations } } + ((SqliteItemRepository)Resolve()).Initialize(); + ((SqliteUserDataRepository)Resolve()).Initialize(); + var localizationManager = (LocalizationManager)Resolve(); await localizationManager.LoadAll().ConfigureAwait(false); @@ -634,9 +637,6 @@ namespace Emby.Server.Implementations SetStaticProperties(); - var userDataRepo = (SqliteUserDataRepository)Resolve(); - ((SqliteItemRepository)Resolve()).Initialize(userDataRepo, Resolve()); - FindParts(); } diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index bc520b86e..859a3c746 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Threading; using Jellyfin.Extensions; using Microsoft.Extensions.Logging; using SQLitePCL.pretty; @@ -27,9 +26,19 @@ namespace Emby.Server.Implementations.Data /// /// Gets or sets the path to the DB file. /// - /// Path to the DB file. protected string DbFilePath { get; set; } + /// + /// Gets or sets the number of write connections to create. + /// + /// Path to the DB file. + protected int WriteConnectionsCount { get; set; } = 1; + + /// + /// Gets or sets the number of read connections to create. + /// + protected int ReadConnectionsCount { get; set; } = 1; + /// /// Gets the logger. /// @@ -63,7 +72,7 @@ namespace Emby.Server.Implementations.Data /// /// Gets the locking mode. . /// - protected virtual string LockingMode => "EXCLUSIVE"; + protected virtual string LockingMode => "NORMAL"; /// /// Gets the journal mode. . @@ -88,7 +97,7 @@ namespace Emby.Server.Implementations.Data /// /// The temp store mode. /// - protected virtual TempStoreMode TempStore => TempStoreMode.Default; + protected virtual TempStoreMode TempStore => TempStoreMode.Memory; /// /// Gets the synchronous mode. @@ -101,63 +110,115 @@ namespace Emby.Server.Implementations.Data /// Gets or sets the write lock. /// /// The write lock. - protected SemaphoreSlim WriteLock { get; set; } = new SemaphoreSlim(1, 1); + protected ConnectionPool WriteConnections { get; set; } /// /// Gets or sets the write connection. /// /// The write connection. - protected SQLiteDatabaseConnection WriteConnection { get; set; } + protected ConnectionPool ReadConnections { get; set; } + + public virtual void Initialize() + { + WriteConnections = new ConnectionPool(WriteConnectionsCount, CreateWriteConnection); + ReadConnections = new ConnectionPool(ReadConnectionsCount, CreateReadConnection); + } protected ManagedConnection GetConnection(bool readOnly = false) { - WriteLock.Wait(); - if (WriteConnection is not null) + if (readOnly) { - return new ManagedConnection(WriteConnection, WriteLock); + return ReadConnections.GetConnection(); } - WriteConnection = SQLite3.Open( + return WriteConnections.GetConnection(); + } + + protected SQLiteDatabaseConnection CreateWriteConnection() + { + var writeConnection = SQLite3.Open( DbFilePath, DefaultConnectionFlags | ConnectionFlags.Create | ConnectionFlags.ReadWrite, null); if (CacheSize.HasValue) { - WriteConnection.Execute("PRAGMA cache_size=" + CacheSize.Value); + writeConnection.Execute("PRAGMA cache_size=" + CacheSize.Value); } if (!string.IsNullOrWhiteSpace(LockingMode)) { - WriteConnection.Execute("PRAGMA locking_mode=" + LockingMode); + writeConnection.Execute("PRAGMA locking_mode=" + LockingMode); } if (!string.IsNullOrWhiteSpace(JournalMode)) { - WriteConnection.Execute("PRAGMA journal_mode=" + JournalMode); + writeConnection.Execute("PRAGMA journal_mode=" + JournalMode); } if (JournalSizeLimit.HasValue) { - WriteConnection.Execute("PRAGMA journal_size_limit=" + JournalSizeLimit.Value); + writeConnection.Execute("PRAGMA journal_size_limit=" + JournalSizeLimit.Value); } if (Synchronous.HasValue) { - WriteConnection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value); + writeConnection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value); } if (PageSize.HasValue) { - WriteConnection.Execute("PRAGMA page_size=" + PageSize.Value); + writeConnection.Execute("PRAGMA page_size=" + PageSize.Value); } - WriteConnection.Execute("PRAGMA temp_store=" + (int)TempStore); + writeConnection.Execute("PRAGMA temp_store=" + (int)TempStore); // Configuration and pragmas can affect VACUUM so it needs to be last. - WriteConnection.Execute("VACUUM"); + writeConnection.Execute("VACUUM"); - return new ManagedConnection(WriteConnection, WriteLock); + return writeConnection; + } + + protected SQLiteDatabaseConnection CreateReadConnection() + { + var writeConnection = SQLite3.Open( + DbFilePath, + DefaultConnectionFlags | ConnectionFlags.ReadOnly, + null); + + if (CacheSize.HasValue) + { + writeConnection.Execute("PRAGMA cache_size=" + CacheSize.Value); + } + + if (!string.IsNullOrWhiteSpace(LockingMode)) + { + writeConnection.Execute("PRAGMA locking_mode=" + LockingMode); + } + + if (!string.IsNullOrWhiteSpace(JournalMode)) + { + writeConnection.Execute("PRAGMA journal_mode=" + JournalMode); + } + + if (JournalSizeLimit.HasValue) + { + writeConnection.Execute("PRAGMA journal_size_limit=" + JournalSizeLimit.Value); + } + + if (Synchronous.HasValue) + { + writeConnection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value); + } + + if (PageSize.HasValue) + { + writeConnection.Execute("PRAGMA page_size=" + PageSize.Value); + } + + writeConnection.Execute("PRAGMA temp_store=" + (int)TempStore); + + return writeConnection; } public IStatement PrepareStatement(ManagedConnection connection, string sql) @@ -240,22 +301,10 @@ namespace Emby.Server.Implementations.Data if (dispose) { - WriteLock.Wait(); - try - { - WriteConnection?.Dispose(); - } - finally - { - WriteLock.Release(); - } - - WriteLock.Dispose(); + WriteConnections.Dispose(); + ReadConnections.Dispose(); } - WriteConnection = null; - WriteLock = null; - _disposed = true; } } diff --git a/Emby.Server.Implementations/Data/ConnectionPool.cs b/Emby.Server.Implementations/Data/ConnectionPool.cs new file mode 100644 index 000000000..86a125ba5 --- /dev/null +++ b/Emby.Server.Implementations/Data/ConnectionPool.cs @@ -0,0 +1,68 @@ +#pragma warning disable CS1591 + +using System; +using System.Collections.Concurrent; +using System.Threading; +using SQLitePCL.pretty; + +namespace Emby.Server.Implementations.Data; + +public sealed class ConnectionPool : IDisposable +{ + private readonly int _count; + private readonly SemaphoreSlim _lock; + private readonly ConcurrentQueue _connections = new ConcurrentQueue(); + private bool _disposed; + + public ConnectionPool(int count, Func factory) + { + _count = count; + _lock = new SemaphoreSlim(count, count); + for (int i = 0; i < count; i++) + { + _connections.Enqueue(factory.Invoke()); + } + } + + public ManagedConnection GetConnection() + { + _lock.Wait(); + if (!_connections.TryDequeue(out var connection)) + { + _lock.Release(); + throw new InvalidOperationException(); + } + + return new ManagedConnection(connection, this); + } + + public void Return(SQLiteDatabaseConnection connection) + { + _connections.Enqueue(connection); + _lock.Release(); + } + + public void Dispose() + { + if (_disposed) + { + return; + } + + for (int i = 0; i < _count; i++) + { + _lock.Wait(); + if (!_connections.TryDequeue(out var connection)) + { + _lock.Release(); + throw new InvalidOperationException(); + } + + connection.Dispose(); + } + + _lock.Dispose(); + + _disposed = true; + } +} diff --git a/Emby.Server.Implementations/Data/ManagedConnection.cs b/Emby.Server.Implementations/Data/ManagedConnection.cs index 11e33278d..e84ed8f91 100644 --- a/Emby.Server.Implementations/Data/ManagedConnection.cs +++ b/Emby.Server.Implementations/Data/ManagedConnection.cs @@ -2,23 +2,22 @@ using System; using System.Collections.Generic; -using System.Threading; using SQLitePCL.pretty; namespace Emby.Server.Implementations.Data { public sealed class ManagedConnection : IDisposable { - private readonly SemaphoreSlim _writeLock; + private readonly ConnectionPool _pool; - private SQLiteDatabaseConnection? _db; + private SQLiteDatabaseConnection _db; private bool _disposed = false; - public ManagedConnection(SQLiteDatabaseConnection db, SemaphoreSlim writeLock) + public ManagedConnection(SQLiteDatabaseConnection db, ConnectionPool pool) { _db = db; - _writeLock = writeLock; + _pool = pool; } public IStatement PrepareStatement(string sql) @@ -73,9 +72,9 @@ namespace Emby.Server.Implementations.Data return; } - _writeLock.Release(); + _pool.Return(_db); - _db = null; // Don't dispose it + _db = null!; // Don't dispose it _disposed = true; } } diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index e59f2a198..33466c34b 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -336,6 +336,7 @@ namespace Emby.Server.Implementations.Data _jsonOptions = JsonDefaults.Options; DbFilePath = Path.Combine(_config.ApplicationPaths.DataPath, "library.db"); + ReadConnectionsCount = 5; } /// @@ -347,10 +348,10 @@ namespace Emby.Server.Implementations.Data /// /// Opens the connection to the database. /// - /// The user data repository. - /// The user manager. - public void Initialize(SqliteUserDataRepository userDataRepo, IUserManager userManager) + public override void Initialize() { + base.Initialize(); + const string CreateMediaStreamsTableCommand = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, DvVersionMajor INT NULL, DvVersionMinor INT NULL, DvProfile INT NULL, DvLevel INT NULL, RpuPresentFlag INT NULL, ElPresentFlag INT NULL, BlPresentFlag INT NULL, DvBlSignalCompatibilityId INT NULL, IsHearingImpaired BIT NULL, PRIMARY KEY (ItemId, StreamIndex))"; @@ -551,8 +552,6 @@ namespace Emby.Server.Implementations.Data connection.RunQueries(postQueries); } - - userDataRepo.Initialize(userManager, WriteLock, WriteConnection); } public void SaveImages(BaseItem item) diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index 5f2c3c9dc..a1e217ad1 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.IO; using System.Threading; using Jellyfin.Data.Entities; -using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; @@ -18,33 +18,32 @@ namespace Emby.Server.Implementations.Data { public class SqliteUserDataRepository : BaseSqliteRepository, IUserDataRepository { + private readonly IUserManager _userManager; + public SqliteUserDataRepository( ILogger logger, - IApplicationPaths appPaths) + IServerConfigurationManager config, + IUserManager userManager) : base(logger) { - DbFilePath = Path.Combine(appPaths.DataPath, "library.db"); + _userManager = userManager; + + DbFilePath = Path.Combine(config.ApplicationPaths.DataPath, "library.db"); } /// /// Opens the connection to the database. /// - /// The user manager. - /// The lock to use for database IO. - /// The connection to use for database IO. - public void Initialize(IUserManager userManager, SemaphoreSlim dbLock, SQLiteDatabaseConnection dbConnection) + public override void Initialize() { - WriteLock.Dispose(); - WriteLock = dbLock; - WriteConnection?.Dispose(); - WriteConnection = dbConnection; + base.Initialize(); using (var connection = GetConnection()) { var userDatasTableExists = TableExists(connection, "UserDatas"); var userDataTableExists = TableExists(connection, "userdata"); - var users = userDatasTableExists ? null : userManager.Users; + var users = userDatasTableExists ? null : _userManager.Users; connection.RunInTransaction( db => @@ -371,20 +370,5 @@ namespace Emby.Server.Implementations.Data return userData; } - -#pragma warning disable CA2215 - /// - /// - /// There is nothing to dispose here since and - /// are managed by . - /// See . - /// - protected override void Dispose(bool dispose) - { - // The write lock and connection for the item repository are shared with the user data repository - // since they point to the same database. The item repo has responsibility for disposing these two objects, - // so the user data repo should not attempt to dispose them as well - } -#pragma warning restore CA2215 } } From 33f97045f957179c907a22af96dac0261b3651d8 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 14 Apr 2023 21:38:12 +0200 Subject: [PATCH 2/6] Use BlockingCollection --- .../Data/BaseSqliteRepository.cs | 39 +++++++---------- .../Data/ConnectionPool.cs | 42 ++++++++----------- 2 files changed, 33 insertions(+), 48 deletions(-) diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 859a3c746..ce0d03b2b 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -122,17 +122,16 @@ namespace Emby.Server.Implementations.Data { WriteConnections = new ConnectionPool(WriteConnectionsCount, CreateWriteConnection); ReadConnections = new ConnectionPool(ReadConnectionsCount, CreateReadConnection); + + // Configuration and pragmas can affect VACUUM so it needs to be last. + using (var connection = GetConnection(true)) + { + connection.Execute("VACUUM"); + } } protected ManagedConnection GetConnection(bool readOnly = false) - { - if (readOnly) - { - return ReadConnections.GetConnection(); - } - - return WriteConnections.GetConnection(); - } + => readOnly ? ReadConnections.GetConnection() : WriteConnections.GetConnection(); protected SQLiteDatabaseConnection CreateWriteConnection() { @@ -173,52 +172,44 @@ namespace Emby.Server.Implementations.Data writeConnection.Execute("PRAGMA temp_store=" + (int)TempStore); - // Configuration and pragmas can affect VACUUM so it needs to be last. - writeConnection.Execute("VACUUM"); - return writeConnection; } protected SQLiteDatabaseConnection CreateReadConnection() { - var writeConnection = SQLite3.Open( + var connection = SQLite3.Open( DbFilePath, DefaultConnectionFlags | ConnectionFlags.ReadOnly, null); if (CacheSize.HasValue) { - writeConnection.Execute("PRAGMA cache_size=" + CacheSize.Value); + connection.Execute("PRAGMA cache_size=" + CacheSize.Value); } if (!string.IsNullOrWhiteSpace(LockingMode)) { - writeConnection.Execute("PRAGMA locking_mode=" + LockingMode); + connection.Execute("PRAGMA locking_mode=" + LockingMode); } if (!string.IsNullOrWhiteSpace(JournalMode)) { - writeConnection.Execute("PRAGMA journal_mode=" + JournalMode); + connection.Execute("PRAGMA journal_mode=" + JournalMode); } if (JournalSizeLimit.HasValue) { - writeConnection.Execute("PRAGMA journal_size_limit=" + JournalSizeLimit.Value); + connection.Execute("PRAGMA journal_size_limit=" + JournalSizeLimit.Value); } if (Synchronous.HasValue) { - writeConnection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value); + connection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value); } - if (PageSize.HasValue) - { - writeConnection.Execute("PRAGMA page_size=" + PageSize.Value); - } + connection.Execute("PRAGMA temp_store=" + (int)TempStore); - writeConnection.Execute("PRAGMA temp_store=" + (int)TempStore); - - return writeConnection; + return connection; } public IStatement PrepareStatement(ManagedConnection connection, string sql) diff --git a/Emby.Server.Implementations/Data/ConnectionPool.cs b/Emby.Server.Implementations/Data/ConnectionPool.cs index 86a125ba5..091a1b74f 100644 --- a/Emby.Server.Implementations/Data/ConnectionPool.cs +++ b/Emby.Server.Implementations/Data/ConnectionPool.cs @@ -2,44 +2,47 @@ using System; using System.Collections.Concurrent; -using System.Threading; using SQLitePCL.pretty; namespace Emby.Server.Implementations.Data; public sealed class ConnectionPool : IDisposable { - private readonly int _count; - private readonly SemaphoreSlim _lock; - private readonly ConcurrentQueue _connections = new ConcurrentQueue(); + private readonly BlockingCollection _connections = new(); private bool _disposed; public ConnectionPool(int count, Func factory) { - _count = count; - _lock = new SemaphoreSlim(count, count); for (int i = 0; i < count; i++) { - _connections.Enqueue(factory.Invoke()); + _connections.Add(factory.Invoke()); } } public ManagedConnection GetConnection() { - _lock.Wait(); - if (!_connections.TryDequeue(out var connection)) + if (_disposed) { - _lock.Release(); - throw new InvalidOperationException(); + ThrowObjectDisposedException(); } - return new ManagedConnection(connection, this); + return new ManagedConnection(_connections.Take(), this); + + void ThrowObjectDisposedException() + { + throw new ObjectDisposedException(GetType().Name); + } } public void Return(SQLiteDatabaseConnection connection) { - _connections.Enqueue(connection); - _lock.Release(); + if (_disposed) + { + connection.Dispose(); + return; + } + + _connections.Add(connection); } public void Dispose() @@ -49,20 +52,11 @@ public sealed class ConnectionPool : IDisposable return; } - for (int i = 0; i < _count; i++) + foreach (var connection in _connections) { - _lock.Wait(); - if (!_connections.TryDequeue(out var connection)) - { - _lock.Release(); - throw new InvalidOperationException(); - } - connection.Dispose(); } - _lock.Dispose(); - _disposed = true; } } From 8dba3a44fd3f4771acb0cade2c91696179ad21d9 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 14 Apr 2023 22:43:14 +0200 Subject: [PATCH 3/6] Get write connection for vacuum --- Emby.Server.Implementations/Data/BaseSqliteRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index ce0d03b2b..4b9ab53ae 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -124,7 +124,7 @@ namespace Emby.Server.Implementations.Data ReadConnections = new ConnectionPool(ReadConnectionsCount, CreateReadConnection); // Configuration and pragmas can affect VACUUM so it needs to be last. - using (var connection = GetConnection(true)) + using (var connection = GetConnection()) { connection.Execute("VACUUM"); } From 13152bf09dcadf8807e1240be7ca84662d2ed397 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 21 Apr 2023 14:05:27 +0200 Subject: [PATCH 4/6] Change number of read connections based on # of threads and add comments --- .../Data/ConnectionPool.cs | 19 +++++++++++++++++-- .../Data/SqliteItemRepository.cs | 2 +- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Data/ConnectionPool.cs b/Emby.Server.Implementations/Data/ConnectionPool.cs index 091a1b74f..6d28a5e43 100644 --- a/Emby.Server.Implementations/Data/ConnectionPool.cs +++ b/Emby.Server.Implementations/Data/ConnectionPool.cs @@ -1,16 +1,22 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Concurrent; using SQLitePCL.pretty; namespace Emby.Server.Implementations.Data; +/// +/// A pool of SQLite Database connections. +/// public sealed class ConnectionPool : IDisposable { private readonly BlockingCollection _connections = new(); private bool _disposed; + /// + /// Initializes a new instance of the class. + /// + /// The number of database connection to create. + /// Factory function to create the database connections. public ConnectionPool(int count, Func factory) { for (int i = 0; i < count; i++) @@ -19,6 +25,10 @@ public sealed class ConnectionPool : IDisposable } } + /// + /// Gets a database connection from the pool if one is available, otherwise blocks. + /// + /// A database connection. public ManagedConnection GetConnection() { if (_disposed) @@ -34,6 +44,10 @@ public sealed class ConnectionPool : IDisposable } } + /// + /// Return a database connection to the pool. + /// + /// The database connection to return. public void Return(SQLiteDatabaseConnection connection) { if (_disposed) @@ -45,6 +59,7 @@ public sealed class ConnectionPool : IDisposable _connections.Add(connection); } + /// public void Dispose() { if (_disposed) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 33466c34b..22d485d33 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -336,7 +336,7 @@ namespace Emby.Server.Implementations.Data _jsonOptions = JsonDefaults.Options; DbFilePath = Path.Combine(_config.ApplicationPaths.DataPath, "library.db"); - ReadConnectionsCount = 5; + ReadConnectionsCount = Environment.ProcessorCount * 2; } /// From 0d67901e373ff8a2ab279d51c3c1f88adf68bfcf Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 24 Apr 2023 13:08:46 +0200 Subject: [PATCH 5/6] Dispose BlockingCollection --- Emby.Server.Implementations/Data/ConnectionPool.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Emby.Server.Implementations/Data/ConnectionPool.cs b/Emby.Server.Implementations/Data/ConnectionPool.cs index 6d28a5e43..a671eb165 100644 --- a/Emby.Server.Implementations/Data/ConnectionPool.cs +++ b/Emby.Server.Implementations/Data/ConnectionPool.cs @@ -72,6 +72,8 @@ public sealed class ConnectionPool : IDisposable connection.Dispose(); } + _connections.Dispose(); + _disposed = true; } } From 8e1f0d53c1efe286628a4119b3c595f219513d23 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 4 May 2023 14:42:39 +0200 Subject: [PATCH 6/6] nameof instead of GetType().Name --- Emby.Server.Implementations/Data/ConnectionPool.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Data/ConnectionPool.cs b/Emby.Server.Implementations/Data/ConnectionPool.cs index a671eb165..5ea7e934f 100644 --- a/Emby.Server.Implementations/Data/ConnectionPool.cs +++ b/Emby.Server.Implementations/Data/ConnectionPool.cs @@ -38,9 +38,9 @@ public sealed class ConnectionPool : IDisposable return new ManagedConnection(_connections.Take(), this); - void ThrowObjectDisposedException() + static void ThrowObjectDisposedException() { - throw new ObjectDisposedException(GetType().Name); + throw new ObjectDisposedException(nameof(ConnectionPool)); } }