从 ConcurrentDictionary 中消失的条目

Entries disappearing from ConcurrentDictionary

我主持了一个跟踪帐户权限的 API。权限存储在 APIs 数据库中。为了能够在数据库极慢或不可用的情况下准确响应,我选择尝试在内存中反映数据库权限。

为此,我选择了 ConcurrentDictionary<string, Permission>。键是帐户 ID,值是它的权限。它包装在一个服务 InMemoryStorageService 中,该服务在我们的 DI 框架中注册为单例。

为了能够在内存中反映数据库,采取了以下步骤:

  1. 在应用程序启动时,整个数据库 table 被预加载到内存中(大约 100k 个条目)
  2. 当对帐户权限进行更改时,它们会在数据库和内存存储中更新

我遇到的问题是,对于 api 的少数百分比的调用,无法找到内存中的权限,而它们存在于数据库中。我已经添加了日志记录,所以我知道特定帐户的权限是在启动时从数据库加载的。在 API 启动后,这种情况经常发生。所以事件的顺序是这样的:

  1. Api 开始
  2. 帐户(123、144、168)的权限已加载到 ConcurrentDictionary
  3. 帐户 144 次尝试获取其权限
  4. 在内存中找不到帐户 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。但它们源自引擎,它不会预加载内存存储。

抱歉!