Skip to content

perf(build): не архивировать файлы компонента в .preserved.zip при установке (#783) - #784

Merged
biz87 merged 2 commits into
betafrom
dev/install-preserve-vendor
Sep 24, 2026
Merged

biz87 merged 2 commits into
betafrom
dev/install-preserve-vendor

Conversation

@biz87

@biz87 biz87 commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Убирает архивацию .preserved.zip при установке и обновлении. Это ускоряет обновление примерно на треть ценой режима «Восстановить» при деинсталляции — сознательный размен, см. ниже.

Что происходило

core/ и assets/ ехали file-резолверами на vehicle категории, то есть наследовали её PRESERVE_PREEXISTING. На каждом обновлении xPDO зипует установленный core/components/minishop3 целиком — 4248 файлов — в .preserved.zip.

Что теряется

Первая редакция этого описания утверждала, что архив никто не читает. Это было неверно, спасибо за проверку.

Режим RESTORE_PREEXISTING приходит прямо из интерфейса: окно деинсталляции предлагает «Восстановить» с inputValue: 2 (manager/assets/modext/workspace/package.windows.js), а Processors/Workspace/Packages/Uninstall.php передаёт выбор в опции vehicle. Я искал по имени константы и не заметил, что значение приезжает из UI обычным числом.

После этого PR:

  1. архива нет, восстанавливать нечего;
  2. xPDOVehicle::get() делает array_merge($options, $element), то есть зашитый в payload режим перекрывает выбор пользователя;
  3. «Восстановить» вернёт объекты категории к прежней версии, а файлы компонента удалит — получится рассинхрон.

Режимы «Сохранение» (по умолчанию) и «Деинсталлировать» не меняются: они и раньше удаляли файлы.

Считаю размен оправданным: этот откат и раньше был неполным, потому что Phinx-миграции не откатываются, а платили за архив все и на каждом обновлении. Но теперь это записано как есть — и в docblock, и в changelog.

Почему нельзя было точечно

  • xPDOTransport::_pack() архивирует целевой каталог на сайте, а не источник. Исключение vendor/ из копирования на архивацию не влияет.
  • PREEXISTING_MODE общий для всех резолверов vehicle. Поставить его на vehicle категории нельзя: он управляет и объектами — при REMOVE_PREEXISTING они удаляются при деинсталляции вместо сохранения.
  • Дробить core-резолвер по подкаталогам, чтобы vendor стал отдельным именем, — это 17 резолверов и 17 архивов.

Поэтому файлы переехали в собственные xPDOFileVehicle со своим режимом.

Замер

Обновление тем же пакетом, прогретый кэш ФС, по три прогона:

Время Архивов записано
было 3,09 / 4,58 / 3,10 с 2
стало 2,16 / 2,19 / 2,21 с 0

Около 30%. В #783 я сперва написал «три четверти» — та оценка снята с холодного кэша и для прогретого завышена. На реальном сайте, где обновления редки, выигрыш будет между этими числами; замера на shared-хостинге у меня нет.

Довод про max_execution_time из первой редакции тоже убран: modTransportPackage::install() сам выставляет его в 0, так что упираются скорее в таймаут FastCGI или прокси.

Что не изменилось

  • Удаление файлов. xPDOFileVehicle::_uninstallFiles() сносит components/minishop3, пока UNINSTALL_FILES не скажет иначе, — ровно как резолвер. Каталог components/ целиком не трогается, name задан явно.
  • Порядок установки. File-vehicle идут до vehicle категории, поэтому PHP-резолверы находят vendor/autoload.php на диске. В манифесте позиции 222–223 против 224 у категории.
  • Поведение объектов. Атрибуты vehicle категории не тронуты.

Побочно стало надёжнее при деинсталляции: xPDOTransport::uninstall() идёт по vehicle в обратном порядке, так что теперь резолверы категории отрабатывают, пока файлы ещё на месте. Раньше файловые резолверы стояли внутри категории и файлы исчезали раньше. Сейчас их uninstall-ветки файлы не используют, но запас есть.

Правки после ревью

  • docblock packageFiles() переписан: размен назван прямо, довод про max_execution_time убран;
  • запись про изменение поведения добавлена в CHANGELOG.md и docs/changelog.txt под [Unreleased];
  • readLatestChangelogEntry() научился пропускать [Unreleased] — иначе этот заголовок уехал бы в метаданные пакета вместо версии ([Bug] Обновление до 1.11.0-beta1: Data too long for column 'metadata' в modx_transport_packages #296);
  • в BuildFileVehicleTest починен регэксп: [^\]]* останавливался на вложенном RELATED_OBJECT_ATTRIBUTES => [], а ключ пишется константой в верхнем регистре, так что проверка никогда не срабатывала. Проверено мутацией — теперь падает;
  • добавлен BuildChangelogMetadataTest на пропуск [Unreleased] и лимит TEXT.

Локально: PHPStan без ошибок, smoke 121 тест.

🤖 Generated with Claude Code

…783)

core/ and assets/ travelled as file resolvers on the category vehicle, so they
inherited PRESERVE_PREEXISTING and every upgrade zipped the installed
core/components/minishop3 — 4248 files. That archive is write-only: xPDO unpacks
it solely under RESTORE_PREEXISTING, a mode nothing in xPDO or MODX ever sets.

The mode cannot be narrowed in place. Setting it on the category vehicle would
also change how its objects behave on uninstall, and excluding vendor/ from the
resolver would not help either: _pack() archives the target directory on the
site, not the source.

Upgrade on a warm filesystem cache, same package, three runs each:
  before  3.09 / 4.58 / 3.10 s, two archives written
  after   2.16 / 2.19 / 2.21 s, none

Removal on uninstall is unchanged: xPDOFileVehicle deletes the target tree
unless UNINSTALL_FILES says otherwise, exactly as the resolver did. The file
vehicles are registered before the category so the PHP resolvers still find
vendor/autoload.php on disk.
…ody reads the archive

Review caught a wrong premise. RESTORE_PREEXISTING is not dead code: the uninstall
dialog offers it as preexisting_mode=2 (manager/assets/modext/workspace/
package.windows.js) and Processors/Workspace/Packages/Uninstall.php passes the
choice straight into the vehicle options. I had grepped for the constant name and
missed that the value arrives as a bare integer from the UI.

The change still stands, but as a deliberate trade-off: with no archive there is
nothing to restore, and xPDOVehicle::get() merges the payload over the caller's
options, so the pinned mode wins over the user's pick. "Restore" now rolls category
objects back while the files are removed. It was already partial — Phinx migrations
never roll back — and the archive cost every upgrade on every site.

- Rewrite the packageFiles() docblock accordingly; drop the max_execution_time
  argument too, since modTransportPackage::install() already sets it to 0 and the
  real wall is a FastCGI or proxy timeout.
- Record the behaviour change under [Unreleased] in both changelogs.
- readLatestChangelogEntry() now skips [Unreleased], or package metadata would ship
  that heading instead of the installed version (#296).
- BuildFileVehicleTest: the category-attributes guard never fired — [^\]]* stopped at
  the nested RELATED_OBJECT_ATTRIBUTES => [] and the key is spelled in upper case.
  Verified by mutation that it now fails.
- New BuildChangelogMetadataTest covers the [Unreleased] skip and the TEXT limit.
@biz87
biz87 merged commit 7d829a4 into beta Sep 24, 2026
6 checks passed
@biz87
biz87 deleted the dev/install-preserve-vendor branch September 24, 2026 20:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant