Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Java [игнор отключен] [закрыт для гостей] / Spring @Cacheable впадает в бесконечный цикл / 25 сообщений из 27, страница 1 из 2
03.12.2019, 21:24
    #39897811
rabiter
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
Доброго вечера!

Не знаю, пользуется ли кто аннотациями спринговыми аннотациями @Cacheable или нет, но есть один очень важный нюанс: не стоит вызывать методы вот так:

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
    @Cacheable(value = "myCache", sync = true)
    public String method1(String key) {
        return proxy.method2(key);
    }

    @Cacheable(value = "myCache", sync = true)
    public String method2(String key) {
        return "hello " + key;
    }



т.е. когда один метод с такой аннотацией вызывает другой метод с такой же аннотацией на том же кеше.
Потому что внутри это приведет к вложенному вызову computeIfAbsent на одной и той же ConcurrentHashMap. Если выйдет так, что ключи сгенерируют hash, указывающий на одну и ту же корзину, то все это зависнет в бесконечном цикле в недрах ConcurrentHashMap.

Т.е. например, вот такой код зациклится:
Код: java
1.
map.computeIfAbsent("FB", s -> map.computeIfAbsent("Ea", s1 -> "value"));


все потому что строки "FB" и "Ea" возвращают одинаковый hash и соответственно попадут в одну корзину, и computeIfAbsent сносит башню от этого. Последствия такого кульбита - минус поток выполнения, да не просто залоченого, а в бесконечном цикле, что с моей точки зрения - просто катастрофа.
...
Рейтинг: 0 / 0
03.12.2019, 23:34
    #39897846
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
rabiter, если тебя послушать то можно подумать что ConcurrentHashMap имеет дефект коллизии хешей
который приводит к зависанию.

Ты это хотел сказать?
...
Рейтинг: 0 / 0
04.12.2019, 00:07
    #39897859
rabiter
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
mayton,

Да, это уже обсуждалось здесь:
https://stackoverflow.com/questions/28840047/recursive-concurrenthashmap-computeifabsent-call-never-terminates-bug-or-fea

и здесь на форуме
https://www.sql.ru/forum/1206647/concurrenthashmap-mozhet-poymat-dedlok-pri-odnopotochnom-ispolzovanii

Это не совсем дефект, потому что в документации к ConcurrentHashMap#computeIfAbsent можно найти следующее:
Some attempted update operations on this map by other threads may be blocked while computation is in progress, so the computation should be short and simple, and must not attempt to update any other mappings of this map .

Но я не про этот "дефект", а про то, во что он может вылиться, если вот так использовать вложенные вызовы @Cacheable аннотации.
...
Рейтинг: 0 / 0
04.12.2019, 00:12
    #39897861
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
rabiter, ну ОК.

Но наверное не стоит из кешируемого метода вызывать другой кешируемый.
Убежден что в твоём коде можно зарефакторить общую логику и сделать к ней вызов из двух.

Как бритва Оккама.
...
Рейтинг: 0 / 0
04.12.2019, 00:14
    #39897862
rabiter
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
Похоже в девятке пофиксили, ну как, просто бросают исключение теперь, а не уходят в бесконечный цикл. Но мало ли, я вот на 8-ке еще пока.
...
Рейтинг: 0 / 0
04.12.2019, 00:18
    #39897865
rabiter
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
mayton
rabiter, ну ОК.

Но наверное не стоит из кешируемого метода вызывать другой кешируемый.
Убежден что в твоём коде можно зарефакторить общую логику и сделать к ней вызов из двух.

Как бритва Оккама.


Да да, все верно, так и придется делать.
Есть еще один вариант, предложенный тут:
https://github.com/ben-manes/caffeine/issues/89
Т.е. в мапу класть не результат выполнения, а CompletableFuture. Развязав таким образом CacheLoader от самого кеша (мапы).
Но что-то рисковать нет желания)
...
Рейтинг: 0 / 0
04.12.2019, 01:05
    #39897882
Leonid Kudryavtsev
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
mayton
Но наверное не стоит из кешируемого метода вызывать другой кешируемый.

в ТОМ же кеше. Смысл этого, мне совершенно не понятен
...
Рейтинг: 0 / 0
04.12.2019, 02:30
    #39897896
rabiter
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
Leonid Kudryavtsev
mayton
Но наверное не стоит из кешируемого метода вызывать другой кешируемый.

