Инвалидация кэша что это

Кэш: что это такое и как с ним справиться начинающим разработчикам

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

Прежде чем мы начнем говорить о инвалидации кэша, давайте посмотрим, что такое кэш на самом деле. Википедия говорит, что кэш — это просто промежуточный буфер с быстрым доступом к нему, содержащий информацию, которая может быть запрошена с наибольшей вероятностью.

В этой статье мы поговорим о трех наиболее часто используемых типах кэша:

Кэш браузера

Каждый раз, когда вы посещаете веб-сайт впервые, браузер локально сохраняет ресурсы веб-страницы (например, html, css, js, изображения и так далее). Это нужно для более быстрой работы и меньшего потребления трафика при следующем посещении.

Инвалидация кэша что это. Смотреть фото Инвалидация кэша что это. Смотреть картинку Инвалидация кэша что это. Картинка про Инвалидация кэша что это. Фото Инвалидация кэша что это

Инвалидация кэша браузера

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

Сети доставки контента (CDN)

Сеть доставки контента (CDN) способна на больше, чем просто кэширование. Она хранит данные в географически распределенных местах, из-за чего время приема и передачи в конкретный географически локализованный браузер и обратно сокращается. Благодаря этому ваш браузер получает данные из ближайшего к вам узла сети CDN.

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

Инвалидация кэша что это. Смотреть фото Инвалидация кэша что это. Смотреть картинку Инвалидация кэша что это. Картинка про Инвалидация кэша что это. Фото Инвалидация кэша что это

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

Инвалидация кэша в сетях доставки контента (CDN)

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

Кэширование в базах данных

В предыдущем разделе мы обсудили сети доставки контента (CDN) и тот факт, что они являются посредниками между клиентом и сервером. Аналогичным образом система кэширования базы данных является посредником между сервером и базой данных. Существует множество таких систем кэширования, например redis, memcache и т. д. Их работа объяснена ниже:

Инвалидация кэша что это. Смотреть фото Инвалидация кэша что это. Смотреть картинку Инвалидация кэша что это. Картинка про Инвалидация кэша что это. Фото Инвалидация кэша что это

Инвалидация кэша в базах данных

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

Теперь перейдем к вопросу, почему начинающим разработчикам приходится с этим бороться. Как правило, в современных стеках технологий применяются все три вида кэширования. И иногда разработчики застревают при отладке.

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

Источник

К вопросу об инвалидации кеша

Полнота и избыточность

Начнём всё же с общих соображений не специфичных ни для SQL-запросов, ни для ORM. Упомянутые полноту и избыточность я определяю следующим образом. Полнота инвалидации — это её характеристика, определяющая насколько часто и в каких случаях может/будет возникать ситуация когда в кеше будут содержаться грязные данные и как долго они там будут оставаться. Избыточностью, в свою очередь, назовём то как часто кеш будет инвалидироваться без необходимости.

Рассмотрим для примера распространённый способ инвалидации по времени. С одной стороны, он практически гарантирует, что сразу после изменения данных кеш грязен. С другой стороны, время которое кеш остаётся грязным, мы можем легко ограничить уменьшив время жизни (что в свою очередь сократит процент попаданий). Т.е. при сокращении времени жизни кеша полнота инвалидации улучшается, а избыточность ухудшается. В итоге, чтобы достигнуть идеальной полноты инвалидации (никаких грязных данных) мы должны выставить таймаут в 0, или, другими словами, отключить кеш. Во многих случаях временное устаревание данных в кеше допустимо. Например, как правило, не так уж и страшно если новость в блоке последних новостей появится там на несколько минут позже или общее количество пользователей вашей социальной сети будет указано с ошибкой в пару-тройку тысяч.

Инвалидация по событию

Способ с инвалидацией по времени хорош своей простотой, однако, не всегда применим. Что ж, можно сбрасывать кеш при изменении данных. Одной из проблем при таком подходе является то, что при добавлении нового запроса, который мы кешируем приходиться добавлять код для его инвалидации в при изменении данных. Если мы используем ORM, то данные изменяются (в хорошем случае) в одном месте — при сохранении модели. Наличие одного центрального кода изменения данных облегчает задачу, однако, при большом количестве разнообразных запросов приходиться всё время дописывать туда всё новые и новые строки сброса различных кусочков кеша. Таким образом, мы получаем на свою голову избыточную связность кода. Пора её ослабить.

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

Автоматическая инвалидация ORM-запросов

