Improved locking inside RagfairRequiredItemsService

Comment and name improvements
This commit is contained in:
Chomp
2025-09-08 10:58:08 +01:00
parent 5bd3331def
commit 01570a1621
2 changed files with 28 additions and 19 deletions
@@ -40,7 +40,7 @@ public class RagfairServer(
ProcessExpiredFleaOffers();
// Flag data as stale and in need of regeneration
ragfairRequiredItemsService.CacheIsStale();
ragfairRequiredItemsService.InvalidateCache();
}
protected void RefreshTraderOffers()
@@ -8,17 +8,18 @@ namespace SPTarkov.Server.Core.Services;
[Injectable(InjectionType.Singleton)]
public class RagfairRequiredItemsService(RagfairOfferService ragfairOfferService, PaymentHelper paymentHelper)
{
private bool _cacheIsStale = true;
private readonly Lock _createCacheLock = new();
private volatile bool _cacheIsStale = true;
/// <summary>
/// Key = tpl
/// Key = tpl, Value = offerIds
/// </summary>
protected readonly ConcurrentDictionary<MongoId, HashSet<MongoId>> RequiredItemsCache = new();
private ConcurrentDictionary<MongoId, HashSet<MongoId>> _requiredItemsCache = new();
/// <summary>
/// Empty hashset to be returned when no keys found by GetRequiredOffersById (reduces memory allocations)
/// </summary>
protected readonly IReadOnlySet<MongoId> EmptyOfferIdSet = new HashSet<MongoId>();
private readonly IReadOnlySet<MongoId> _emptyOfferIdSet = new HashSet<MongoId>();
/// <summary>
/// Get the offerId of offers that require the supplied tpl
@@ -29,10 +30,18 @@ public class RagfairRequiredItemsService(RagfairOfferService ragfairOfferService
{
if (_cacheIsStale)
{
BuildRequiredItemTable();
// Lock to prevent 2 threads building table
lock (_createCacheLock)
{
// Second check in the event another thread just built table
if (_cacheIsStale)
{
BuildRequiredItemTable();
}
}
}
return RequiredItemsCache.TryGetValue(tpl, out var offerIds) ? offerIds : EmptyOfferIdSet;
return _requiredItemsCache.TryGetValue(tpl, out var offerIds) ? offerIds : _emptyOfferIdSet;
}
/// <summary>
@@ -40,11 +49,16 @@ public class RagfairRequiredItemsService(RagfairOfferService ragfairOfferService
/// </summary>
public void BuildRequiredItemTable()
{
Clear();
ConcurrentDictionary<MongoId, HashSet<MongoId>> newCache = new();
foreach (var offer in ragfairOfferService.GetOffers())
{
foreach (var requirement in offer.Requirements ?? [])
if (offer.Requirements is null)
{
continue;
}
foreach (var requirement in offer.Requirements)
{
// Skip offers for currency, we only need barter offers as this cache is used by `GetOffersThatRequireItem`
if (paymentHelper.IsMoneyTpl(requirement.TemplateId))
@@ -53,29 +67,24 @@ public class RagfairRequiredItemsService(RagfairOfferService ragfairOfferService
}
// Ensure cache has Hashset init for this tpl
var offerIds = RequiredItemsCache.GetOrAdd(requirement.TemplateId, _ => []);
var offerIds = newCache.GetOrAdd(requirement.TemplateId, _ => []);
// Add offer id against the tpl key
offerIds.Add(offer.Id);
}
}
// Replace cache in one go
_requiredItemsCache = newCache;
// Cache is now fresh
_cacheIsStale = false;
}
/// <summary>
/// Clear all data from cache
/// </summary>
public void Clear()
{
RequiredItemsCache.Clear();
}
/// <summary>
/// Flag the cache as stale
/// </summary>
public void CacheIsStale()
public void InvalidateCache()
{
_cacheIsStale = true;
}