Pull to refresh

Comments 11

PinnedPinned comments

Спасибо за статью. Оказывается если класс использовать для задач, для которых он не предназначен, то он не сработает! Не знал, что такое бывает. :)

Проблема конечно в стремлении понадобавлять разных методов во вполне себе хорошие классы, при этом не объяснив джунам, что лучше этими методами не пользоватся либо пользоватся понимая ограничения. Но, кто будет разбиратся и вдумыватся о возможных проблемах? Таких единицы.

Описанная вами проблема не решается ни одним классом, так как само решение нетревиальное и ненадёжное.

Решение - при отсутсвии ключа в кэше, первый вызов, записывается ключ и статус "не готово", затем запускается метод получения данных, после того как метод завершён, данные помещаются в кэш и статус меняется на "готово". Во время получения данных все вызовы из других потоков с этим ключём встают в ожидание - в идеале подписываются на событие и ожидают завершения получения данных.

Это основной, успешный сценарий. но есть и альтернативные - когда данные не удалось получить. Что делать в этом случае? :)

Вот тут и начинается самое интересное, и сценариев может быть множество. Эти сценарии сильно зависят от вашей задачи и пихать их в библиотеку .NET никто не будет - ибо дело неблагодарное :)

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

Вывод - если этих настроек нет, значит в классе нет реализации альтернативных сценариев. Если нет альтернативных сценариев, то нет и никакого кода для решения проблемы.

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


Решение - при отсутсвии ключа в кэше, первый вызов, записывается ключ и статус "не готово", затем запускается метод получения данных, после того как метод завершён, данные помещаются в кэш и статус меняется на "готово".

Но это тоже не решение. В кеш должны помещаться только найденные данные. А если их там нет? Нам надо, например, вернуть 404, и не пытаться что-то писать в кеш.

Первичная запись "не готово" тоже может случиться дважды, если это не атомарная или выполняется без записи со сравнением.

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

Есть другая крайность. Изобретать новые кирпичи для строительства каждого здания. И тогда кроме добавленной сложности мы получим добавленную стоимость и время.

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

Первичная запись "не готово" тоже может случиться дважды, если это не атомарная или выполняется без записи со сравнением.

Вот зачем вы такое пишите? Начали то хорошо, а перешли к замаскированному шельмованию, объяснили "дураку" азы.

Стыдно, коллега.

Но это тоже не решение. В кеш должны помещаться только найденные данные. А если их там нет? Нам надо, например, вернуть 404, и не пытаться что-то писать в кеш.

С такими требованиями подходит решение через контейнер-функцию, как упомянули в комментариях так и реализовано в HybridCache, через Task.

Вот зачем вы такое пишите?

Ну изначально из-за довольно резкого сарказма:

Оказывается если класс использовать для задач, для которых он не предназначен, то он не сработает! Не знал, что такое бывает.

Виагра, например, вообще задумана как лекарство от стенокардии :)

Всё же в крайности впадать не надо. Должно быть понятно, что это статья не мануал, покрывающий все кейсы во всех случаях вообще. Формат и объёмы не те.

С такими требованиями подходит решение через контейнер-функцию, как упомянули в комментариях так и реализовано в HybridCache, через Task.

Жаль, это работает только в рамках одного экземпляра.

Есть хорошее решение основанное на Task на все запросы на один ключ, все получают один и тот же Task, который при получении результата переходит в успешное состояние, предоставляя доступ к данным.

Да, примерно так и реализовано в HybridCache. Нужно только учесть, что это работает в рамках одного процесса. Если у приложения несколько реплик, то требуются блокировки на уровне L2.

Остальные будут ждать и прочитают уже обновлённое значение из L2. Я, к сожалению, не нашёл готовых библиотек для распределённых кэшей, реализующих этот функционал. Если вы знаете о таких, то напишите в комментариях.

Это же подсистема, а не библиотека. Ребята предусмотрели интерфейс для запросов наружу, но у каждого решения будут свои требования к слою кэширования. Кто же станет в библиотеку запихивать, скажем, Redis?

А что эффективнее Task или Lazy

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

Прекрасно. Спасибо за ссылку. Это то, чего не хватало экосистеме .NET.

Sign up to leave a comment.

Articles