Вспомним, что у нас есть ORM, а для него каждый запрос представляет не просто текст, а определённую структуру — модели, дерево условий и прочее. Так что, по идее, ORM может и кешировать и вешать инвалидационные обработчики прямо при кешировании по мере надобности. Чертовски привлекательное решение для ленивых ребят, вроде меня.

Небольшой пример. Допустим мы выполняем запрос:

select * from post where category_id =2 and published

и т.д.
В реальности в инвалидаторах удобнее хранить списки ключей кеша, а не тексты запросов, тексты здесь для наглядности.

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

Очевидно, нужно как-то группировать и отсеивать инвалидаторы без их полной проверки. Заметим, что картина когда условия различаются только значениями. Например, инвалидаторы в модели post все имеют вид category_id=? and published=. Сгруппируем инвалидаторы из примера по схемам:

то единственный подходящий инвалидатор из семейства будет category_id=2 and published=true и, следовательно нужно стереть соответствующие ему 3 ключа кеша. Т.е. не требуется последовательная проверка условий мы сразу получаем нужный инвалидатор по схеме и данным объекта.

Однако, что делать с более сложными условиями? В отдельных случаях кое-что можно сделать: or разложить на два инвалидатора, in развернуть в or. В остальных случаях либо придётся всё усложнить, либо сделать инвалидацию избыточной, отбросив такие условия. Приведём то, какими будут инвалидаторы для foo после таких преобразований:

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

Источник

Каскадная инвалидация кэша. Часть 1

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

Все известные мне средства для сборки проектов поддерживают добавление к именам файлов хэша их содержимого. Делается это с помощью простого конфигурационного правила (наподобие того, что показано ниже):

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

Эксперты в сфере производительности веб-проектов, кроме того, рекомендуют пользоваться методиками разделения кода. Эти методики позволяют разбивать JavaScript-код на отдельные бандлы. Такие бандлы могут быть загружены браузером параллельно, или даже лишь тогда, когда в них возникнет необходимость, по запросу браузера.

Одним из многих преимуществ разделения кода, в частности, имеющих отношение к передовым методикам кэширования, называют то, что изменения, внесённые в отдельный файл с исходным кодом, не приводят к инвалидации кэша всего бандла. Другими словами, если для npm-пакета, созданного разработчиком «X», вышло обновление безопасности, и при этом содержимое node_modules разбито на фрагменты по разработчикам, то изменить придётся только фрагмент, содержащий пакеты, созданные «X».

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

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

Проблема, касающаяся версионирования имён файлов

Представьте, что вы создали и развернули веб-сайт. Вы воспользовались разделением кода, в результате большая часть JavaScript-кода вашего сайта загружается по запросу.

Инвалидация кэша что это. Смотреть фото Инвалидация кэша что это. Смотреть картинку Инвалидация кэша что это. Картинка про Инвалидация кэша что это. Фото Инвалидация кэша что это

Типичное дерево зависимостей JavaScript-модуля

И наконец, так как содержимое файла main изменилось, имя этого файла тоже должно будет измениться.

Вот как теперь будет выглядеть диаграмма зависимостей.

Инвалидация кэша что это. Смотреть фото Инвалидация кэша что это. Смотреть картинку Инвалидация кэша что это. Картинка про Инвалидация кэша что это. Фото Инвалидация кэша что это

Модули в дереве зависимостей, на которые повлияло единственное изменение в коде одного из листовых узлов дерева

Из этого примера видно, как небольшое изменение кода, сделанное всего лишь в одном файле, привело к инвалидации кэша 80% фрагментов бандла.

Хотя и правда то, что не все изменения приводят к столь печальным последствиям (например, инвалидация кэша листового узла приводит к инвалидации кэша всех узлов вплоть до корневого, но инвалидация кэша корневого узла не вызывает каскадной инвалидации, доходящей до листовых улов), в идеальном мире нам не приходилось бы сталкиваться с любыми ненужными инвалидациями кэша.

Это приводит нас к следующему вопросу: «Можно ли получить преимущества, даваемые иммутабельными ресурсами и долговременным кэшированием, и при этом не страдать от каскадных инвалидаций кэша?».

Подходы к решению проблемы

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

Как оказалось, существует несколько способов достижения этой цели:

Подход №1: карты импорта

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

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

Использование карт импорта для предотвращения каскадной инвалидации кэша состоит из трёх шагов.

▍Шаг 1

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

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

Команды импорта в соответствующих модулях тоже не будут включать в себя хэши:

▍Шаг 2

