name: java-resilience-pattern
description: Use this skill when statically reviewing resilience4j + Spring composition on a Java code path: decorator/aspect order between @Retry, @CircuitBreaker, @RateLimiter, @TimeLimiter, and @Bulkhead; retry safety on non-idempotent write paths (idempotency/dedup keys); TimeLimiter and total retry timeout-budget coherence; Bulkhead isolation strategy (semaphore vs thread pool); RateLimiter blocking/timeout behavior; fallback correctness (must not swallow the failure signal); bounded queues/backpressure; and retry composed with @Transactional (retry must wrap, not sit inside, the transaction). Trigger when a user provides resilience4j annotations/configuration or Decorators functional-chaining code and asks whether their resilience composition is correct, why a circuit breaker trips unexpectedly, why a timeout doesn't seem to apply, or whether a retry is safe on a given path. Reads source and sanitized configuration only; it never builds, runs, invokes a JDK, or opens a live connection.
allowed-tools: Read Grep Glob
metadata:
author: "github: Raishin"
version: "0.1.0"
updated: "2026-07-17"
category: resilience
lifecycle: experimental
java-resilience-pattern
Purpose
This skill statically reviews resilience4j decorator composition on Spring-based Java code paths for correctness, not just presence. Fault-tolerance annotations are only sound in composition — the same @Retry and @CircuitBreaker pairing can be correct or dangerous depending on aspect order, whether the protected operation is idempotent, whether retry sits inside or outside a transaction boundary, whether TimeLimiter is actually bounding an asynchronous call, and whether Bulkhead isolates the right thread pool. The review catches misordered aspects that inflate the circuit breaker's observed failure rate, non-idempotent writes retried without a dedup key, retry composed unsafely with @Transactional, timeout budgets that don't compose, semaphore bulkheads mistaken for thread isolation, and fallbacks that silently swallow degraded-mode signal.
Trigger conditions
- A user provides resilience4j annotations (@Retry/@CircuitBreaker/@RateLimiter/@TimeLimiter/@Bulkhead) or Decorators functional-chaining code and asks whether the composition is correct.
- A user asks why a circuit breaker trips faster or slower than expected, why retries seem to duplicate a write, or why a configured timeout doesn't seem to take effect.
- A user wants a static review of a resilience-pattern change (new @Retry, new circuit breaker, changed bulkhead type) before merge or release.
When not to use
- The task is JPA/Hibernate fetch-strategy, N+1, or HikariCP connection-pool sizing — route to java-jpa-hibernate-performance-agent.
- The task is untrusted-deserialization or parser RCE surface — route to java-deserialization-and-parser-security-agent.
- The task is @Transactional propagation/isolation/boundary semantics themselves, with no resilience4j decorator involved — route to the Java transaction and consistency agent.
- The task is general JVM thread-pool/executor sizing unrelated to a resilience4j ThreadPoolBulkhead — route to the Java concurrency and thread-pool agent.
Lean operating rules
- Load and follow this skill first; do not drift into generic Spring Boot review, general microservice architecture advice, or non-resilience4j fault-tolerance libraries (Hystrix, Sentinel, service-mesh-level retries) unless asked to compare a hand-rolled mechanism against the same idempotency/order rules.
- CRITICAL — treat @Retry (or a manual retry loop) on a non-idempotent write path (INSERT without a unique constraint, a payment/charge call, a message publish, a non-idempotent POST) with no idempotency/dedup key as a blocking defect.
- HIGH — treat an unexamined aspect order as a finding: resilience4j's Spring default is Retry(CircuitBreaker(RateLimiter(TimeLimiter(Bulkhead(f))))), so every retry attempt is independently evaluated by the circuit breaker, inflating the observed failure rate; require explicit retryAspectOrder/circuitBreakerAspectOrder properties (or literal functional-chaining nesting) as evidence — never infer order from the sequence annotations are stacked in source.
- HIGH — treat @Retry sitting inside a @Transactional boundary as a defect: retry must wrap the transaction so each attempt opens its own transaction, not retry inside one already-open transaction; also flag same-class self-invocation between a @Retry method and a @Transactional method, which silently bypasses the Spring proxy for the inner annotation.
- HIGH — check TimeLimiter.timeoutDuration against the total retry budget (maxAttempts times per-attempt wait/backoff) and against CircuitBreaker.slowCallDurationThreshold for coherence; flag TimeLimiter applied to a call that is not backed by a Future/CompletionStage as a no-op.
- HIGH — flag SemaphoreBulkhead used where the intent is isolating the caller's own thread pool from a slow dependency; a semaphore bulkhead still runs the call on the caller's thread, so recommend ThreadPoolBulkhead for genuine thread isolation.
- MEDIUM — flag a fallback/@Recover that swallows the triggering failure and returns a default/success-shaped result with no degraded-mode signal (log, metric, response flag), and flag a fallback whose exception signature is broader than the resilience exceptions it should catch.
- MEDIUM — flag an unbounded or very large ThreadPoolBulkhead queueCapacity as a backpressure defect; it turns a fast, explicit rejection into slow, silent memory growth.
- MEDIUM — flag a request-path RateLimiter.timeoutDuration long enough to matter (seconds, not tens of milliseconds), and flag RequestNotPermitted handled by silent retry-without-backoff or a bare catch-and-continue.
- MEDIUM — a CircuitBreaker threshold claim (failure rate, sliding window) needs minimumNumberOfCalls, failureRateThreshold, slowCallDurationThreshold, and waitDurationInOpenState all visible; without all four, label the finding inference, not confirmed.
- LOW — flag fixed-interval retry with no exponential backoff or jitter against a shared/contended dependency as a retry-storm risk.
- Base every conclusion on the annotation/config/call-site evidence actually provided; an order, idempotency, or budget claim without that evidence is inference (partial source) or assumption (source absent) — say so.
References
Load these only when needed:
Response minimum
Return, at minimum:
- A verdict (pass / pass-with-conditions / block) and an evidence level (which annotations, functional-chaining code, and resilience4j.* configuration were provided).
- Aspect-order and composition findings (misordered Retry/CircuitBreaker/RateLimiter/TimeLimiter/Bulkhead, or an unexamined default).
- Retry-safety findings (idempotency/dedup on write paths; retry-vs-@Transactional composition).
- Timeout-budget, Bulkhead-isolation, and RateLimiter findings.
- Fallback-correctness findings.
- A severity-labelled finding list (critical / high / medium / low), each with an evidence-basis label.
- Safe next actions and open questions.