Имеется N: N > 1
клиентов расположенных на разных серверах. В таком сценарии возможна ситуация когда множество клиентов пытаются записать данные в Redis
из-за их истекшего TTL
, а это влечет за собой дополнительные запросы к DB
. Количество запросов к DB
При N: N = 8
равно 8. Требуется минимизировать количество обращений к DB
.
Схема: User -> API -> DB || Redis -> User
.
Один из вариантов - использование встроенного в Redis
механизма блокировок. Пример ниже иллюстрирует добавление записи с учетом блокировки ключа. Ожидается, что при указанных условиях будет выполнен только один запрос к DB
.
public override async Task<T> SetItemAsync<T>(string k, Func<Task<T>> action, TimeSpan TTL)
{
// Вероятно клиент, что один
// из клиент завершил запись.
T v = await GetFromCache<T>(k);
// Если данные в Redis есть,
// возвращаем, а иначе
// пытаемся добавить.
if (!IsDefault(v))
return v;
// Время на которое блокируется ключ.
// Может зависить от типа данных.
// Сделать конфигурируемым.
TimeSpan _lockTimeout = TimeSpan.FromSeconds(3);
// Время окончания блокировки.
DateTime _lockTimeoutEnd = DateTime.Now.AddSeconds(_lockTimeout.TotalSeconds);
// Пытаемся получить блокировку
// с учетом таймаута.
while (DateTime.Now < _lockTimeoutEnd)
{
// _lockToken - Environment.MachineName.
if (_db.LockTake(k, _lockToken, _lockTimeout))
{
try
{
// Получаем данные из DB. Возможны случаи
// когда выполнения данного участка кода
// не уложится в таймаут блокировки.
v = await action();
// Записываем данные в Redis.
await SetToCache(k, v, TTL);
// Публикуем сообщение в соответствующий канал, чтобы
// остальные клиенты перезапросили из Redis данные с
// заданным ключем и перезаписали локальный кеш. Это
// для случая когда будет использоваться Redis + LocalMemory.
await _db.PublishAsync(PubSubMessageType.CACHE_RESETED.ToString(), k);
}
finally
{
_db.LockRelease(k, _lockToken);
}
}
else
{
await Task.Delay(50);
// Вероятно клиент, что один
// из клиент завершил запись.
v = await GetFromCache<T>(k);
if (!IsDefault(v))
return v;
}
}
return default;
}
Корректен ли Retry
механизм? Если получить блокировку не получится, вызывающий код не получит данных, правильно ли это? Подразумевается, что он сам решит, что в таком случае делать.
Из-за установленного таймаута вечной блокировки быть не может, например из-за неудачно завершившегося потока, который ранее получил блокировку. Так или иначе один из клиентов или потоков в рамках того же клиента установит значение.
В случае когда получить блокировку не получилось, попыток снять ее не производится. Подразумевается, что другой клиент или поток в рамках того же клиента получит блокировку в результате ее таймаута.
Является ли overhead'ом попытки считать информацию в рамках описанного процесса?
Подразумевается, что доступ к методу SetItemAsync
синхронизирован в рамках клиента.
П.3. Если выставлять таймаут блокировки с учетом данных, которые будут извлекаться из DB
, может возникнуть ситуация когда задан долгий таймаут, тогда следует снять блокировку принудительно. Как тут грамотно поступить?
Вопросов оставленно больше одного, но тем не менее. Правильна ли реализация с учетом поставленной цели? Ответы на вопросы из пунктов 1-5 тоже привествуются. Может что-то не учтено или в корне неправильно.
Дополнительная информация:
Redis
- 4.0.11. 3 VM. Clustered | .NET Client
- StackExchange.Redis
Сейчас у вас данные выбрасываются исключительно по TTL. Значит, вас устраивает неактуальность данных в кэше течении TTL секунд. Т.е. реальные ограничения у вас:
Этим ограничениям полностью соответствует Upfront population:
При необходимости - триггерите заселение кэша не по таймеру, а по событиям изменения данных - и получаете минимальное отставание кэша от реальных данных, сравнимое с временем выполнения прямой выборки.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
При вводе некоторых чисел например 4,5 выводит Nan все перепробовал и на -1 домножал и по модулю брал все-ровно NaN
В примере с использованием MVVM контекст данных вводили прямо вxaml
Имею процедуру, она выполняется и работает в студии mssql Я её связал ранее через источник данных с vs проектомПотом я изменил её, поменял в селекте...
Помогите пожалуйста, как решить проблему экспортаУ меня есть определенное количество столбцов в listView их может быть меньше или больше и их название...