Нужно воспользоваться инструментом, наподобие rev-hash, и сгенерировать с его помощью копию каждого файла, к имени которого добавлен хэш, указывающий на версию его содержимого.
После того, как эта часть работы выполнена, содержимое выходной директории должно будет выглядеть примерно так, как показано ниже (обратите внимание на то, что там теперь присутствует по два варианта каждого файла):

▍Шаг 3

Нужно создать JSON-объект, хранящий сведения о соответствии каждого файла, в имени которого нет хэша, каждому файлу, в имени которого хэш есть. Этот объект нужно добавить в HTML-шаблоны.

Этот JSON-объект и является картой импорта. Вот как он может выглядеть:

После этого всякий раз, когда браузер увидит команду импорта файла, находящегося по адресу, соответствующему одному из ключей карты импорта, браузер импортирует тот файл, который соответствует значению ключа.

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

Возможно, вы сейчас задаётесь вопросом о том, почему я создал копию каждого файла вместо того, чтобы просто файлы переименовать. Это нужно для поддержки браузеров, которые не могут работать с картами импорта. В предыдущем примере подобные браузеры увидят лишь файл /vendor.mjs и просто загрузят этот файл, поступив так, как обычно поступают, встречая подобные конструкции. В результате и оказывается, что на сервере должны существовать оба файла.

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

Уважаемые читатели! Знакома ли вам проблема каскадной инвалидации кэша?

Источник

Новые директивы HTTP для кеширования с учётом CDN

В данный момент на рассмотрении IETF находятся два стандарта. Это новые поля в заголовке ответа HTTP: Cache-Status и таргетированный Cache-Control .

Новые поля должны упростить разработку веб-приложений, а именно: упорядочить кеширование статического контента. Сейчас с этим небольшой бардак, поскольку кеширование происходит в нескольких системах на нескольких уровнях, почти как в этом ↑ комиксе. А синхронизировать все уровни непросто, ведь текущие стандарты разрабатывались в те времена, когда ещё не существовало CDN.

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

Кеш снимает с сервера до 99% нагрузки. В результате стоимость хостинга может упасть до смехотворной величины. Например, известный специалист по безопасности Трой Хант платит за облачный хостинг своего популярного сайта Pwned Passwords (базы утёкших паролей) меньше 3 центов в день, а у него 19 ГБ файлов и 34,4 млн запросов к API в неделю (правда, это статистика за 2018 год, с тех пор размер баз наверняка вырос). Как видно на скриншоте, из 32 408 715 запросов к API только 122 566 пошли на сервер, а остальные 99,62% получили результат из кеша.

Инвалидация кэша что это. Смотреть фото Инвалидация кэша что это. Смотреть картинку Инвалидация кэша что это. Картинка про Инвалидация кэша что это. Фото Инвалидация кэша что это

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

Кроме количества запросов, CDN на порядок снижает трафик с сервера. В данном случае общий трафик за неделю составил 477,63 ГБ, в том числе оплаченный трафик с облака — всего 945,96 МБ. Теперь понятно, почему хостинг обходится ему так дёшево.

Инвалидация кэша что это. Смотреть фото Инвалидация кэша что это. Смотреть картинку Инвалидация кэша что это. Картинка про Инвалидация кэша что это. Фото Инвалидация кэша что это

Уровни кеширования

Так в чём проблема? Казалось бы, существующие механизмы кеширования работают отлично, на порядок ускоряют загрузку контента пользователям и экономят кучу денег на хостинге.

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

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

Инвалидация кэша что это. Смотреть фото Инвалидация кэша что это. Смотреть картинку Инвалидация кэша что это. Картинка про Инвалидация кэша что это. Фото Инвалидация кэша что это

Дополнительно на дальнем уровне обычно работает CDN-провайдер типа Cloudflare, который раздаёт контент пользователям через свою сеть серверов. Свои кеширующие прокси могут стоять также у интернет-провайдера или в корпоративной сети.

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

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

Проблемы с инвалидацией кеша

Инвалидация или очистка кеша — удаление всех закешированных объектов, связанных с изменениями в состоянии модели. Наиболее распространённый тип инвалидации — прямое удаление объектов. Но если мы имеем дело с пятью или десятью уровнями кеша на пути от бэкенда к конечным пользователям, то гарантировать инвалидацию не так просто.

Тут поджидает целый ряд скрытых угроз: из-за некорректной инвалидации мы можем просто поломать механизм кеширования, так что ничего не будет кешироваться вообще — и 100% запросов хлынут на бэкенд (как вариант, контент будет сохраняться только в локальном кеше, но не в CDN). Или, наоборот, ответы сохранятся там дольше, чем положено, а пользователям будет отгружаться устаревший контент. Из-за нескольких уровней кеширования может получиться так, что клиенты начнут получать смесь из новой и старой информации.

