Skip to content
resilience4cata
ENES

Registro de cambios

Cambios notables en cada versión de resilience4cata, de la más reciente a la más antigua.

El formato sigue Keep a Changelog, y el proyecto usa Versionado Semántico. Hasta 1.0.0, una versión menor puede cambiar la API.

[0.10.0] - 2026-10-27 :id=0.10.0

Agregado

  • @PolicyOrder: cambia el orden de las políticas de un método. Nombra capas (Layer.RETRY, TIMEOUT, CIRCUIT_BREAKER, …) que se mueven juntas, en el orden indicado, al lugar de la más exterior de ellas; las demás conservan su orden por defecto. @PolicyOrder({TIMEOUT, RETRY}) pone un único timeout alrededor de todo el bucle del retry, {CIRCUIT_BREAKER, RETRY} registra un resultado por operación en el breaker, {BULKHEAD, RETRY} mantiene el slot entre reintentos. En un método, un tipo o un bundle; se decide al compilar, y el fallback sigue siendo la capa más exterior. Nombrar una capa dos veces es R4C-ORD-001. Sin ella, o cuando no cambia nada, el código generado no cambia. Un Pipeline mantiene el orden por defecto. Consulta Cambiar el orden.

[0.9.0] - 2026-10-27 :id=0.9.0

Cambiado

  • Los tipos de retorno reactivos son un error de compilación. Un método con anotaciones de resiliencia que devuelve un tipo reactivo perezoso (Flow.Publisher o el Publisher de Reactive Streams, es decir Mono y Flux de Reactor; Uni y Multi de Mutiny; los tipos de RxJava) compilaba como un método síncrono, así que las políticas solo protegían la construcción del pipeline y un fallo nunca se reintentaba ni tenía timeout. Ahora es R4C-ASY-002; devuelve un CompletionStage en su lugar (uni.subscribeAsCompletionStage(), mono.toFuture()).
  • Quarkus: dos casos que perdían la protección sin aviso son errores de build. Un recurso de Quarkus REST que es una clase anotada (R4C-QK-007: Quarkus REST crea las clases de recursos por su cuenta, así que la subclase generada nunca atendía los endpoints), y un bean protegido con @LookupIfProperty/@LookupUnlessProperty (R4C-QK-006: la condición se ignoraba).

