[PR #4223] [CLOSED] Make TvdbClientManager thread safe #9778

Closed
opened 2025-12-22 07:39:57 +01:00 by backuprepo · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/jellyfin/jellyfin/pull/4223
Author: @Spacetech
Created: 9/28/2020
Status: Closed

Base: masterHead: tvdbclient_thread_safety


📝 Commits (4)

  • 49522a4 Make TvdbClientManager thread safe
  • b739475 update based on suggestions
  • 9316336 move WaitAsync outside the try statement
  • 00e291d Merge branch 'master' into tvdbclient_thread_safety

📊 Changes

1 file changed (+96 additions, -50 deletions)

View changed files

📝 MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs (+96 -50)

📄 Description

Along with #4222, another issue I ran into was when multiple TvdbClientManager calls ran concurrently.

Here's some errors that will happen if you try that:

[2020-09-27 15:27:00.698 -07:00] [ERR] [16] MediaBrowser.Providers.TV.SeriesMetadataService: Error in "TheTVDB"
System.ArgumentException: An item with the same key has already been added. Key: System.Net.Http.Headers.HeaderDescriptor
   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at System.Net.Http.Headers.HttpHeaders.GetOrCreateHeaderInfo(HeaderDescriptor descriptor, Boolean parseRawValues)
   at System.Net.Http.Headers.HttpHeaders.SetParsedValue(HeaderDescriptor descriptor, Object value)
   at System.Net.Http.Headers.HttpRequestHeaders.set_Authorization(AuthenticationHeaderValue value)
   at TvDbSharper.Clients.AuthenticationClient.UpdateAuthenticationHeader(String token)
   at TvDbSharper.Clients.AuthenticationClient.AuthenticateAsync(AuthenticationData authenticationData, CancellationToken cancellationToken)
   at MediaBrowser.Providers.Plugins.TheTvdb.TvdbClientManager.GetTvDbClient(String language) in C:\Programming\jellyfin\MediaBrowser.Providers\Plugins\TheTvdb\TvdbClientManager.cs:line 49
[2020-09-27 14:36:38.036 -07:00] [ERR] [44] MediaBrowser.Providers.TV.SeriesMetadataService: Error in "TheTVDB"
System.ArgumentException: An item with the same key has already been added. Key: System.Net.Http.Headers.HeaderDescriptor
   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
   at System.Net.Http.Headers.HttpHeaders.Add(HeaderDescriptor descriptor, String value)
   at System.Net.Http.Headers.HttpHeaderValueCollection`1.ParseAdd(String input)
   at TvDbSharper.TvDbClient.set_AcceptedLanguage(String value)
   at MediaBrowser.Providers.Plugins.TheTvdb.TvdbClientManager.TryGetValue[T](String key, String language, Func`1 resultFactory) in C:\Programming\jellyfin\MediaBrowser.Providers\Plugins\TheTvdb\TvdbClientManager.cs:line 279
   at MediaBrowser.Providers.Plugins.TheTvdb.TvdbSeriesProvider.FindSeriesInternal(String name, String language, CancellationToken cancellationToken) in C:\Programming\jellyfin\MediaBrowser.Providers\Plugins\TheTvdb\TvdbSeriesProvider.cs:line 229
   at MediaBrowser.Providers.Plugins.TheTvdb.TvdbSeriesProvider.FindSeries(String name, Nullable`1 year, String language, CancellationToken cancellationToken) in C:\Programming\jellyfin\MediaBrowser.Providers\Plugins\TheTvdb\TvdbSeriesProvider.cs:line 197
   at MediaBrowser.Providers.Plugins.TheTvdb.TvdbSeriesProvider.Identify(SeriesInfo info) in C:\Programming\jellyfin\MediaBrowser.Providers\Plugins\TheTvdb\TvdbSeriesProvider.cs:line 398
   at MediaBrowser.Providers.Plugins.TheTvdb.TvdbSeriesProvider.GetMetadata(SeriesInfo itemId, CancellationToken cancellationToken) in C:\Programming\jellyfin\MediaBrowser.Providers\Plugins\TheTvdb\TvdbSeriesProvider.cs:line 80
   at MediaBrowser.Providers.Manager.MetadataService`2.ExecuteRemoteProviders(MetadataResult`1 temp, String logName, TIdType id, IEnumerable`1 providers, CancellationToken cancellationToken) in C:\Programming\jellyfin\MediaBrowser.Providers\Manager\MetadataService.cs:line 853

It's complaining about setting AcceptedLanguage and calling AuthenticateAsync.

A fix is to have individual clients per language, so AcceptedLanguage is set once during the initial creation. Along with that, add a locking mechanism to prevent concurrent authentication requests.

Changes

  • Keep a ConcurrentDictionary of TvDbClients per language.
  • Add a locking mechanism to prevent concurrent authentication requests. Technically we could introduce a lock per language so clients could authenticate concurrently but I doubt that would result in a notice performance increase.
  • Change GetTvDbClient to return a specific langauges TvDbClient and make it async so the token methods can be await'd
  • Update TryGetValue to use GetOrCreateAsync. Something to note is that MemoryCache.GetOrCreateAsync is thread safe but has odd behavior (ref 1, ref 2). So it might run the factory a few times for the same key at the start. This is kind of annoying but might not be a huge issue for this use case.
  • Use UtcNow for the token DateTime since it's less scary than local times.

Issues
None


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/jellyfin/jellyfin/pull/4223 **Author:** [@Spacetech](https://github.com/Spacetech) **Created:** 9/28/2020 **Status:** ❌ Closed **Base:** `master` ← **Head:** `tvdbclient_thread_safety` --- ### 📝 Commits (4) - [`49522a4`](https://github.com/jellyfin/jellyfin/commit/49522a4f09be8212ac29e183cf41f9d19188789d) Make TvdbClientManager thread safe - [`b739475`](https://github.com/jellyfin/jellyfin/commit/b739475587e9e7e17b91eee50cccdedf258a2c2f) update based on suggestions - [`9316336`](https://github.com/jellyfin/jellyfin/commit/9316336c8eb52bf2f77427d3886d1c1c8310de26) move WaitAsync outside the try statement - [`00e291d`](https://github.com/jellyfin/jellyfin/commit/00e291dae2db747be86928425dacaa6f0fd0140f) Merge branch 'master' into tvdbclient_thread_safety ### 📊 Changes **1 file changed** (+96 additions, -50 deletions) <details> <summary>View changed files</summary> 📝 `MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs` (+96 -50) </details> ### 📄 Description Along with #4222, another issue I ran into was when multiple `TvdbClientManager` calls ran concurrently. Here's some errors that will happen if you try that: ``` [2020-09-27 15:27:00.698 -07:00] [ERR] [16] MediaBrowser.Providers.TV.SeriesMetadataService: Error in "TheTVDB" System.ArgumentException: An item with the same key has already been added. Key: System.Net.Http.Headers.HeaderDescriptor at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior) at System.Net.Http.Headers.HttpHeaders.GetOrCreateHeaderInfo(HeaderDescriptor descriptor, Boolean parseRawValues) at System.Net.Http.Headers.HttpHeaders.SetParsedValue(HeaderDescriptor descriptor, Object value) at System.Net.Http.Headers.HttpRequestHeaders.set_Authorization(AuthenticationHeaderValue value) at TvDbSharper.Clients.AuthenticationClient.UpdateAuthenticationHeader(String token) at TvDbSharper.Clients.AuthenticationClient.AuthenticateAsync(AuthenticationData authenticationData, CancellationToken cancellationToken) at MediaBrowser.Providers.Plugins.TheTvdb.TvdbClientManager.GetTvDbClient(String language) in C:\Programming\jellyfin\MediaBrowser.Providers\Plugins\TheTvdb\TvdbClientManager.cs:line 49 ``` ``` [2020-09-27 14:36:38.036 -07:00] [ERR] [44] MediaBrowser.Providers.TV.SeriesMetadataService: Error in "TheTVDB" System.ArgumentException: An item with the same key has already been added. Key: System.Net.Http.Headers.HeaderDescriptor at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior) at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value) at System.Net.Http.Headers.HttpHeaders.Add(HeaderDescriptor descriptor, String value) at System.Net.Http.Headers.HttpHeaderValueCollection`1.ParseAdd(String input) at TvDbSharper.TvDbClient.set_AcceptedLanguage(String value) at MediaBrowser.Providers.Plugins.TheTvdb.TvdbClientManager.TryGetValue[T](String key, String language, Func`1 resultFactory) in C:\Programming\jellyfin\MediaBrowser.Providers\Plugins\TheTvdb\TvdbClientManager.cs:line 279 at MediaBrowser.Providers.Plugins.TheTvdb.TvdbSeriesProvider.FindSeriesInternal(String name, String language, CancellationToken cancellationToken) in C:\Programming\jellyfin\MediaBrowser.Providers\Plugins\TheTvdb\TvdbSeriesProvider.cs:line 229 at MediaBrowser.Providers.Plugins.TheTvdb.TvdbSeriesProvider.FindSeries(String name, Nullable`1 year, String language, CancellationToken cancellationToken) in C:\Programming\jellyfin\MediaBrowser.Providers\Plugins\TheTvdb\TvdbSeriesProvider.cs:line 197 at MediaBrowser.Providers.Plugins.TheTvdb.TvdbSeriesProvider.Identify(SeriesInfo info) in C:\Programming\jellyfin\MediaBrowser.Providers\Plugins\TheTvdb\TvdbSeriesProvider.cs:line 398 at MediaBrowser.Providers.Plugins.TheTvdb.TvdbSeriesProvider.GetMetadata(SeriesInfo itemId, CancellationToken cancellationToken) in C:\Programming\jellyfin\MediaBrowser.Providers\Plugins\TheTvdb\TvdbSeriesProvider.cs:line 80 at MediaBrowser.Providers.Manager.MetadataService`2.ExecuteRemoteProviders(MetadataResult`1 temp, String logName, TIdType id, IEnumerable`1 providers, CancellationToken cancellationToken) in C:\Programming\jellyfin\MediaBrowser.Providers\Manager\MetadataService.cs:line 853 ``` It's complaining about setting `AcceptedLanguage` and calling `AuthenticateAsync`. A fix is to have individual clients per language, so `AcceptedLanguage` is set once during the initial creation. Along with that, add a locking mechanism to prevent concurrent authentication requests. **Changes** - Keep a `ConcurrentDictionary` of `TvDbClient`s per language. - Add a locking mechanism to prevent concurrent authentication requests. Technically we could introduce a lock per language so clients could authenticate concurrently but I doubt that would result in a notice performance increase. - Change `GetTvDbClient` to return a specific langauges `TvDbClient` and make it async so the token methods can be await'd - Update `TryGetValue` to use `GetOrCreateAsync`. Something to note is that `MemoryCache.GetOrCreateAsync` is thread safe but has odd behavior ([ref 1](https://blog.novanet.no/asp-net-core-memory-cache-is-get-or-create-thread-safe/), [ref 2](https://www.hanselman.com/blog/EyesWideOpenCorrectCachingIsAlwaysHard.aspx)). So it might run the factory a few times for the same key at the start. This is kind of annoying but might not be a huge issue for this use case. - Use `UtcNow` for the token `DateTime` since it's less scary than local times. **Issues** None --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
backuprepo 2025-12-22 07:39:57 +01:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: starred/jellyfin#9778
No description provided.