И главное, что сама конфигурация кеша из заголовка Cache-Control тоже кешируется, что может привести к большим трудностям в инвалидации.

Новые стандарты призваны всё это исправить.

Итак, что же предлагает Марк Ноттингем с соавторами новых спецификаций? Если кто-то не знает, Марк Ноттингем — очень авторитетный специалист, один из самых известных разработчиков инфраструктуры современного веба, входил в группу технической архитектуры W3C и в настоящее время является членом Совета по архитектуре Интернета.

Cache-Status

Спецификация Cache-Status определяет новое поле заголовка ответа HTTP со стандартизированным синтаксисом и семантикой для всех уровней кеширования, упомянутых выше.

Это поле показывает, каким образом система кеширования обработала данный ответ и соответствующий ему запрос. Синтаксис по стандарту RFC8941 (структурированные поля для HTTP, то есть точка с запятой между параметрами, запятая между кешами) и представляет собой список:

Каждый член списка — это одна из систем кеширования, которая обработала пакет по ходу его маршрута. Все они перечислены в порядке от бэкенда до пользователя (возможно, включая кеш самого агента пользователя, если он добавляет значение).

(два уровня кеша, где OriginCache ответил на предыдущий запрос с сохранённым значением, а CDN сохранил этот ответ и повторно использовал его для ответа на текущий запрос)

(здесь три уровня, два из которых пропустили запрос мимо кеша из-за отсутствия данного URI, а Nginx ответил из кеша)

Для каждого кеша спецификация предусматривает ряд параметров:

Стирать предыдущие строки запрещено, чтобы можно было отладить всю цепочку кешей, обрабатывающих запрос.

Поле Cache-Status вносит ясность и порядок во всю систему многочисленных кешей на пути HTTP-ответа. Сразу видно, пакет идёт от сервера или из кеша. Если из кеша, то из какого именно, как долго там будет храниться этот контент. Если не из кеша, то почему произошёл промах и сохранился ли в кеше этот новый ответ.

Это поле рассказывает всю историю пакета и всех кешей, через которые он прошёл, что исключительно полезно при отладке.

Таргетированный Cache-Control

Cache-Control — это список прямых инструкций кеширования, которые задаются сервером для запросов и ответов. Например, список инструкций для запросов:

Инструкции для ответов:

Все они хорошо известны разработчикам. Проблема в том, что эти инструкции «слепые» и направлены всем кешам одновременно, что в наше время не очень эффективно.

Таргетированный Cache-Control просто указывает, какой конкретно системе предназначены инструкции. Например, можно обновлять контент только для запросов во внутренний балансировщик нагрузки, но не в CDN. Или включить кеширование объекта только в CDN, но в других внешних системах. Естественно, все они должны поддерживать эти новые спецификации, чтобы выполнять таргетированные инструкции.

(инструкция для CDN: считать ответ свежим 600 секунд, другим общим кешам — 120 секунд, а всем остальным — 60 секунд)

(разрешение на кеширование только в CDN и Squid, больше никому)

В данном виде спецификация предусматривает только одну «цель» для таргетирования — это CDN. То есть директива CDN-Cache-Control относится ко всем CDN. Но в будущем в спецификации могут добавить другие классы систем. Например, ISP-провайдеров, браузеры или корпоративные сети.

Разумеется, выполнять директиву CDN-Cache-Control может только тот CDN, который понимает эту спецификацию.

Принятие индустрией

По идее, Cache-Status должен объединить в себе поля ответа HTTP всех существующих провайдеров, которые зачастую несовместимы между собой, такие как X-Cache-Status от Nginx, CF-Cache-Status от Cloudflare, X-Served-By и X-Cache от Fastly и другие. Будем надеяться, что все они постепенно перейдут на Cache-Status и мы не получим ситуацию как в другом комиксе xkcd со стандартами.

Инвалидация кэша что это. Смотреть фото Инвалидация кэша что это. Смотреть картинку Инвалидация кэша что это. Картинка про Инвалидация кэша что это. Фото Инвалидация кэша что это

Обсуждение Cache-Status и таргетированного Cache-Control продолжается. Можно вносить предложения в гитхабе рабочей группы IETF HTTP.

НЛО прилетело и оставило здесь промокоды для читателей нашего блога:

Источник

Каскадная инвалидация кэша. Часть 1

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

