M14, M15, M19 — declare and check what the bundle actually uses - #277
Open
gplanchat wants to merge 1 commit into
Open
M14, M15, M19 — declare and check what the bundle actually uses#277gplanchat wants to merge 1 commit into
gplanchat wants to merge 1 commit into
Conversation
Trois formes du même défaut : le bundle accepte ce qu'il ne peut pas tenir, et rien ne le dit avant l'incident. **Le pool de cache configuré était jeté en silence.** `hasDefinition($cacheId)` répond faux pour un alias — et `Psr\Cache\CacheItemPoolInterface` en est un — comme pour une définition posée par une extension qui tourne après celle-ci. L'exploitant écrivait `activity_contracts.cache: mon.pool`, rien n'était câblé, rien ne le signalait. La référence est désormais posée sans condition : un pool qui n'existe pas devient une erreur de compilation, que le conteneur sait nommer. **Et le résolveur re-réfléchissait à chaque appel.** Le pool vaut `null` par défaut, donc le cache warmer ne réchauffait rien et chaque appel d'activité refaisait la réflexion sur le contrat. Ces métadonnées dérivent des attributs, donc du code : elles ne peuvent pas changer tant que le processus vit. Une mémoire par instance les sert, et court-circuite aussi le pool — sur un Redis, c'était un aller-retour réseau par appel d'activité. **`lock.factory` manquant ne disait pas quoi configurer.** Le conteneur échouait déjà, sur un « service inexistant » qui nomme `lock.factory` et laisse chercher. Une passe le dit : quelle section pose ce service, et pourquoi elle n'est pas optionnelle ici — sans verrou, deux workers rejouent le même journal en même temps. Vérifié dans une passe et non dans l'extension : au chargement des extensions, celle qui pose `lock.factory` n'a pas forcément tourné. **Les deux ponts entrent dans `suggest`.** L'extension importe seize classes du pont Temporal et sept du pont DBAL ; un `composer require` du seul bundle donnait un conteneur qui compile et un fatal au premier appel. `DurableMiddlewareReachesTheBusTest` montait un conteneur avec le verrou DBAL et sans fabrique. La passe le refuse maintenant, à raison : une application dans cet état ne démarrerait pas. Le conteneur du test pose donc `lock.factory`, comme il posait déjà `messenger.bus.default` pour la même raison. Suite unit : 1081 tests contre 1073 sur main, mêmes 4 erreurs d'environnement. PHPStan : 2 erreurs, la base de main. Refs: M14, M15 et M19 de documentation/audit/ Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First batch of the bundle's major findings. Three of them, one theme: the bundle accepts what it cannot honour, and nothing says so before the incident.
M15 — the configured cache pool was silently discarded
hasDefinition()answers false for an alias — andPsr\Cache\CacheItemPoolInterfaceis one — as it does for a definition placed by an extension that runs after this one. The operator wroteactivity_contracts.cache: my.pool, nothing was wired, and nothing reported it.The reference is now placed unconditionally. A non-existent pool becomes a compile-time error, which the container knows how to name — this is the upstream convention: pass a
Referenceand letCheckExceptionOnInvalidReferenceBehaviorPassdecide.And its second half, which the report did not separate
The pool defaults to
null. So the cache warmer warmed nothing, and above all: every activity call redid the reflection on its contract. These metadata derive from attributes, hence from code — they cannot change while the process lives.A per-instance memo now serves them, and short-circuits the pool when there is one: on Redis, the previous code made one network round trip per activity call for immutable data. The test measures it against a spy pool — two resolutions, a single
getItem.M19 — a missing
lock.factorydid not say what to configureContrary to what the report implied, this was not silent: the container already failed. But on a "non-existent service" naming
lock.factoryand leaving you to search, when what you need to know is which configuration section would have placed it, and why that section is not optional here.RequireLockFactoryPasssays so, and names both ways out:framework.lock, or your own factory indurable.dbal.lock_factory. With the reason: without a lock, two workers replay the same journal at the same time.In a compiler pass rather than in the extension, for the same reason as the pool above: when extensions load, the one that places
lock.factorymay not have run, and an existence test would answer false for a correctly configured application.M14 — two bridges hard-wired, declared nowhere
The extension imports sixteen classes from
durable-bridge-temporaland seven fromdurable-bridge-dbal. A barecomposer require gplanchat/durable-bundlegave you a container that compiles and a "class not found" fatal on the first call. Both entersuggest.One existing test changed, and why
DurableMiddlewareReachesTheBusTestbuilt a container with the DBAL lock and without a lock factory. The new pass refuses it — rightly: an application in that state would not boot either. The test's container therefore placeslock.factory, exactly as it already placedmessenger.bus.defaultto stand in for what FrameworkExtension would have done.It is the only existing test touched, and it is touched because it described an impossible container.
Verification
mainunitsuitePre-existing and identical errors (
illuminate/cachemissing on this machine). No new diagnostic.What this batch does not do
config/services.php+AbstractBundle. That is a change in its own right, not a fix.#[AsWorkflow]not autoconfigured, issue [bundle] #[AsWorkflow] is not autoconfigured: WorkflowPass finds nothing unless the class is tagged by hand #255) — the obvious fix breaks compilation, because a workflow is instantiated by reflection with aWorkflowEnvironmentthat is not a service. It deserves its own pass.activity_contracts.contractslist re-enumerates by hand what the tags already carry. With the memo added here, its value shrinks further. Removing it is a public API break: to be decided, not slipped into a fix.