Corregido

  • Quarkus: condiciones de build en clases anotadas. @IfBuildProfile, @UnlessBuildProfile, @IfBuildProperty y @UnlessBuildProperty en una clase anotada se ignoraban, así que tanto la implementación de producción como la de pruebas eran beans (una inyección ambigua). Ahora la subclase generada de una clase deshabilitada también queda deshabilitada.
  • Anotaciones repetidas en las subclases generadas. Los overrides ya no repiten las anotaciones de Bean Validation (@Valid y las restricciones, que Bean Validation hereda del método sobrescrito): un valor de retorno con @Valid impedía arrancar una aplicación Quarkus (HV000131). En Quarkus, un @Named de clase sin valor ahora nombra al bean como la clase anotada, no …$Resilient, y un método @Scheduled ya no le da dos scopes al bean. En Micronaut, una clase marcada con @ResilientBean que además tenía anotaciones de políticas propias aplicaba dos veces las políticas de su interfaz (un @Retry(maxAttempts = 3) hacía 9 intentos); su subclase generada ya no se vuelve a envolver.
  • Micronaut: subclases y @MockBean de clases anotadas. La subclase generada llevaba @Replaces(C.class), que Micronaut aplica a todo bean asignable a C: una subclase anotada de C (con su propio @Named) desaparecía, y el @MockBean de C en un test no dejaba ningún bean. La subclase generada ya no lo lleva; la integración deja fuera solo la definición de bean de la propia C, y un @Inject C sin calificador sigue prefiriendo C$Resilient sobre los beans de subclases, como Micronaut prefiere el tipo exacto. El stub de un @MockBean de una clase anotada ya no se reporta como desprotegido (R4C-MN-004). Una subclase generada compilada sin -Ar4c.inject=micronaut que Micronaut crea igualmente (su constructor repite @Inject) se reporta con R4C-MN-004, porque no tiene el ámbito de la clase.
  • Spring Boot. El endpoint de actuator no arrancaba en aplicaciones web (Failed to extract parameter names), porque el módulo se compilaba sin -parameters. Una clase anotada con un constructor sin parámetros y otros, ninguno @Autowired, no arrancaba (No default constructor found): ahora Spring usa el espejo generado del constructor sin parámetros, como usa ese constructor en la clase. Los beans de una interfaz anotada genérica (NameLookup implements Lookup<String>) vuelven a poder inyectarse por su argumento de tipo (@Autowired Lookup<String>) una vez envueltos.
  • Dobles de prueba de beans protegidos. Spring: un @MockitoSpyBean de una interfaz anotada se envolvía después de que Spring lo creara, así que verify(spy) fallaba con NotAMockException; los spies ya no se envuelven, igual que los mocks de @MockitoBean. Quarkus: @InjectMock sobre una interfaz anotada fallaba porque su envoltorio era @Singleton; ahora el envoltorio de un bean @ApplicationScoped también es @ApplicationScoped (detrás de un proxy de cliente). Micronaut: un @MockBean de una interfaz @ResilientBean (un @Client) hacía fallar el arranque con R4C-MN-003; ahora se inyecta tal cual.
  • Configuración y mensajes. down-when-open (Spring, Micronaut y Quarkus) partía en su coma un id derivado como com.acme.Charges#charge(java.lang.String,int), así que no correspondía a ningún breaker; ahora las partes se vuelven a unir. Micronaut con singletons anticipados hacía fallar el arranque con R4C-CFG-013 por una clave de instancia de un bean protegido @Prototype, @Refreshable o de ámbito de petición; el valor por defecto sigue siendo warn cuando existen esos beans. Un valor inválido de resilience4cata.micronaut.verify-instances ahora falla con el código nuevo R4C-MN-006. El informe de arranque de Spring para R4C-CFG-013 indica resilience4cata.spring.verify-instances=warn para beans creados después del arranque (@Lazy, prototypes, pipelines). El mensaje de R4C-CFG-013 cierra la frase antes de las instancias conocidas y dice <interface or class>. Los enlaces de los mensajes y de los metadatos usan las URLs actuales del sitio.
  • Quarkus: interfaces anotadas genéricas. @Inject Store<String> de un Store<T> anotado recibía el bean sin proteger, porque los tipos del bean wrapper eran raw; ahora conservan los argumentos de tipo del bean.
  • Las clases anotadas conservan los nombres de sus parámetros. Los overrides de una subclase generada llamaban a sus parámetros p0, p1, …, así que fallaban en las clases anotadas las funciones que leen los nombres en el método invocado: @Cacheable(key = "#sku") y @EventListener(condition = "#event.urgent") de Spring, @PathVariable/@QueryValue sin nombre explícito en controladores de Spring MVC y Micronaut, y Bean Validation, que reportaba cada violación dos veces (p0 y el nombre real). Ahora conservan los nombres declarados; solo se renombra un parámetro llamado java, dev, como la primera parte del paquete de un fallback estático, o que empiece por r4c$. Un método heredado de un archivo de clase solo tiene sus nombres si esa clase se compiló con -parameters, como hacen los builds de Spring Boot, Micronaut y Quarkus. Los wrappers de interfaces no cambian.

[0.8.0] - 2026-10-27 :id=0.8.0

Agregado

  • resilience4cata-all: una sola dependencia para empezar. Depende de core, annotations y config y no tiene código propio, así que un proyecto necesita dos líneas: resilience4cata-all y el procesador en el path de procesadores de anotaciones (ninguna dependencia puede llevar un procesador ahí). También es un módulo JPMS, dev.resilience4cata.all, que lee los tres de forma transitiva. Los módulos por separado y el BOM funcionan como antes; úsalos para dejar las anotaciones solo en compilación. Incluido en el BOM. Ver La forma corta.

[0.7.0] - 2026-10-27 :id=0.7.0