Все известные мне средства для сборки проектов поддерживают добавление к именам файлов хэша их содержимого. Делается это с помощью простого конфигурационного правила (наподобие того, что показано ниже):

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

Эксперты в сфере производительности веб-проектов, кроме того, рекомендуют пользоваться методиками разделения кода. Эти методики позволяют разбивать JavaScript-код на отдельные бандлы. Такие бандлы могут быть загружены браузером параллельно, или даже лишь тогда, когда в них возникнет необходимость, по запросу браузера.

Одним из многих преимуществ разделения кода, в частности, имеющих отношение к передовым методикам кэширования, называют то, что изменения, внесённые в отдельный файл с исходным кодом, не приводят к инвалидации кэша всего бандла. Другими словами, если для npm-пакета, созданного разработчиком «X», вышло обновление безопасности, и при этом содержимое node_modules разбито на фрагменты по разработчикам, то изменить придётся только фрагмент, содержащий пакеты, созданные «X».

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

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

Проблема, касающаяся версионирования имён файлов

Представьте, что вы создали и развернули веб-сайт. Вы воспользовались разделением кода, в результате большая часть JavaScript-кода вашего сайта загружается по запросу.

Инвалидация кэша что это. Смотреть фото Инвалидация кэша что это. Смотреть картинку Инвалидация кэша что это. Картинка про Инвалидация кэша что это. Фото Инвалидация кэша что это

Типичное дерево зависимостей JavaScript-модуля

И наконец, так как содержимое файла main изменилось, имя этого файла тоже должно будет измениться.

Вот как теперь будет выглядеть диаграмма зависимостей.

Инвалидация кэша что это. Смотреть фото Инвалидация кэша что это. Смотреть картинку Инвалидация кэша что это. Картинка про Инвалидация кэша что это. Фото Инвалидация кэша что это

Модули в дереве зависимостей, на которые повлияло единственное изменение в коде одного из листовых узлов дерева

Из этого примера видно, как небольшое изменение кода, сделанное всего лишь в одном файле, привело к инвалидации кэша 80% фрагментов бандла.

Хотя и правда то, что не все изменения приводят к столь печальным последствиям (например, инвалидация кэша листового узла приводит к инвалидации кэша всех узлов вплоть до корневого, но инвалидация кэша корневого узла не вызывает каскадной инвалидации, доходящей до листовых улов), в идеальном мире нам не приходилось бы сталкиваться с любыми ненужными инвалидациями кэша.

Это приводит нас к следующему вопросу: «Можно ли получить преимущества, даваемые иммутабельными ресурсами и долговременным кэшированием, и при этом не страдать от каскадных инвалидаций кэша?».

Подходы к решению проблемы

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

Как оказалось, существует несколько способов достижения этой цели:

Подход №1: карты импорта

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

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

Использование карт импорта для предотвращения каскадной инвалидации кэша состоит из трёх шагов.

▍Шаг 1

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

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

Команды импорта в соответствующих модулях тоже не будут включать в себя хэши:

▍Шаг 2

Нужно воспользоваться инструментом, наподобие rev-hash, и сгенерировать с его помощью копию каждого файла, к имени которого добавлен хэш, указывающий на версию его содержимого.
После того, как эта часть работы выполнена, содержимое выходной директории должно будет выглядеть примерно так, как показано ниже (обратите внимание на то, что там теперь присутствует по два варианта каждого файла):

▍Шаг 3

Нужно создать JSON-объект, хранящий сведения о соответствии каждого файла, в имени которого нет хэша, каждому файлу, в имени которого хэш есть. Этот объект нужно добавить в HTML-шаблоны.

Этот JSON-объект и является картой импорта. Вот как он может выглядеть:

После этого всякий раз, когда браузер увидит команду импорта файла, находящегося по адресу, соответствующему одному из ключей карты импорта, браузер импортирует тот файл, который соответствует значению ключа.

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

Возможно, вы сейчас задаётесь вопросом о том, почему я создал копию каждого файла вместо того, чтобы просто файлы переименовать. Это нужно для поддержки браузеров, которые не могут работать с картами импорта. В предыдущем примере подобные браузеры увидят лишь файл /vendor.mjs и просто загрузят этот файл, поступив так, как обычно поступают, встречая подобные конструкции. В результате и оказывается, что на сервере должны существовать оба файла.

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

Уважаемые читатели! Знакома ли вам проблема каскадной инвалидации кэша?

Источник

Leave a Reply

Your email address will not be published. Required fields are marked *