From a011e76d494eddb10703da812cee7b8b8c2b43d6 Mon Sep 17 00:00:00 2001 From: Chomp Date: Mon, 8 Sep 2025 11:28:18 +0100 Subject: [PATCH 1/4] `RagfairOfferHolder` lock improvements --- .../Services/RagfairOfferService.cs | 1 + .../Utils/RagfairOfferHolder.cs | 57 +++++++++---------- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/Libraries/SPTarkov.Server.Core/Services/RagfairOfferService.cs b/Libraries/SPTarkov.Server.Core/Services/RagfairOfferService.cs index 8d5264884..5eabeec12 100644 --- a/Libraries/SPTarkov.Server.Core/Services/RagfairOfferService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/RagfairOfferService.cs @@ -170,6 +170,7 @@ public void RemoveExpiredOffers() { // Gather all stale offers var staleOfferIds = ragfairOfferHolder.GetStaleOfferIds(); + logger.Debug($"Processing: {staleOfferIds.Count} stale offers"); foreach (var offerId in staleOfferIds) { ProcessStaleOffer(offerId, false); diff --git a/Libraries/SPTarkov.Server.Core/Utils/RagfairOfferHolder.cs b/Libraries/SPTarkov.Server.Core/Utils/RagfairOfferHolder.cs index b74396718..03aaa5c0b 100644 --- a/Libraries/SPTarkov.Server.Core/Utils/RagfairOfferHolder.cs +++ b/Libraries/SPTarkov.Server.Core/Utils/RagfairOfferHolder.cs @@ -21,7 +21,7 @@ ItemHelper itemHelper /// /// Expired offer Ids /// - private readonly HashSet _expiredOfferIds = []; + private readonly ConcurrentBag _expiredOfferIds = []; /// /// Ragfair offer cache, keyed by offer Id @@ -43,7 +43,7 @@ ItemHelper itemHelper /// private readonly ConcurrentDictionary> _fakePlayerOffers = new(); - private readonly Lock _expiredOfferIdsLock = new(); + private readonly Lock _processExpiredOffersLock = new(); private readonly Lock _ragfairOperationLock = new(); /// @@ -60,11 +60,11 @@ ItemHelper itemHelper /// Get a ragfair offer by its id /// /// RagfairOffer - public HashSet GetStaleOfferIds() + public List GetStaleOfferIds() { - lock (_expiredOfferIdsLock) + lock (_processExpiredOffersLock) { - return _expiredOfferIds; + return _expiredOfferIds.ToList(); } } @@ -107,12 +107,7 @@ public IEnumerable GetOffersByTrader(MongoId traderId) /// RagfairOffer list public List GetOffers() { - if (!_offersById.IsEmpty) - { - return _offersById.Values.ToList(); - } - - return []; + return _offersById.IsEmpty ? [] : _offersById.Values.ToList(); } /// @@ -324,12 +319,9 @@ protected bool AddFakePlayerOffer(MongoId itemTpl, MongoId offerId) /// Id of offer to add to stale collection public void FlagOfferAsExpired(MongoId staleOfferId) { - lock (_expiredOfferIdsLock) + lock (_processExpiredOffersLock) { - if (!_expiredOfferIds.Add(staleOfferId)) - { - logger.Warning($"Unable to add offer: {staleOfferId} to expired offers"); - } + _expiredOfferIds.Add(staleOfferId); } } @@ -339,7 +331,7 @@ public void FlagOfferAsExpired(MongoId staleOfferId) /// Number of expired offers public int GetExpiredOfferCount() { - lock (_expiredOfferIdsLock) + lock (_processExpiredOffersLock) { return _expiredOfferIds.Count; } @@ -352,7 +344,7 @@ public int GetExpiredOfferCount() public IEnumerable> GetExpiredOfferItems() { List expiredOfferIdsCopy; - lock (_expiredOfferIdsLock) + lock (_processExpiredOffersLock) { expiredOfferIdsCopy = _expiredOfferIds.ToList(); } @@ -385,7 +377,7 @@ public IEnumerable> GetExpiredOfferItems() /// public void ResetExpiredOfferIds() { - lock (_expiredOfferIdsLock) + lock (_processExpiredOffersLock) { _expiredOfferIds.Clear(); } @@ -397,24 +389,27 @@ public void ResetExpiredOfferIds() /// Timestamp at point offer is 'expired' public void FlagExpiredOffersAfterDate(long timestamp) { - lock (_expiredOfferIdsLock) + lock (_processExpiredOffersLock) { - foreach (var offer in GetOffers()) - { - if (_expiredOfferIds.Contains(offer.Id) || offer.IsTraderOffer()) + var offers = GetOffers(); + Parallel.ForEach( + offers, + offer => { - // Already flagged or trader offer (handled separately), skip - continue; - } + if (_expiredOfferIds.Contains(offer.Id) || offer.IsTraderOffer()) + { + // Already flagged or trader offer (handled separately), skip + return; + } - if (offer.IsStale(timestamp)) - { - if (!_expiredOfferIds.Add(offer.Id)) + if (!offer.IsStale(timestamp)) { - logger.Warning($"Unable to add offer: {offer.Id} to expired offers as it already exists"); + return; } + + _expiredOfferIds.Add(offer.Id); } - } + ); } } } From fc835f1f11b48d12a4b32abe6b377e2252a6dcab Mon Sep 17 00:00:00 2001 From: Chomp Date: Mon, 8 Sep 2025 11:33:30 +0100 Subject: [PATCH 2/4] Perf improvement for debug logging inside `RemoveExpiredOffers` --- .../SPTarkov.Server.Core/Services/RagfairOfferService.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Libraries/SPTarkov.Server.Core/Services/RagfairOfferService.cs b/Libraries/SPTarkov.Server.Core/Services/RagfairOfferService.cs index 5eabeec12..5d6bba9d5 100644 --- a/Libraries/SPTarkov.Server.Core/Services/RagfairOfferService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/RagfairOfferService.cs @@ -170,7 +170,12 @@ public void RemoveExpiredOffers() { // Gather all stale offers var staleOfferIds = ragfairOfferHolder.GetStaleOfferIds(); - logger.Debug($"Processing: {staleOfferIds.Count} stale offers"); + + if (logger.IsLogEnabled(LogLevel.Debug)) + { + logger.Debug($"Processing: {staleOfferIds.Count} stale offers"); + } + foreach (var offerId in staleOfferIds) { ProcessStaleOffer(offerId, false); From 1251e5c347535de264eafff843d1a2c700c563fe Mon Sep 17 00:00:00 2001 From: Chomp Date: Mon, 8 Sep 2025 11:51:32 +0100 Subject: [PATCH 3/4] Replaced ConcurrentBag with ConcurrentDictionary to maintain previous behaviour --- .../Utils/RagfairOfferHolder.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Libraries/SPTarkov.Server.Core/Utils/RagfairOfferHolder.cs b/Libraries/SPTarkov.Server.Core/Utils/RagfairOfferHolder.cs index 03aaa5c0b..ff1ef81ca 100644 --- a/Libraries/SPTarkov.Server.Core/Utils/RagfairOfferHolder.cs +++ b/Libraries/SPTarkov.Server.Core/Utils/RagfairOfferHolder.cs @@ -21,7 +21,7 @@ ItemHelper itemHelper /// /// Expired offer Ids /// - private readonly ConcurrentBag _expiredOfferIds = []; + private readonly ConcurrentDictionary _expiredOfferIds = []; /// /// Ragfair offer cache, keyed by offer Id @@ -64,7 +64,7 @@ public List GetStaleOfferIds() { lock (_processExpiredOffersLock) { - return _expiredOfferIds.ToList(); + return _expiredOfferIds.Keys.ToList(); } } @@ -321,7 +321,10 @@ public void FlagOfferAsExpired(MongoId staleOfferId) { lock (_processExpiredOffersLock) { - _expiredOfferIds.Add(staleOfferId); + if (!_expiredOfferIds.TryAdd(staleOfferId, 0)) + { + logger.Warning($"Unable to add offer: {staleOfferId} to expired offers"); + } } } @@ -346,7 +349,7 @@ public IEnumerable> GetExpiredOfferItems() List expiredOfferIdsCopy; lock (_processExpiredOffersLock) { - expiredOfferIdsCopy = _expiredOfferIds.ToList(); + expiredOfferIdsCopy = _expiredOfferIds.Keys.ToList(); } // list of lists of item+children @@ -396,7 +399,7 @@ public void FlagExpiredOffersAfterDate(long timestamp) offers, offer => { - if (_expiredOfferIds.Contains(offer.Id) || offer.IsTraderOffer()) + if (_expiredOfferIds.ContainsKey(offer.Id) || offer.IsTraderOffer()) { // Already flagged or trader offer (handled separately), skip return; @@ -407,7 +410,10 @@ public void FlagExpiredOffersAfterDate(long timestamp) return; } - _expiredOfferIds.Add(offer.Id); + if (!_expiredOfferIds.TryAdd(offer.Id, 0)) + { + logger.Warning($"Unable to add offer: {offer.Id} to expired offers as it already exists"); + } } ); } From 94282b9bf02ad7e9536d8997c0cc714f56366edc Mon Sep 17 00:00:00 2001 From: Chomp Date: Mon, 8 Sep 2025 11:52:00 +0100 Subject: [PATCH 4/4] Removed unnecessary debug logging --- .../SPTarkov.Server.Core/Services/RagfairOfferService.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Libraries/SPTarkov.Server.Core/Services/RagfairOfferService.cs b/Libraries/SPTarkov.Server.Core/Services/RagfairOfferService.cs index 5d6bba9d5..8d5264884 100644 --- a/Libraries/SPTarkov.Server.Core/Services/RagfairOfferService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/RagfairOfferService.cs @@ -170,12 +170,6 @@ public void RemoveExpiredOffers() { // Gather all stale offers var staleOfferIds = ragfairOfferHolder.GetStaleOfferIds(); - - if (logger.IsLogEnabled(LogLevel.Debug)) - { - logger.Debug($"Processing: {staleOfferIds.Count} stale offers"); - } - foreach (var offerId in staleOfferIds) { ProcessStaleOffer(offerId, false);