Запуск не более одного обновления
От: Слава  
Дата: 24.03.19 02:07
Оценка:
ASP.NET Core

Есть некий большой словарь, который берётся из стороннего вебсервиса.

Когда в ходе обработки запроса требуется найти ключ в этом словаре, можно поискать ключ, а если не нашёлся — то обновить словарь, обратившись к вебсервису. Если же и после этого не нашёлся — значит, есть несогласованность в данных возвращаем 500. Если словарь обновлялся меньше, чем t времени назад, то можно сразу возвращать 500, не обновляя его.

Словарь можно держать в MemoryCache, или в каком-то синглтоне. Но, если мы получили два или больше запроса, которые требуют обращения к словарю, если оба запроса не нашли ключ, то как ограничить количество одновременно запущенных запросов на обновление словаря?

Пока что я себе это представляю, как возврат некоего Task, который создаётся только одним запросом, а все остальные им пользуются, ожидая завершения. Или можно сделать некий выделенный тред с машиной состояний и очередью запросов вида "верни значение по ключу", который будет отвечать либо значением, либо ошибкой, либо будет держать запрос в очереди, пока словарь не обновится.

Но мне не нравится идея сразу городить отдельный тред, и обрабатывать всё руками, писать switch по состояниям (привет WndProc). Я подобное уже писал для работы с оборудованием, и больше не хочу.

Наверняка в дотнете уже есть нечто готовое.
Re: Запуск не более одного обновления
От: Ночной Смотрящий Россия  
Дата: 24.03.19 12:40
Оценка:
Здравствуйте, Слава, Вы писали:

С>Пока что я себе это представляю, как возврат некоего Task, который создаётся только одним запросом, а все остальные им пользуются, ожидая завершения. Или можно сделать некий выделенный тред с машиной состояний и очередью запросов вида "верни значение по ключу", который будет отвечать либо значением, либо ошибкой, либо будет держать запрос в очереди, пока словарь не обновится.


https://blogs.msdn.microsoft.com/fkaduk/2018/09/02/multiple-ways-how-to-limit-parallel-tasks-processing/
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re: Запуск не более одного обновления
От: RushDevion Россия  
Дата: 24.03.19 14:40
Оценка:
Не проще сделать отдельный тред, который будет обновлять словарь раз в t минут/секунд?
Тогда можно считать, что данные в словаре всегда актуальны и либо возвращать найденное, либо выдавать 500.
Из минусов — потенциально большее количество запросов к стороннему сервису.
Из плюсов — элементарная реализация, не требующая никакой синхронизации доступа к словарю.
Типа такого:
public class AutoUpdateCache<TKey, TValue> : IDisposable
{
    private readonly Func<IDictionary<TKey, TValue>> m_DataLoader;
    private readonly Task m_SyncTask;
    private readonly ManualResetEvent m_DisposingEvent;
    private volatile IDictionary<TKey, TValue> m_Inner;
    private readonly TimeSpan m_SyncPeriod;

    public AutoUpdateCache(Func<IDictionary<TKey, TValue>> dataLoader, TimeSpan? syncPeriod = null)
    {
        m_DataLoader = dataLoader ?? throw new ArgumentNullException(nameof(dataLoader));
        m_SyncPeriod = syncPeriod ?? TimeSpan.FromSeconds(5);
        m_Inner = new Dictionary<TKey, TValue>();
        m_DisposingEvent = new ManualResetEvent(false);
        m_SyncTask = Task.Factory.StartNew(syncRoutine, TaskCreationOptions.LongRunning);
    }

    public TValue GetItem(TKey key)
    {
        return m_Inner.TryGetValue(key, out var value) ? value : default(TValue);
    }

    public void Dispose()
    {
        if (m_DisposingEvent.WaitOne(1)) throw new ObjectDisposedException(nameof(AutoUpdateCache<TKey, TValue>));
        m_DisposingEvent.Set();
        m_SyncTask.Wait();
        m_SyncTask.Dispose();
        m_DisposingEvent.Dispose();
    }

    private void syncRoutine()
    {
        do 
        {
            m_Inner = m_DataLoader(); // Atomic volatile write of the reference variable
        }
        while (!m_DisposingEvent.WaitOne(m_SyncPeriod))
    }
}
Отредактировано 24.03.2019 15:54 RushDevion . Предыдущая версия . Еще …
Отредактировано 24.03.2019 15:51 RushDevion . Предыдущая версия .
Re: Запуск не более одного обновления
От: Vladek Россия Github
Дата: 28.03.19 22:27
Оценка:
Здравствуйте, Слава, Вы писали:

С>ASP.NET Core


С>Есть некий большой словарь, который берётся из стороннего вебсервиса.


С>Когда в ходе обработки запроса требуется найти ключ в этом словаре, можно поискать ключ, а если не нашёлся — то обновить словарь, обратившись к вебсервису. Если же и после этого не нашёлся — значит, есть несогласованность в данных возвращаем 500. Если словарь обновлялся меньше, чем t времени назад, то можно сразу возвращать 500, не обновляя его.


Это похоже на https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern — там есть ссылка внизу на пример использования библиотеки Polly для этого.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.