Añadido

  • locked = true en @Retry, @Hedge, @CircuitBreaker, @RateLimiter, @AdaptiveLimit, @Timeout y @Bulkhead (y Pipeline.Builder.locked()): los valores que fija la anotación ganan sobre los overrides por instancia (instances.<id>.*, retryInstance(...) y similares); los atributos que deja sin fijar se pueden seguir sobrescribiendo. Un override ignorado se escribe en el log al crear la instancia, lo lista el nuevo registry.lockedOverrides() (LockedOverride), y config.verifyInstances(registry) lo reporta clave por clave con el nuevo código R4C-CFG-014, así que las integraciones de Spring, Micronaut y Quarkus lo reportan al arrancar. El código generado solo cambia en las anotaciones con locked = true. Ver Bloquear una instancia.
  • El build compara la API pública de cada módulo publicado con la última versión (./gradlew apiCompatibility, parte de check), con japicmp.

[0.6.0] - 2026-10-26 :id=0.6.0

Agregado

  • @AdaptiveLimit: límites de concurrencia adaptativos, sync y async, en interfaces, clases anotadas y Pipeline.adaptiveLimit(...). Como un bulkhead, limita las llamadas que corren al mismo tiempo, pero mueve el límite por sí mismo, entre minLimit y maxLimit, según la latencia de las llamadas: GRADIENT2 (el valor por defecto) compara la latencia promedio de cada ventana con un promedio de largo plazo, AIMD crece de a uno y retrocede por backoffRatio después de un descarte; los dos siguen concurrency-limits de Netflix. Un fallo de dropOn (por defecto una CallTimeoutException, una BulkheadFullException o una TimeoutException de las políticas de adentro) cuenta como descarte; los demás fallos no son muestras. Un límite lleno rechaza enseguida con la nueva LimitExceededException. El límite se recalcula una vez por ventana (window, al menos minWindowSamples llamadas) en el hilo que termina una llamada después de que la ventana terminó: sin hilo de fondo, sin asignaciones en el camino de éxito. Está entre el rate limiter y el timeout: Fallback → Retry | Hedge → CircuitBreaker → RateLimiter → AdaptiveLimit → Timeout → Bulkhead. API nueva: AdaptiveLimitConfig (con el ajuste de los algoritmos: smoothing, rttTolerance, longWindow, queueSize, backoffRatio), AdaptiveLimitHandle desde registry.adaptiveLimit(id), AdaptiveLimitSnapshot en registry.snapshot().adaptiveLimits(), AdaptiveLimitEvent.Rejected y LimitChanged, adaptiveLimitDefaults, adaptiveLimit, adaptiveLimitOverlay y adaptiveLimitInstance en el builder del registry, claves de configuración externa *.adaptiveLimit.*, y las métricas r4c.adaptivelimit.limit, r4c.adaptivelimit.concurrent.calls, r4c.adaptivelimit.limit.changes y r4c.calls.rejected{kind=adaptivelimit} en los dos módulos de métricas. Nuevo error del procesador R4C-ATR-009. Consulta Adaptive limit.
  • @Hedge deja de lanzar intentos cuando un adaptive limit rechaza uno, como con un breaker abierto o un bulkhead lleno.
  • Tarjeta en la Dev UI de Quarkus: en modo dev, /q/dev-ui muestra cada instancia de política del registry (breakers, bulkheads, rate limiters, adaptive limits, retries y hedges con sus presupuestos, timeouts), actualizada cada dos segundos, y puede forzar la apertura de un circuit breaker, deshabilitarlo o reiniciarlo. Solo en modo dev: la tarjeta y su servicio JsonRPC no existen en builds de tests ni de producción. Ver Quarkus, Dev UI.

Corregido

  • El endpoint resilience4cata del actuator de Spring Boot ahora incluye los hedges cuando reporta una sola instancia (/actuator/resilience4cata/{instance}).

[0.5.0] - 2026-10-26 :id=0.5.0

