Кэш в CI/CD окружении

В условиях автоматизированной доставки кода ключевым фактором становится стабильность и скорость повторяемых сборок. В экосистеме Parcel кэширование встроено на нескольких уровнях: файловый кэш сборки, кэш зависимостей, кэш трансформаций модулей и интеграция с внешними CI/CD системами.

CI/CD окружения в связке с Node.js требуют особого подхода к сохранению промежуточных результатов, поскольку каждая сборка чаще всего выполняется в чистом контейнере. Без правильно настроенного кэша Parcel фактически выполняет полную пересборку графа зависимостей при каждом запуске пайплайна.


Архитектура кэша Parcel и его значение в CI

Parcel использует многослойную систему кэширования:

Файловый кэш .parcel-cache

Основной механизм ускорения сборки — директория .parcel-cache. В ней хранятся:

  • результаты трансформации модулей;
  • граф зависимостей;
  • результаты резолва импортов;
  • промежуточные AST-деревья;
  • метаданные о хешах файлов.

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

Инкрементальная модель сборки

Parcel не пересобирает весь проект при изменении одного файла. Вместо этого:

  1. Пересчитывается хеш изменённого модуля.
  2. Инвалидируются только зависимые узлы графа.
  3. Используются ранее сохранённые трансформации для неизменённых модулей.

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


Особенности работы кэша в CI/CD окружении

CI/CD системы, такие как GitHub Actions, GitLab CI и аналогичные, используют эфемерные среды выполнения. Это создаёт проблему: кэш Parcel не сохраняется между запусками по умолчанию.

Проблема чистого окружения

Каждый pipeline стартует в состоянии:

  • отсутствует .parcel-cache;
  • отсутствуют локальные node_modules (или они пересобираются);
  • отсутствует информация о предыдущих графах сборки.

Следствие — полная пересборка проекта даже при минимальных изменениях.


Стратегии кэширования Parcel в CI

1. Сохранение .parcel-cache как CI artifact

Базовый подход — сохранение директории кэша между job-ами:

  • сохраняется .parcel-cache;
  • восстанавливается перед сборкой;
  • обновляется после завершения pipeline.

Важное свойство: кэш Parcel можно безопасно переносить между агентами, так как он основан на контентных хешах.


2. Кэширование зависимостей node_modules

Хотя Parcel не зависит напрямую от структуры node_modules, скорость резолва модулей существенно зависит от их наличия.

В CI обычно применяются стратегии:

  • кэширование node_modules;
  • кэширование менеджеров пакетов (npm/yarn/pnpm store);
  • восстановление lock-файлов.

Это уменьшает время подготовки окружения до этапа сборки.


3. Разделение кэша по ключам окружения

Кэш Parcel чувствителен к:

  • версии Node.js;
  • версии Parcel;
  • lock-файлам зависимостей;
  • операционной системе CI runner-а.

Поэтому применяется стратегия ключей:

  • ключ: hash(package-lock.json + version + OS);
  • fallback-ключ: частичный кэш без строгого совпадения.

Это позволяет переиспользовать кэш при незначительных изменениях зависимостей.


4. Глобальный CI cache store

Современные CI системы предоставляют централизованный кэш:

  • GitHub Actions cache API;
  • GitLab CI cache;
  • S3-совместимые хранилища.

Parcel-кэш обычно помещается в один архив вместе с зависимостями или отдельно:

  • .parcel-cache
  • .npm / .pnpm-store

Инвалидация кэша и типичные ошибки

Некорректная инвалидизация

Parcel автоматически управляет кэшем, но CI может ломать эту модель:

  • очистка директорий между шагами;
  • изменение прав доступа;
  • частичное восстановление кэша.

Следствие — кэш существует, но не используется.


Несовместимость версий Parcel

Кэш бинарно несовместим между версиями:

  • Parcel 1 и Parcel 2 используют разные форматы;
  • minor-обновления могут менять структуру кэша;
  • пересборка после обновления обязательна.

Проблемы монорепозиториев

В монорепозиториях:

  • один кэш используется для нескольких пакетов;
  • изменения в одном пакете могут инвалидировать весь граф;
  • кэш разрастается по размеру.

Parcel частично решает это через изоляцию графа зависимостей, но CI всё равно требует грамотного разделения кэшей по workspace.


Оптимизация кэша в контейнеризированных CI

При использовании Docker в CI:

  • слой node_modules можно кешировать как image layer;
  • .parcel-cache сохраняется в volume;
  • build stage отделяется от dependency install stage.

Это позволяет добиться эффекта warm cache даже при пересоздании контейнеров.


Пример типовой модели кэширования

Схема CI pipeline обычно делится на этапы:

  1. Установка зависимостей

    • восстановление package store
    • установка node_modules
  2. Восстановление кэша Parcel

    • загрузка .parcel-cache
  3. Сборка

    • инкрементальная обработка графа
    • использование сохранённых трансформаций
  4. Сохранение кэша

    • обновлённый .parcel-cache
    • обновлённый dependency cache

Влияние кэша на производительность

Правильно настроенный CI-кэш в связке с Parcel даёт следующие эффекты:

  • резкое снижение времени cold build;
  • ускорение инкрементальных сборок в 5–20 раз;
  • уменьшение нагрузки на трансформационные пайплайны (Babel, SWC, PostCSS);
  • стабильность времени сборки независимо от размера проекта после прогрева кэша.

Поведение кэша при параллельных сборках

Parcel поддерживает конкурентное выполнение, однако в CI возможны сценарии:

  • несколько jobs используют один cache key;
  • параллельная запись в .parcel-cache;
  • гонки при сохранении артефактов.

Поэтому часто применяется правило:

  • кэш читается параллельно;
  • кэш записывается только в одном финальном job.

Эффективная структура CI для Parcel-проектов

Оптимальная структура пайплайна обычно включает:

  • изолированную установку зависимостей;
  • отдельный слой кэша Parcel;
  • неизменяемый build step;
  • сохранение артефактов сборки отдельно от кэша.

Такая архитектура позволяет использовать инкрементальность Parcel максимально полно, не теряя детерминизма сборок.