в ТОМ же кеше. Смысл этого, мне совершенно не понятен


Ну смысл в том, чтобы создать некий фреймворк, которым могли бы пользоваться программисты "послабее" и не париться.

Вот например пример:
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
@Cacheable(value="mimeTypes")
public MimeType getMimeType(String mimeType) {
  List<MimeType> allMimeTypes = proxy.getAllMimeTypes();
  return allMimeTypes.stream().filter(m -> m.getName().equals(mimeType)).get();
}

@Cacheable(value="mimeTypes")
public List<MimeTypes> getAllMimeTypes() {
  return someDBService.getMimeTypes();
}



Тут оба метода кешируются в одном и том же кеше, который инвалидируется сразу как только справочник mime types изменяется.
Ну тут понятно, что кешировать результат первого метода нет смысла - там нет ничего тяжелого, лишь выборка из списка.
Но теперь, если все же кто-нибудь так напишет - нужна дополнительная защита - проверка в runtime, чтобы так Cacheable не использовали. Иначе словить бесконечный цикл, это просто дикость какая-то, не хотелось бы, это же катастрофа.
...
Рейтинг: 0 / 0
04.12.2019, 10:08
    #39897968
lleming
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
Можно уточнить
Если так
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
class ValueGenerator{

    private ValueGenerator proxy;

    @Cacheable(value = "myCache", sync = true)
    public String method1(String key) {
        return proxy.method2(key);
    }

    @Cacheable(value = "myCache", sync = true)
    public String method2(String key) {
        return "hello " + key;
    }
}



то это странно, тк запрос будет мимо кэша посколу ссылка прокси в действительности на реальный класс а не прокси.

Leonid Kudryavtsevв ТОМ же кеше. Смысл этого, мне совершенно не понятен

Тоже так же кажется как то странно даже звучит логически (ИМХО)

Вот еще интересная тема с кэшами
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
    @Cacheable(value = "myCache", sync = true)
    public List<String> method1(String key) {
        //....
    }

    @Cacheable(value = "myCache", sync = true)
    public Set<String> method2(String key) {
       //.....
    }


Потратил много времени пока допер в чем проблема :=)
...
Рейтинг: 0 / 0
04.12.2019, 17:17
    #39898240
rabiter
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
lleming
Можно уточнить
Если так
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
class ValueGenerator{

    private ValueGenerator proxy;

    @Cacheable(value = "myCache", sync = true)
    public String method1(String key) {
        return proxy.method2(key);
    }

    @Cacheable(value = "myCache", sync = true)
    public String method2(String key) {
        return "hello " + key;
    }
}



то это странно, тк запрос будет мимо кэша посколу ссылка прокси в действительности на реальный класс а не прокси.

Не, я имел ввиду что все вызовы, конечно же, через прокси (я не совсем еще того) :-)
...
Рейтинг: 0 / 0
04.12.2019, 17:21
    #39898245
rabiter
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
lleming

Leonid Kudryavtsevв ТОМ же кеше. Смысл этого, мне совершенно не понятен

Тоже так же кажется как то странно даже звучит логически (ИМХО)
[/src]

Ну это меня успокаивает, потому что уже как минимум два человека находят такие вызовы бессмысленными. Но опасность-то в том, что если кто-нибудь все-таки так напишет (ну не я, коллеги мои), то есть вероятность словить бесконечный цикл, а это минус поток.
...
Рейтинг: 0 / 0
04.12.2019, 17:26
    #39898247
rabiter
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
    @Cacheable(value = "myCache", sync = true)
    public String method1(String key) {
        return proxy.method2("Mr " + key);
    }

    @Cacheable(value = "myCache", sync = true)
    public String method2(String key) {
        return "hello " + key;
    }




А такой код уже не кажется таким бесмысленным? Т.к. я в кеш не с одним ключем лезу, а с разными (добавил префикс "Mr " + key)?
Так вот если хеши у ключей на одну корзину укажут, то зациклится внутри ConcurrentHashMap
...
Рейтинг: 0 / 0
04.12.2019, 17:29
    #39898248
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
А зачем это кешировать?

Код: java
1.
2.
3.
4.
@Cacheable(value = "myCache", sync = true)
    public String method2(String key) {
        return "hello " + key;
    }



Это идеальная быстро вычислимая pure-function в духе функнионального программирования.

