HazelCast FencedLock не всегда успешно блокирует метод для других нод
Решил синхронизировать метод между 2 нодами приложения. Для этого взял FencedLock решение из библиотеки HazelCast. Все сделал по документации, ноды друг-друга увидели и объединились в кластер. Метод тоже синхронизировался, начал отрабатывать по 1 разу, но время от времени происходит дубляж работы метода(не часто).
Не могу понять с чем это связано!
Проблему я решил тем, что создал распределенный объект IMap, который поместил внутрь метода и исходя из него стал либо отрабатывать метод, либо игнорировать. Мне кажется, это больше похоже на костыль.
Нет ли пути решения данной проблемы через fencedLock-и и его конфиги?
P.S. Просьба не ссылаться на какие-то ключевые слова и отправлять меня в google, так как документации крайне мало и нет примеров работающего кода. Буду благодарен развернутому ответу.
hazelCast.yaml файл:
hazelcast:
network:
join:
multicast:
enabled: true
HazelCastConfiguration класс:
@Configuration
public class HazelCastConfiguration {
@Bean
public com.hazelcast.config.Config hazelCastConfig() {
return new Config();
}
@Bean
public HazelcastInstance hazelcastInstance(Config hazelCastConfig) {
return Hazelcast.newHazelcastInstance(hazelCastConfig);
}
@Bean
public IMap<String, LocalDateTime> timeMap(@Qualifier("hazelcastInstance") HazelcastInstance hazelcastInstance) {
return hazelcastInstance.getMap("hazelcastTimeMap");
}
Над методом я повесил Spring Scheduler и настроил его cron на работу каждые 5 секунд.
Метод до того как я добавил IMap:
@Scheduled(cron = "0/5 * * * * *")
public void sentMessage() {
FencedLock lock = hazelcastInstance.getCPSubsystem().getLock("myLock");
if (lock.tryLock()) {
try {
System.out.println("test message!" + LocalDateTime.now());
} finally {
lock.unlock();
}
}
}
Результат его работы между 2 нодами:
Нода 1:
Нода 2:
После добавление IMap и ориентирования по нему, проблему ушла.
Метод после добавления IMap:
@Scheduled(cron = "0/5 * * * * *")
public void sentMessage() {
FencedLock lock = hazelcastInstance.getCPSubsystem().getLock("myLock");
if (lock.tryLock()) {
try {
LocalDateTime date = timeMap.get("message");
if (date == null || LocalDateTime.now().isAfter(date)) {
System.out.println("test message!" + LocalDateTime.now());
timeMap.put("message", LocalDateTime.now().plusSeconds(4));
}
} finally {
lock.unlock();
}
}
}
Ответы (1 шт):
Спринг, да и ОС, не гарантируют тебе одновременное исполнение @Scheduled(cron = "0/5 * * * * *") + тело метода маленькое и отрабатывает быстро.
В итоге, иногда получается, что на одном узле вызов по расписанию успевает взять лок, напечатать и отпустить лок. В этот момент до лока добирается второй, но лок никто не держит, поэтому второй тоже отрабатывает.
IMap как раз эту проблему решает. Решение не костыльное, а вполне нормальное.
По другому можно было бы сделать за счёт удержания лока подольше, чтобы второй узел успел проснуться, попытаться взять лок и обломиться. Например, Thread.sleep(100) - но это тоже не будет 100% гарантией, потому что второй может опоздать и на больше, чем 100мс - например, у него случился GC. Это даже по твоим логами видно - один раз исполнение в X.008ms, другой раз в X.171ms.