Agregado

  • resilience4cata-micronaut: integración con Micronaut 5 (Micronaut 5.2 o posterior, Java 25, sin AOP). Los beans de interfaces anotadas marcados con @ResilientBean(I.class) se reemplazan por su envoltorio generado cuando Micronaut los crea (expuestos solo como I; la AOP de Micronaut sobre el bean se ejecuta dentro de las políticas); las clases anotadas las crea Micronaut como su subclase generada con la nueva opción del procesador de abajo, o se devuelven desde un @Factory; un ResilienceConfig y un ResilienceRegistry @Context configurados desde la configuración resilience4cata.* de la aplicación con la precedencia de Micronaut (listas YAML, ids de instancia derivados entre comillas, el archivo de cada clave incorrecta), con personalización mediante BeanCreatedEventListener<ResilienceRegistry.Builder> y beans EventListener/Ticker; métricas de Micrometer cuando están resilience4cata-micrometer y un MeterRegistry; un indicador de salud resilience4cata (UP con breakers abiertos salvo los listados); un endpoint de gestión resilience4cata de solo lectura y sensible. Nueva área de errores R4C-MN-001…005: un @ResilientBean mal usado detiene el arranque; R4C-MN-004 reporta una clase anotada creada sin su subclase generada; R4C-MN-005 detiene un método @Transactional reintentado o con hedge de una clase anotada (todos los intentos compartirían una transacción), salvo con resilience4cata.micronaut.allow-retry-inside-transaction=true. El módulo se compila con --release 25, la única excepción a la base Java 21. Consulta Micronaut.
  • Opción del procesador -Ar4c.inject=micronaut: la subclase generada de una clase anotada repite las anotaciones a nivel de tipo de la clase (ámbito, calificadores, @Requires…) y añade @Replaces(C.class), para que Micronaut la cree en lugar de la clase. Sin la opción el código generado no cambia. Un valor distinto de micronaut, o la opción sin Micronaut en el class path de compilación, es el nuevo error R4C-APT-006.
  • resilience4cata-quarkus: extensión de Quarkus (Quarkus 3.40 o posterior; ejecución resilience4cata-quarkus y despliegue resilience4cata-quarkus-deployment, ambos en el BOM; sin interceptores). Al construir, una clase anotada se veta y su subclase generada pasa a ser el bean con el ámbito, los qualifiers y los estereotipos de la clase (las llamadas sobre this se interceptan); un bean de una interfaz anotada se inyecta a través de un bean envoltorio sintético (Resilient.of, una alternativa habilitada con los qualifiers del bean; @Inject de la clase sigue dando el bean); una interfaz de cliente REST (@RegisterRestClient) con envoltorio recibe un bean @Default alrededor del @RestClient. Un bean ResilienceRegistry (@DefaultBean) se configura desde las claves resilience4cata.* de la aplicación a través de SmallRye Config (perfiles de Quarkus, expresiones, variables R4C_, fichero y línea de cada clave incorrecta), y se crea al arrancar para que los errores de configuración lo detengan; se aplican los beans ResilienceRegistryCustomizer, EventListener y Ticker; las claves resilience4cata.instances.<id> se verifican al arrancar (quarkus.resilience4cata.verify-instances, warn por defecto). Métricas de Micrometer con quarkus-micrometer y resilience4cata-micrometer (si no, OpenTelemetry), una comprobación de preparación resilience4cata (UP con breakers abiertos salvo los listados en quarkus.resilience4cata.health.down-when-open). Nueva área de errores R4C-QK-001…005, reportados mientras Quarkus construye la aplicación: R4C-QK-001 (anotaciones de MicroProfile/SmallRye Fault Tolerance y de resilience4cata en un mismo método), R4C-QK-002 (un método @Transactional reintentado o con hedge de una clase anotada; quarkus.resilience4cata.allow-retry-inside-transaction), R4C-QK-003 (varias interfaces anotadas no relacionadas), R4C-QK-004 (productores en una clase anotada), R4C-QK-005 (una clase anotada de ámbito normal sin constructor sin parámetros). Consulta Quarkus, incluida la comparación con SmallRye Fault Tolerance. Se construyó a mano una imagen nativa de la aplicación de la guía (GraalVM para JDK 25.0.4, Quarkus 3.40.1, macOS arm64), que sirvió la clase anotada, el envoltorio de la interfaz, el del cliente REST, la comprobación de preparación y las métricas sin configuración nativa adicional; las construcciones nativas no forman parte de la CI. En una imagen nativa, los errores de configuración nombran la fuente que informa SmallRye allí (Runtime Values para los valores fijados al construir) en lugar del fichero y la línea.