Твой пример - неудачен IMHO. Дай нам более приближенный к реальности.
...
Рейтинг: 0 / 0
04.12.2019, 17:30
    #39898250
rabiter
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
lleming

Вот еще интересная тема с кэшами
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
    @Cacheable(value = "myCache", sync = true)
    public List<String> method1(String key) {
        //....
    }

    @Cacheable(value = "myCache", sync = true)
    public Set<String> method2(String key) {
       //.....
    }


Потратил много времени пока допер в чем проблема :=)


Это потому что в спринге поумолчанию метод не участвует в генерации ключа :-) У себя я настроил генерацию ключа таким образом: составной ключ, который состоит из метода + параметры. Тогда получается что для этих двух методов будет разный ключ и соответственно две разные записи в кеше. И мой пример выше тоже получается обретает смысл, потому что эти два метода будут закешированы в одном кеше, но под разными ключами (методы-то разные, хоть и параметр один и тот же).
...
Рейтинг: 0 / 0
04.12.2019, 17:49
    #39898263
Leonid Kudryavtsev
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
ну и не забываем. что сам по себе LRU подходить не для всех задач !
т.ч. у нормально пользователя может возникнуть желание, вместо LRU выбирать какой-то другой алгоритм (очистки кэша)

смутные воспоминания, что mayton какой-то такой топик недавно создавал
...
Рейтинг: 0 / 0
04.12.2019, 17:50
    #39898264
rabiter
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
mayton
А зачем это кешировать?

Код: java
1.
2.
3.
4.
@Cacheable(value = "myCache", sync = true)
    public String method2(String key) {
        return "hello " + key;
    }



Это идеальная быстро вычислимая pure-function в духе функнионального программирования.

Твой пример - неудачен IMHO. Дай нам более приближенный к реальности.

Ну да, эта конструкция не имеет смысла, конечно, потому что кешировать результат конкатинации конечно не имеет смысл...

Давайте вот такой пример:

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
    @Cacheable(value = "myCache", sync = true)
    public DocumentTypes getDocumentTypesForUser(String username, String tenant) {
        DocumentTypes allDocTypes = proxy.getAllDocumentTypes(tenant); 
        // ...
        // здесь мы пробегаемся по всем документным типам и составляем
        // новый список в соответствии с правами/локализацией/личных
        // настроек пользователя
        // пусть это in memory операция, но вызывается очень часто,
        // и ее результат выполнения хотелось бы так же в кеше сохранить
       return result;
    }

    @Cacheable(value = "myCache", sync = true)
    public DocumentTypes getAllDocumentTypes(String tenant) {
        return dbService.loadAllDocumentTypes(tenant); // тяжелая операция, поэтому кешируем
    }



Т.е. вот это уже может зациклиться. Workaround простой - использовать разные кеши. Но все же, за этим уже надо следить, а коллеги разные бывают.
...
Рейтинг: 0 / 0
04.12.2019, 17:51
    #39898265
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
Вспомнился афоризм

В компьютерных науках есть две сложности: инвалидация кэша, именование переменных.
...
Рейтинг: 0 / 0
04.12.2019, 17:54
    #39898268
rabiter
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
mayton
Вспомнился афоризм

В компьютерных науках есть две сложности: инвалидация кэша, именование переменных.


Да да) про это я давно слышал
...
Рейтинг: 0 / 0
04.12.2019, 17:54
    #39898270
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
rabiter

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
    @Cacheable(value = "myCache", sync = true)
    public DocumentTypes getDocumentTypesForUser(String username, String tenant) {
        DocumentTypes allDocTypes = proxy.getAllDocumentTypes(tenant); 
        // ...
        // здесь мы пробегаемся по всем документным типам и составляем
        // новый список в соответствии с правами/локализацией/личных
        // настроек пользователя
        // пусть это in memory операция, но вызывается очень часто,
        // и ее результат выполнения хотелось бы так же в кеше сохранить
       return result;
    }

    @Cacheable(value = "myCache", sync = true)
    public DocumentTypes getAllDocumentTypes(String tenant) {
        return dbService.loadAllDocumentTypes(tenant); // тяжелая операция, поэтому кешируем
    }



Т.е. вот это уже может зациклиться. Workaround простой - использовать разные кеши. Но все же, за этим уже надо следить, а коллеги разные бывают.

У тебя здесь по определению должны быть разные кеши. У тебя уникальный ключ для одной функции
username + tenant а для другой - tenant.

