Heisenberg est en 0.1.0-SNAPSHOT. La spec MicroProfile Fault Tolerance 4.1 est entièrement couverte (TCK 463/463, voir état TCK), mais la SPI publique io.vidocq.heisenberg.api.* peut encore évoluer avant la 1.0.0. Cette page documente les correspondances de modèle pour préparer une migration progressive.

Côté code applicatif, la migration depuis une implémentation MP FT (SmallRye) est triviale : Heisenberg consomme strictement les annotations standard org.eclipse.microprofile.faulttolerance.*. Aucun import propriétaire à remplacer. Les écarts portent sur la configuration, le packaging et le modèle de threading. Depuis Resilience4j (API non standard), la migration est plus structurante.

Depuis SmallRye Fault Tolerance

SmallRye Fault Tolerance est l’implémentation de référence chez Quarkus et certains serveurs Jakarta EE. Migration directe — Heisenberg vise la stricte conformance à la même spec.

SmallRye Fault Tolerance Heisenberg Note

Annotations org.eclipse.microprofile.faulttolerance.*

Mêmes annotations

Aucun changement de code applicatif.

Clés MP Config fault-tolerance/…​

Mêmes clés

Précédence méthode > classe > globale identique.

Pool de threads plateforme (io.smallrye.faulttolerance.globalThreadPoolSize)

Virtual threads — pas de pool plateforme

Pas de propriété équivalente. Le nombre de threads s’ajuste seul.

Stratégies non-spec (SmallRye @RateLimit, @ApplyGuard)

Non implémentées (hors spec)

Garder uniquement les annotations MP FT 4.1 standard.

Dépendances : SmallRye Common, Jandex, SmallRye Config

Aucune (sauf API Jakarta + MicroProfile)

Empreinte de classpath réduite. Compatibilité jlink immédiate.

Depuis Resilience4j

Resilience4j est une bibliothèque indépendante (sans CDI obligatoire). La migration demande un remplacement d’annotations et de configuration.

Resilience4j Heisenberg Note

@Retry(name = "…​") + resilience4j.retry.instances.<name>.maxAttempts

@Retry(maxRetries = N) ou clé MP Config

Pas de notion de "registry" nommé — la config est par classe/méthode.

@TimeLimiter

@Timeout(value, unit)

Sémantique équivalente. @TimeLimiter exige Future, @Timeout non.

@CircuitBreaker(name = "…​") (failureRateThreshold, slidingWindowSize, etc.)

@CircuitBreaker(failureRatio, requestVolumeThreshold, delay, successThreshold)

Spec MP : fenêtre request volume (compte par appels), pas de fenêtre temporelle.

@Bulkhead(name = "…​") (maxConcurrentCalls, maxWaitDuration)

@Bulkhead(value, waitingTaskQueue)

maxWaitDuration n’a pas d’équivalent — la spec MP BulkheadException est immédiate ou file bornée (en async).

@RateLimiter

Non implémenté (hors spec MP FT 4.1)

Garder Resilience4j en parallèle pour ce besoin, ou implémenter côté gateway.

@Fallback (Spring Cloud) ou Try.recover (Vavr)

@Fallback(fallbackMethod = "…​") ou @Fallback(MyHandler.class)

Aucun pont fonctionnel — utiliser des records purs.

Différence de modèle : virtual threads vs pool plateforme

C’est le point d’attention principal lors d’une migration depuis SmallRye ou Resilience4j.

Aspect SmallRye / Resilience4j Heisenberg

Exécution @Asynchronous / @TimeLimiter

ExecutorService plateforme borné (io.smallrye.faulttolerance.globalThreadPoolSize=100)

Thread.ofVirtual().start(…​) — un virtual thread par invocation

Coût mémoire par tentative

~512 Ko (stack OS)

~5 Ko (stack frame virtuelle)

Saturation

File LinkedBlockingQueue ; au-delà, rejet

Le @Bulkhead borne explicitement — sans bulkhead, pas de borne

Pinning

Pas pertinent

synchronized ou ThreadLocal non-zéro = pinning → coût mémoire explose. Heisenberg interdit les deux.

Si du code applicatif utilise synchronized ou ThreadLocal à l’intérieur d’une méthode annotée @Asynchronous, le virtual thread est pinned sur son thread porteur — c’est légal mais inefficace. Profiter de la migration pour basculer vers ReentrantLock et ScopedValue. Voir Vauban.

Deltas de configuration

SmallRye Heisenberg

io.smallrye.faulttolerance.globalThreadPoolSize=100

Pas d’équivalent — virtual threads à la demande.

io.smallrye.faulttolerance.mainThreadPoolQueueSize=…​

Pas d’équivalent.

MP_Fault_Tolerance_NonFallback_Enabled (spec MP)

Même clé sous le préfixe fault-tolerance/MP_Fault_Tolerance_NonFallback_Enabled.

MP_Fault_Tolerance_Metrics_Enabled (spec MP)

Même clé sous le préfixe fault-tolerance/MP_Fault_Tolerance_Metrics_Enabled.

Checklist de portage

  1. Déclarer heisenberg-cdi-vauban au lieu de smallrye-fault-tolerance ou resilience4j-*.

  2. Vérifier que les annotations applicatives sont issues de org.eclipse.microprofile.faulttolerance.. Remplacer les annotations propriétaires (io.smallrye.faulttolerance.api., io.github.resilience4j.*).

  3. Renommer les clés MP Config si nécessaire — fault-tolerance/<FQCN>/<méthode>/<Annotation>/<param> est la forme canonique.

  4. Supprimer les propriétés liées aux pools de threads plateforme — sans effet.

  5. Auditer les méthodes @Asynchronous pour synchronized / ThreadLocal — basculer vers ReentrantLock / ScopedValue quand possible.

  6. Lancer ./mvnw test puis ./run-official-tck-mp-fault-tolerance-4.1.sh (voir TCK).

  7. Comparer un appel typique avant/après — taux de retry, ouverture du circuit, latence p99.

Pour aller plus loin