【发布时间】:2020-03-31 16:02:42
【问题描述】:
我托管了一个跟踪帐户权限的 API。权限存储在 API 数据库中。为了能够在数据库极慢或不可用的情况下准确响应,我选择尝试在内存中反映数据库权限。
为此,我选择了ConcurrentDictionary<string, Permission>。键是帐户 ID,值是它的权限。这包含在服务InMemoryStorageService 中,该服务在我们的 DI 框架中注册为单例。
为了能够在内存中反映数据库,采取以下步骤:
- 在应用程序启动时,整个数据库表被预加载到内存中(大约 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 启动。
【问题讨论】:
标签: .net caching singleton concurrentdictionary