Это просто поинт к тому чтобы просто сделать НОРМАЛЬНЫЙ дизайн и проблема ликвидируется.
...
Рейтинг: 0 / 0
04.12.2019, 18:04
    #39898273
rabiter
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
Leonid Kudryavtsev
ну и не забываем. что сам по себе LRU подходить не для всех задач !
т.ч. у нормально пользователя может возникнуть желание, вместо LRU выбирать какой-то другой алгоритм (очистки кэша)

смутные воспоминания, что mayton какой-то такой топик недавно создавал


Да, я читал этот топик. Но сейчас меня вопросы очистки не особо пока интересуют (у меня два варианта, как осуществлять очистку есть, чуть позже напишу. Но если кратко, это очистка по событию. И второй вариант - перед чтением кеша - проверка флага, который сбрасывается теми, кто меняют данные, и если флаг сброшен - то и кеш сбрасывается, прямо внутри аспекта). Но, кстати, может и правда использовать разные HashMap для разных методов? Но это будет забавно... Великоват overhead получается. Я думал использовать один ConcurrentHashMap, но к составному ключу просто добавить еще и метод (метод + параметры). И все просто валить в одну большую кучу. На самом деле кеш не будет очень большим в моем случае. Это больше попытка "навести порядок". Сейчас у нас куча полей в бинах типа ConcurrentHashMap или volatile DocumentTypes. Т.е. каждый бин кеширует сам у себя. Плюс такие бины подписаны на события по инвалидации кеша и сбрасывает эти поля просто по событию и все. И таких бинов довольно много. А так можно было бы просто аннотировать методы и все дела.
...
Рейтинг: 0 / 0
04.12.2019, 18:19
    #39898276
Leonid Kudryavtsev
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
Cц#$%ко, мое сообщение пропало (((

Нужно определиться, что же такое для нас КЭШ. Что понимать под этим термином?

На мой взгляд:
1. Это соответствие параметр_ф-ции ==> результат
Что для разных ф-ций БЕСМЫСЛЕННО. Если две ф-ции для одного и того же параметра. возврашают один и тот же результат - значит ф-ции совершенно одинаковые. Т.е. в продакшене это просто или копи-паст или переименование ф-ций.
Обычно реализуется через структуры типа HashMap
2. Политика управления кэшем: размер, алгоритм очистки и прочее
Обычно реализуется через LRU. Но НЕ всегда. Для некоторых применений LRU не лучшей вариант

Таким образом, если мы хотим делать по хорошему:
1. HeshMap для каждой ф-ции должен быть СВОЙ
2. А вот LRU (или аналог) никто не запрешает делать один, на несколько HashMap'ов. Конечно, возможно, нужно кодировать

IMHO & AFAIK

22032391
...
Рейтинг: 0 / 0
04.12.2019, 18:20
    #39898277
Leonid Kudryavtsev
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
rabiter
Да, я читал этот топик.....

сорри, пред сообщение это был кусок (продолжение) от сообщения выше
почему-то сообщение не опубликовалось (((
...
Рейтинг: 0 / 0
04.12.2019, 18:50
    #39898289
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
Leonid Kudryavtsev
ну и не забываем. что сам по себе LRU подходить не для всех задач !
т.ч. у нормально пользователя может возникнуть желание, вместо LRU выбирать какой-то другой алгоритм (очистки кэша)

смутные воспоминания, что mayton какой-то такой топик недавно создавал

Да. Помню такое. Вот тут https://www.sql.ru/forum/1317932/chetvergovaya-invalidaciya-keshey
...
Рейтинг: 0 / 0
05.12.2019, 12:52
    #39898539
Alexey Tomin
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
rabiter
Это потому что в спринге поумолчанию метод не участвует в генерации ключа :-)


А, так вот оно что

Ну тогда понятно, в чём ошибка.
Да, нужно разные кэши
...
Рейтинг: 0 / 0
05.12.2019, 15:36
    #39898666
rabiter
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Spring @Cacheable впадает в бесконечный цикл
Да, это мне подойдет, использовать по ConcurrentHashMap на метод. Только для методов без параметров это выглядит глуповато, потому что в мапе для них может быть максимум одно значение.
...
Рейтинг: 0 / 0
Форумы / Java [игнор отключен] [закрыт для гостей] / Spring @Cacheable впадает в бесконечный цикл / 25 сообщений из 27, страница 1 из 2
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]