Corregido

  • Con un ticker manual (ManualClock), un retry asincrónico cuyo siguiente delay es cero (delay = 0, o un jitter FULL que sortea cero) lanzaba el siguiente intento en un hilo virtual nuevo en lugar de en el reloj, así que los tests competían con él. Ahora todo delay que una política programa en un ticker pasa por Ticker.schedule, también el cero, y se ejecuta en el hilo que avanza el reloj la próxima vez (también con advance(Duration.ZERO)), como ya hacían los hedges tras una falla. Sin ticker no cambia nada. Ticker.schedule puede recibir ahora un delay de cero.
  • R4C-RT-003 en una imagen nativa de GraalVM ahora también señala los metadatos de alcanzabilidad que escribe el procesador (META-INF/native-image/dev.resilience4cata.generated/<clase>/reachability-metadata.json), que deben estar en el class path del build de la imagen. En la JVM el mensaje no cambia.

[0.4.0] - 2026-10-25 :id=0.4.0

Agregado

  • @Hedge: peticiones de cobertura (hedged requests) para métodos asincrónicos y Pipeline.executeAsync. La misma llamada se vuelve a lanzar cuando lleva delay sin resultado (1 s por defecto), o enseguida cuando falla con una falla de hedgeOn; gana el primer éxito y los demás intentos se cancelan a través de cada política interna (el breaker no registra nada, el bulkhead libera el slot). maxAttempts (de 2 a 10, 2 por defecto), abortOn, y un presupuesto de hedge activo por defecto (budgetPercent 10, budgetMinHedgesPerSecond 1, budgetWindow 10 s). Ocupa el lugar de Retry en el orden, y no se puede combinar con @Retry. Nuevos HedgeConfig, HedgeCancelledException, HedgeEvent (Hedged, Won, Skipped, Failed), HedgeSnapshot en ResilienceSnapshot.hedges(), ResilienceRegistry.Builder.hedgeDefaults/hedge/hedgeOverlay/hedgeInstance, Pipeline.Builder.hedge(...), y las claves de config defaults.hedge.* (y configs.…, instances.…, variables de entorno). Solo para llamadas idempotentes. Nuevos errores R4C-HDG-001 (método sincrónico), R4C-HDG-002 (con @Retry), R4C-ATR-008 (maxAttempts), R4C-RT-018 (Pipeline.execute con un hedge) y R4C-RT-019 (retry y hedge en un mismo pipeline); R4C-ATR-007 cubre ahora también el presupuesto de hedge. Ver Hedge.
  • resilience4cata-spring: integración con Spring Boot 4 (Spring Boot 4.1 o posterior, un solo módulo, sin AOP). Las clases anotadas se instancian como su subclase generada en lugar de la clase (tipo del bean, nombre, qualifiers e inyección por constructor sin cambios; las llamadas sobre this se interceptan); los beans de interfaces anotadas se envuelven con Resilient.of después de todos los demás post-procesadores; un bean ResilienceRegistry (@ConditionalOnMissingBean) se configura desde las propiedades resilience4cata.* de la aplicación con la precedencia de Spring, con ids de instancia entre […] y listas YAML, validado al arrancar con el fichero y la línea de cada clave incorrecta; se aplican los beans ResilienceRegistryCustomizer, EventListener y Ticker; métricas de Micrometer cuando está resilience4cata-micrometer; un indicador de salud resilience4cata (UP con breakers abiertos salvo los listados); un endpoint de actuator resilience4cata de solo lectura; metadatos para el IDE; análisis de fallos; funciona con Spring AOT. Nueva área de errores R4C-SPR-001…004; R4C-SPR-004 detiene el arranque cuando un método reintentado o con hedge de una clase anotada también es @Transactional (todos los intentos compartirían una transacción), salvo con resilience4cata.spring.allow-retry-inside-transaction=true. Consulta Spring Boot.
  • resilience4cata-config: ResilienceConfig.Builder.precedenceByOrder() (las fuentes se ordenan por el orden en que se agregan, para frameworks con su propia precedencia) y properties(description, map, origins) (dónde se fijó cada clave, para los mensajes).
  • Imagen nativa de GraalVM sin configuración. El procesador escribe META-INF/native-image/dev.resilience4cata.generated/<clase generada>/reachability-metadata.json para cada clase generada (un archivo por clase, así que el procesador sigue siendo isolating), que registra sus constructores públicos para Resilient.of y Resilient.create, condicionado a la interfaz anotada (o a Resilient para las clases anotadas). core trae un native-image.properties que inicializa en tiempo de ejecución los holders de sus hilos de fondo. Requiere GraalVM for JDK 23 o posterior; ver Imagen nativa.
  • Clases anotadas: los constructores generados conservan los nombres de parámetro de los constructores de la clase y copian las anotaciones del constructor y de los parámetros (retención CLASS o RUNTIME), así que los contenedores de inyección de dependencias ven @Inject/@Autowired, @Qualifier, @Named, @Value… también en C$Resilient. @ConstructorProperties recibe primero el nombre del registry. Los overrides generados repiten las anotaciones de los métodos que sobrescriben y de sus parámetros (salvo las anotaciones propias de resilience4cata y los bundles), así que las anotaciones que se leen solo en el método invocado, como el @Transactional de Jakarta en Spring o los interceptor bindings, ya no se pierden. Ver Constructores e inyección de dependencias.

