Spring-Data-Redis с Jedis putIfAbsent для распределенной блокировки — неправильное поведение

У меня есть некоторые проблемы с использованием Spring Data Redis для создания распределенной блокировки. Для этого используется метод putIfAbsent из CacheManager.

С точки зрения высокого уровня операция выглядит примерно так:

if (manager.putIfAbsent(parameters) == null) {
   executeOperation();
}

Из реализации putIfAbsent кажется, что используется операция setNX из базового драйвера Jedis.

Код реализации Spring выглядит примерно так:

if (!connection.setNX(keyBytes, value)) {
   return connection.get(keyBytes);
}
maintainKnownKeys(element, connection);
processKeyExpiration(element, connection);

setNX соединения — это просто делегирование фактической операции клиента. В реализации этого метода есть что-то вроде:

JedisConverters.toBoolean(jedis.setnx(key, value));

Итак, я столкнулся с двумя отдельными проблемами:

  1. Мой executeOperation() был одновременно выполнен двумя отдельными процессами. (Всего несколько случаев этой проблемы).

  2. Я дошел до ситуации, когда ключ остался и не просрочен. Это означает, что код processKeyExpiration(элемент, соединение) не был выполнен. Это означает, что setNx, выполненный как ключ, не был добавлен и возвращен перед этим оператором, но на самом деле ключ был добавлен.

Большую часть времени все работает нормально. Сериализатор ключей — StringRedisSerializer.

Я использую: spring-data-redis 1.8.23.RELEASE jedis 2.9.3

Могут ли быть какие-то экологические проблемы, которые джедаи не решают должным образом, или что-то в этом роде? Кто-нибудь достиг чего-то подобного? Можно ли попробовать какое-либо исправление, обновление библиотеки?


person raduone    schedule 29.10.2019    source источник


Ответы (1)


Так что я сделал немного больше анализа по этому вопросу. И кажется, что из-за того, как реализован putIfAbsent, он подвержен условиям гонки при использовании в нескольких процессах/потоках.

Это связано с тем, что ряд команд для реализации putIfAbsent, setNX, expire, get, не является транзакционным и из-за правильных условий (длительные операции, неправильная логика вытеснения) склонен к некорректному поведению.

Аналогичное объяснение того, как выполнять распределенную блокировку на основе операции Redis setNX, можно найти здесь команда Redis setNX.

Реализация из putIfAbsent склонна к некорректному поведению при использовании для блокировки аналогично тому, как это описано в приведенной выше документации.

Подводя итог, вероятно, не лучшая идея использовать putIfAbsent с драйвером Jedis для распределенной блокировки, по крайней мере, в этой версии Spring, поскольку я знаю, что с 2.x.x реализация немного изменилась.

person raduone    schedule 30.10.2019