从 ConcurrentDictionary 中消失的条目
Entries disappearing from ConcurrentDictionary
我主持了一个跟踪帐户权限的 API。权限存储在 APIs 数据库中。为了能够在数据库极慢或不可用的情况下准确响应,我选择尝试在内存中反映数据库权限。
为此,我选择了 ConcurrentDictionary<string, Permission>
。键是帐户 ID,值是它的权限。它包装在一个服务 InMemoryStorageService
中,该服务在我们的 DI 框架中注册为单例。
为了能够在内存中反映数据库,采取了以下步骤:
- 在应用程序启动时,整个数据库 table 被预加载到内存中(大约 100k 个条目)
- 当对帐户权限进行更改时,它们会在数据库和内存存储中更新
我遇到的问题是,对于 api 的少数百分比的调用,无法找到内存中的权限,而它们存在于数据库中。我已经添加了日志记录,所以我知道特定帐户的权限是在启动时从数据库加载的。在 API 启动后,这种情况经常发生。所以事件的顺序是这样的:
- Api 开始
- 帐户(123、144、168)的权限已加载到 ConcurrentDictionary
- 帐户 144 次尝试获取其权限
- 在内存中找不到帐户 144 的权限的日志条目,但存在于数据库中
有什么建议吗?
public class InMemoryStorageService : IInMemoryStorageService
{
private readonly ILogFactory _logFactory;
private readonly IPermissionRepository _permissionRepository;
private readonly ConcurrentDictionary<string, Permission> _inMemoryStore;
private ILog _log;
protected ILog Log => _log ?? (_log = _logFactory.GetLogger(GetType()));
public InMemoryStorageService(ILogFactory logFactory, IPermissionRepository permissionRepository)
{
_logFactory = logFactory;
_permissionRepository = permissionRepository;
_inMemoryStore = new ConcurrentDictionary<string, Permission>();
}
public Permission GetPermission(string accountId)
{
Permission value;
if (_inMemoryStore.TryGetValue(accountId, out value)) return value;
return null;
}
public void AddOrUpdatePermission(Permission permissions)
{
if (permissions == null) throw new ArgumentNullException(nameof(permissions));
_inMemoryStore.AddOrUpdate(permissions.AccountId, permissions,
(accId, currentPermissions) =>
permissions.Updated > currentPermissions?.Updated ? permissions : currentPermissions);
}
public void PreloadStorageFromDb()
{
try
{
var stopWatch = new Stopwatch();
stopWatch.Start();
int addedPermissions = 0;
var permissions = _permissionRepository.GetAll();
foreach (var accountPermission in permissions)
{
AddOrUpdatePermission(accountPermission);
addedPermissions++;
}
stopWatch.Stop();
Log.InfoAsJson($"{nameof(PreloadStorageFromDb)}", new
{
addedPermissions,
timeToLoadCacheInMilliseconds = stopWatch.ElapsedMilliseconds
});
}
catch (Exception e)
{
Log.ErrorAsJson($"{nameof(PreloadStorageFromDb)}", null, e);
}
}
}
DI 框架是 SimpleInjector
container.RegisterInstance<IInMemoryStorageService>(new InMemoryStorageService(logFactory, new PermissionRepository(appSettings, logFactory)));
api 通过 Owin 自托管。它通过 .NET Framework 4.7.1 WindowsService 启动。
没关系。我真蠢啊。我曾假设记录的条目来自 api。但它们源自引擎,它不会预加载内存存储。
抱歉!
我主持了一个跟踪帐户权限的 API。权限存储在 APIs 数据库中。为了能够在数据库极慢或不可用的情况下准确响应,我选择尝试在内存中反映数据库权限。
为此,我选择了 ConcurrentDictionary<string, Permission>
。键是帐户 ID,值是它的权限。它包装在一个服务 InMemoryStorageService
中,该服务在我们的 DI 框架中注册为单例。
为了能够在内存中反映数据库,采取了以下步骤:
- 在应用程序启动时,整个数据库 table 被预加载到内存中(大约 100k 个条目)
- 当对帐户权限进行更改时,它们会在数据库和内存存储中更新
我遇到的问题是,对于 api 的少数百分比的调用,无法找到内存中的权限,而它们存在于数据库中。我已经添加了日志记录,所以我知道特定帐户的权限是在启动时从数据库加载的。在 API 启动后,这种情况经常发生。所以事件的顺序是这样的:
- Api 开始
- 帐户(123、144、168)的权限已加载到 ConcurrentDictionary
- 帐户 144 次尝试获取其权限
- 在内存中找不到帐户 144 的权限的日志条目,但存在于数据库中
有什么建议吗?
public class InMemoryStorageService : IInMemoryStorageService
{
private readonly ILogFactory _logFactory;
private readonly IPermissionRepository _permissionRepository;
private readonly ConcurrentDictionary<string, Permission> _inMemoryStore;
private ILog _log;
protected ILog Log => _log ?? (_log = _logFactory.GetLogger(GetType()));
public InMemoryStorageService(ILogFactory logFactory, IPermissionRepository permissionRepository)
{
_logFactory = logFactory;
_permissionRepository = permissionRepository;
_inMemoryStore = new ConcurrentDictionary<string, Permission>();
}
public Permission GetPermission(string accountId)
{
Permission value;
if (_inMemoryStore.TryGetValue(accountId, out value)) return value;
return null;
}
public void AddOrUpdatePermission(Permission permissions)
{
if (permissions == null) throw new ArgumentNullException(nameof(permissions));
_inMemoryStore.AddOrUpdate(permissions.AccountId, permissions,
(accId, currentPermissions) =>
permissions.Updated > currentPermissions?.Updated ? permissions : currentPermissions);
}
public void PreloadStorageFromDb()
{
try
{
var stopWatch = new Stopwatch();
stopWatch.Start();
int addedPermissions = 0;
var permissions = _permissionRepository.GetAll();
foreach (var accountPermission in permissions)
{
AddOrUpdatePermission(accountPermission);
addedPermissions++;
}
stopWatch.Stop();
Log.InfoAsJson($"{nameof(PreloadStorageFromDb)}", new
{
addedPermissions,
timeToLoadCacheInMilliseconds = stopWatch.ElapsedMilliseconds
});
}
catch (Exception e)
{
Log.ErrorAsJson($"{nameof(PreloadStorageFromDb)}", null, e);
}
}
}
DI 框架是 SimpleInjector
container.RegisterInstance<IInMemoryStorageService>(new InMemoryStorageService(logFactory, new PermissionRepository(appSettings, logFactory)));
api 通过 Owin 自托管。它通过 .NET Framework 4.7.1 WindowsService 启动。
没关系。我真蠢啊。我曾假设记录的条目来自 api。但它们源自引擎,它不会预加载内存存储。
抱歉!