Repository navigation
Honor a cache-restored build when a versioned extension mismatches - #1108
Conversation
When a PHP package install bundles its own default build of a versioned extension (e.g. redis ships with its own recent version pre-enabled), add_pecl_extension correctly detects the version mismatch against the pinned version and falls back to pecl_install - but it does this even when a matching build has already been restored from a CI cache into a separate directory, since shared_extension only checks that *some* file exists at ext_dir, not which version it is. This adds an opt-in CACHED_EXTENSIONS_DIR: when set and a mismatch is detected, look for extension.so there, and if found, copy it into ext_dir and re-check before falling back to a full pecl_install. When CACHED_EXTENSIONS_DIR is unset, behavior is unchanged. Related to shivammathur/cache-extensions#21 and shivammathur#37, where a cached build of a versioned PECL extension is silently ignored and recompiled on every run regardless of caching.
|
I don't have write access here so I can't formally request reviewers - tagging the people who've actually touched this file/directory before, in case any of you have a moment: @shivammathur (you wrote |
There was a problem hiding this comment.
Thanks for working on this. Could you please update the PR to avoid introducing CACHED_EXTENSIONS_DIR env?
For an exact version request such as redis-5.3.7, setup-php could do the following:
- Keep the active binary at its normal path: redis.so on Unix or php_redis.dll on Windows.
- Preserve a second copy directly in extension_dir as redis-5.3.7, without a .so or .dll suffix.
- On subsequent runs, when redis-5.3.7 is requested and the preserved file exists, copy it over the active binary, enable the extension, and verify that the loaded version is 5.3.7.
- If the preserved file is missing or invalid, follow the current flow: check the installed version and install the requested version on mismatch.
- After installing a versioned extension, preserve the resulting binary for the next run.
cache-extensions already caches extension_dir, so no additional directory or configuration is required. The preserved copy should not have a shared-library suffix, otherwise setup-php may treat it as an active extension while scanning.
… dir Per @shivammathur's review on the PR: drop the opt-in CACHED_EXTENSIONS_DIR env var. Instead, after installing a versioned extension, keep a second copy of the built .so in ext_dir itself under a name with no .so suffix (e.g. redis-5.3.7), so it can't be mistaken for the active extension and rides along in the same ext_dir that cache-extensions already caches wholesale - no separate cache directory, key, or workflow step required. On a version mismatch, restore that preserved build over the active binary and re-check before falling back to a full pecl_install.
|
Updated - dropped |
The 2.37.2 pin predates shivammathur/setup-php#1108 ("Honor a cache-restored build when a versioned extension mismatches"), so the bundled default mongodb build (reporting 2.5.3) overwrites the cache-restored build and every job falls through to pecl, whose fail-open fallback silently enables the wrong version.
Problem
When a PHP package install bundles its own default build of a versioned extension (e.g. redis ships with a recent version pre-enabled),
add_pecl_extensioncorrectly detects the version mismatch against the pinned version and falls back topecl_install- but it does this even when a matching build has already been restored from a CI cache into a separate directory, sinceshared_extensiononly checks that some file exists atext_dir, not which version it is.In practice this means
shivammathur/cache-extensionsnever actually helps for a pinned-version PECL extension: the cache-restored.sogets silently overwritten by the PHP package's own default build beforeadd_pecl_extensionever runs, so the version check always mismatches and always falls through to a fullpecl_install, regardless of caching.This reproduces the exact symptom reported in #21 and #37 (both currently open) - a cached build of a versioned extension is ignored and recompiled on every run.
Fix
Confirmed the mechanism by instrumenting
enable_extension/add_pecl_extensiondirectly and running in CI:enable_extension(redis,...)findscheck_extension=yesimmediately (a default version, e.g. 6.0.2, is already loaded before this function ever runs).sofile backingext_dir/redis.sois still the package's default build, not the cache-restored oneext_dirbefore the re-check resolves it:phpversion('redis')then correctly reports the pinned version and the run logsEnabledinstead ofInstalled and enabledThis adds an opt-in
CACHED_EXTENSIONS_DIR. When set and a version mismatch is detected,add_pecl_extensionlooks for$extension.sothere and copies it intoext_dirbefore re-checking, ahead of falling back topecl_install. WhenCACHED_EXTENSIONS_DIRis unset, behavior is unchanged - existing workflows aren't affected.The intended pairing is: restore a cache to a directory outside
ext_dir(so the package install can't overwrite it before this check runs), pass that path viaCACHED_EXTENSIONS_DIR, and afterSetup PHPcompletes, refresh that directory from the freshly-built (or now-confirmed-cached).sofor the next run's cache save.Verified end-to-end in our own CI against a real ~4500-test PHP 7.3 suite:
Setup PHPdropped from ~50-70s (recompilingredis,rdkafka,apcufrom source every run) to ~7s on a cache hit, with all three extensions loggingEnabled.Happy to add a test case or adjust the approach if you'd prefer a different shape for this (e.g. checking the cache directory unconditionally rather than only on mismatch, or wiring
cache-extensionsitself to populateCACHED_EXTENSIONS_DIRdirectly).