Cambiado

  • ResilienceEvent admite un nuevo subtipo sellado, HedgeEvent: un switch sobre ResilienceEvent sin rama default deja de compilar hasta que maneje HedgeEvent (o añada default). ResilienceSnapshot tiene un componente nuevo, hedges(); su constructor de cinco listas se conserva. A partir de ahora la jerarquía crece en versiones menores: mantén una rama default, o escucha tipos de record específicos (Observabilidad).
  • La subclase generada para una clase anotada ya no es final (public class C$Resilient extends C), así que los frameworks pueden crear proxies de ella (CGLIB para @Transactional, proxies con scope). Los envoltorios de interfaces siguen siendo final.
  • Los constructores generados de las clases anotadas nombran sus parámetros como el constructor de la clase (registry, warehouse en vez de registry, p0); un parámetro llamado registry hace que el del registry sea r4c$registry, y los parámetros llamados java o dev pasan a r4c$p<índice>. Las llamadas no cambian.

Corregido

  • Cancelar una llamada async ahora cancela el trabajo que hay dentro. Cancelar el future que devuelve un método async generado o Pipeline.executeAsync (o completarlo con una CancellationException) se detenía en la política más externa: el stage interno seguía corriendo, un slot del bulkhead quedaba ocupado hasta que ese stage terminara, un retry podía empezar otro intento, un fallback podía correr, y el circuit breaker podía registrar la cancelación como una falla. Ahora la cancelación baja por todas las capas hasta el stage del método: el retry cancela su backoff y no empieza más intentos, el timeout cancela su plazo, el bulkhead libera su slot en cuanto termina el stage (de inmediato para un CompletableFuture) y una llamada en cola sale de la cola, el breaker no registra nada (un permiso de half-open se devuelve), el fallback no corre, y una llamada que todavía espera un permiso del rate limiter lo devuelve y nunca empieza. Una cancelación no publica eventos. Un timeout que vence cancela las capas internas de la misma forma, así que un bulkhead dentro de un timeout libera su slot cuando la llamada vence. Cada política crea por llamada la misma cantidad de objetos que antes. Ver Métodos async.

[0.3.0] - 2026-10-25 :id=0.3.0

Agregado

  • resilience4cata-opentelemetry: métricas de OpenTelemetry. OpenTelemetryMetrics.create(openTelemetry), register(builder) y bindTo(registry) reportan rechazos, retries, abandonos, transiciones del breaker, timeouts y fallbacks como contadores alimentados por eventos, y el estado de breakers, bulkheads y rate limiters como gauges asíncronos leídos del snapshot en cada recolección, así que se incluyen las instancias creadas después. Los mismos nombres de métricas que el módulo de Micrometer. Solo métricas, todavía sin tracing. Consulta OpenTelemetry.
  • resilience4cata-junit: una extensión de JUnit Jupiter. @ResilienceTest inyecta en cada prueba (y en sus métodos @BeforeEach/@AfterEach) un ManualClock nuevo y un ResilienceRegistry que lo sigue, con seed, startNanos y un failOnPendingTasks opcional; los métodos @ConfigureRegistry personalizan el builder. Necesita JUnit 5.10 o posterior, JUnit 6 incluido. Consulta Pruebas.
  • Una llamada async con @Timeout que termina a tiempo ahora quita su plazo de un ManualClock de inmediato, así que pendingTasks() ya no la cuenta hasta que pasa el plazo.
  • resilience4cata-micrometer: métricas de Micrometer en tres líneas (MicrometerMetrics.create(meterRegistry), register(builder), bindTo(registry)): counters a partir de eventos (rechazos, transiciones del breaker, reintentos, abandonos, timeouts, fallbacks) y gauges para cada circuit breaker, bulkhead y rate limiter, incluidas las instancias creadas después de conectar. Reemplaza la receta escrita a mano de la guía de observabilidad.
  • ResilienceRegistry.onInstanceCreated(Runnable): ejecuta una acción una vez por cada instancia de política nueva (al envolver un servicio o construir un pipeline, nunca durante una llamada), para que los puentes de métricas registren meters para instancias creadas después (Observabilidad).
  • Presupuesto de reintentos para @Retry, RetryConfig y Pipeline: budgetPercent, budgetMinRetriesPerSecond y budgetWindow limitan los reintentos de una instancia de retry, compartidos por todos sus llamadores, a una reserva por segundo más un porcentaje de sus llamadas en la ventana, para que una caída no multiplique la carga sobre la dependencia que falla. budgetPercent lo activa, con una reserva por defecto de 10 reintentos por segundo. Apagado por defecto; sin él, el código generado y el camino de éxito no cambian. Un reintento rechazado publica RetryEvent.GaveUp con la nueva razón BUDGET_EXHAUSTED; RetrySnapshot.budget() muestra la ventana. Claves defaults.retry.budget… (y configs.…, instances.…, variables de entorno); nuevo error del procesador R4C-ATR-007. Presupuesto de reintentos
  • Códigos de error para los módulos de pruebas: R4C-TK-001…003 (ManualClock, ResilienceTesting) y R4C-JU-001…003 (la extensión de JUnit), cada uno documentado en la referencia de errores con su corrección.
  • Los módulos de Micrometer y OpenTelemetry dan a cada métrica la misma descripción (el texto # HELP de Prometheus), comprobado por un test.

[0.2.0] - 2026-10-25 :id=0.2.0

Corrige tres errores de comportamiento de la 0.1.0; uno de los arreglos cambia un valor por defecto (ver Actualizar).

Actualizar desde 0.1.0

  • Mantén core y processor en la misma versión (importa el BOM). El código generado por el procesador 0.2.0 necesita el core 0.2.0; el código generado por la 0.1.0 sigue enlazando contra el core 0.2.0, que solo agrega API.
  • abortOn ya no contiene CallNotPermittedException por defecto, porque ahora un circuit breaker abierto detiene los reintentos por sí solo. Quita CallNotPermittedException de tus propias listas abortOn si lo agregaste solo para eso; dejarlo no causa problemas.
  • Una llamada rechazada por un breaker abierto ya no se puede reintentar con ninguna configuración, ni siquiera con un classifier.

Cambiado

  • Un circuit breaker abierto siempre detiene los reintentos. Una llamada rechazada con CallNotPermittedException nunca se reintenta, digan lo que digan abortOn, retryOn o un classifier, en el código generado (sincrónico y async) y en Pipeline. En 0.1.0 esto venía del abortOn por defecto, así que definir tu propio abortOn lo quitaba sin aviso y el retry seguía llamando a un breaker abierto. RetryConfig.DEFAULTS.abortOn() (y defaults.retry.abortOn) ahora está vacío; Retry.

Corregido

  • FallbackEvent.Applied ya no se publica para una falla que ningún método hermano de fallback recibe (por ejemplo un Error cuando el único hermano recibe Exception): esa falla se relanza, y el evento ahora significa que un fallback realmente se ejecutó.
  • Los mensajes de error R4C-ATR-001 y R4C-CFG-002 indican la gramática de duraciones una vez en lugar de dos, y R4C-CFG-007 cierra la oración antes de "Known configs".
  • El descriptor de módulo del procesador declara también AnnotationClaimer, así que -Xlint:processing no avisa cuando el procesador corre desde el module path de procesadores junto a otros procesadores.
  • Un classifier que lanza dentro de @CircuitBreaker (o del breaker de un Pipeline) ahora termina la llamada enseguida, sync y async: el retry que lo rodea ya no clasifica la excepción del classifier como la falla del método ni la reintenta (lo que podía abrir el breaker y mostrar CallNotPermittedException en su lugar). El breaker registra una falla, el retry publica GaveUp(CLASSIFIER_FAILED) una vez, y el fallback o quien llama recibe la excepción del classifier. Ver Un classifier que lanza.

0.1.0 - 2026-10-25 :id=0.1.0

Primera versión. Artefactos en Maven Central bajo el groupId dev.resilience4cata: resilience4cata-annotations, -core, -processor, -config, -testkit y -bom. Requiere Java 21 o posterior; probado en Java 21 y 25.

Agregado

  • Seis políticas como anotaciones, aplicadas en un orden fijo (Fallback → Retry → CircuitBreaker → RateLimiter → Timeout → Bulkhead → método): @Retry (backoff fijo o exponencial, jitter, presupuesto de tiempo), @CircuitBreaker (ventana por cantidad o por tiempo, llamadas lentas, sondeos en semiabierto), @RateLimiter (GCRA, espera opcional), @Timeout (en el thread de quien llama, basado en interrupciones), @Bulkhead (semáforo, espera opcional) y @Fallback (método hermano o clase handler).
  • Procesador de anotaciones que genera una clase Java pura por cada tipo anotado: <Interface>$Resilient, que envuelve una implementación (Resilient.of), o <Class>$Resilient, que extiende una clase anotada (Resilient.create). Las anotaciones y sus valores se verifican al compilar; procesamiento incremental isolating en Gradle; funciona con -Xlint:all -Werror.
  • Métodos async que devuelven CompletionStage<T> o CompletableFuture<T> (también ? extends T), con formas no bloqueantes de cada política (Métodos async).
  • Anotaciones compuestas con @ResiliencePolicy, y anotaciones a nivel de tipo.
  • Classifiers que deciden a partir de un resultado o una excepción si una llamada falló, y @Retry(onExhaustedResult = THROW).
  • Pipeline, una API programática con las mismas políticas y el mismo orden para código que no puedes anotar.
  • ResilienceRegistry: valores predeterminados, configs con nombre, sobrescrituras por instancia, instancias compartidas por name, handles para circuit breakers (forceOpen, disable, reset), rate limiters y bulkheads.
  • Eventos y snapshots: listeners registrados en el builder del registry, snapshots de solo lectura de cada instancia.
  • resilience4cata-config: configuración externa desde archivos properties, recursos del classpath, variables de entorno y propiedades del sistema, con perfiles, extends, sobrescrituras por instancia, validación estricta y verifyInstances (claves).
  • resilience4cata-testkit: ManualClock y ResilienceTesting para pruebas deterministas del comportamiento basado en tiempo.
  • resilience4cata-bom para alinear las versiones de todos los módulos.
  • Códigos de error (R4C-…) en cada error de compilación y de ejecución, cada uno documentado con su solución.
  • Control de asignación de memoria en CI: cada ruta síncrona de éxito y de rechazo asigna 0 bytes (Rendimiento).

Limitaciones conocidas

  • No hay módulos de integración con Spring, CDI, Micrometer ni OpenTelemetry; no hay configuración en YAML.
  • El orden de las políticas no se puede cambiar, y la configuración no se puede cambiar después de construir el registry.
  • En el estilo de interfaz, las llamadas que una implementación se hace a sí misma no se interceptan.
  • Solo se admiten CompletionStage<T> y CompletableFuture<T> como tipos async (no ? super T, no tipos reactivos).