agentsclimarketplace

Kora aop resilient

Skill kora-projects/kora-skills/plugins/kora-v1/skills/kora-aop-resilient

Kora resilience aspects — @CircuitBreaker, @Retry, @Timeout, @Fallback from the resilient-kora module (ResilientModule). Covers circuit breaker states (CLOSED/OPEN/HALF_OPEN), retry backoff, execution timeouts, fallback methods, custom CircuitBreakerPredicate/RetryPredicate/FallbackPredicate, the imperative *Manager API, and stacking aspects on one method. Use when adding fault tolerance to outbound HTTP/gRPC calls, database or external-service operations, debugging "circuit never opens", TimeoutExhaustedException, or fallback not firing, or wiring resilient.* config keys (failureRateThreshold, minimumRequiredCalls, attempts, delayStep, waitDurationInOpenState).From its SKILL.md

Install
npx -y skills add kora-projects/kora-skills --skill kora-aop-resilient

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
  • runs commandsInstructs the agent to run 2 commands, including `find build -name "*__AopProxy.java" | head -5` and 1 more.

SKILL.md

8.2 KB, ~1.9k tokens by cl100k_base, as published. Nobody here has run it

Kora AOP Resilient

Compile-time AOP annotations for fault tolerance: @Retry, @CircuitBreaker, @Timeout, @Fallback. They are generated into $<Class>__AopProxy classes at build time — no reflection. Provided by the resilient-kora module via ResilientModule.

Quick Navigation:


Quick Start

1. Add the dependency and annotation processor

dependencies {
    // All Kora artifacts inherit their version from the kora-parent BOM — never pin them individually.
    annotationProcessor "ru.tinkoff.kora:annotation-processors" // mandatory: generates the AOP proxies

    implementation "ru.tinkoff.kora:resilient-kora"
}

Kotlin uses ksp "ru.tinkoff.kora:symbol-processors" instead of annotationProcessor.

2. Enable ResilientModule

@KoraApp
public interface Application extends
        HoconConfigModule,
        LogbackModule,
        ResilientModule { } // enables the resilience aspects

3. Apply Resilience Annotations

Each annotation lives in its own package:

AnnotationImport
@CircuitBreakerru.tinkoff.kora.resilient.circuitbreaker.annotation.CircuitBreaker
@Retryru.tinkoff.kora.resilient.retry.annotation.Retry
@Timeoutru.tinkoff.kora.resilient.timeout.annotation.Timeout
@Fallbackru.tinkoff.kora.resilient.fallback.annotation.Fallback
@Component
public class PaymentService { // MUST be non-final (Java) / open (Kotlin) for aspects to apply

    @Fallback(value = "payment.process", method = "processFallback(request)")
    @CircuitBreaker("payment.process")
    @Retry("payment.process")
    @Timeout("payment.process")
    public PaymentResult process(PaymentRequest request) {
        return paymentGateway.charge(request);
    }

    protected PaymentResult processFallback(PaymentRequest request) {
        return PaymentResult.pendingManualReview();
    }
}

The annotation value is a config key, not a shared identifier — @Retry("payment.process") reads resilient.retry."payment.process". Different aspects on the same method may use independent keys.

4. Configure Resilience

resilient {
  timeout {
    default { duration = "1s" }
    "payment.process" { duration = "5s" }
  }
  retry {
    default {
      delay = "100ms"
      attempts = 3
      delayStep = "100ms"
    }
  }
  circuitbreaker {
    default {
      slidingWindowSize = 100
      minimumRequiredCalls = 10
      failureRateThreshold = 50
      waitDurationInOpenState = "30s"
      permittedCallsInHalfOpenState = 5
    }
  }
  fallback {
    default { enabled = true }
  }
}

Important: Use minimumRequiredCalls (NOT minimumNumberOfCalls) — Kora-specific key.


Combined Resilience Pattern

Recommended order (outer → inner):

@Component
public class PaymentClient {
    
    // Order (outer → inner):
    // 1. @Fallback — degraded response if everything fails
    // 2. @CircuitBreaker — fail fast if repeatedly failing
    // 3. @Retry — retry transient failures
    // 4. @Timeout — bound each attempt
    
    @Fallback(value = "payment.charge", method = "chargeFallback(request)")
    @CircuitBreaker("payment.charge")
    @Retry("payment.charge")
    @Timeout("payment.charge")
    public PaymentResult charge(PaymentRequest request) {
        return httpClient.post("/payments", request);
    }
    
    protected PaymentResult chargeFallback(PaymentRequest request) {
        return PaymentResult.pendingManualReview();
    }
}

Execution flow:

  1. @Timeout bounds the actual HTTP call
  2. @Retry repeats on transient failures (up to N attempts)
  3. @CircuitBreaker opens if failures exceed threshold
  4. @Fallback returns degraded response if circuit is open or all retries fail

References

Detailed guides with configuration options, patterns, and examples:

ReferenceDescription
retry-reference.md@Retry, predicates, backoff patterns, wait time calculation
circuit-breaker-reference.md@CircuitBreaker, state machine (CLOSED/OPEN/HALF_OPEN), custom predicates
timeout-reference.md@Timeout patterns, per-attempt vs overall timeout, thread interruption
fallback-reference.md@Fallback methods, signature rules, fallback patterns
resilience-config-reference.mdFull configuration reference, custom predicates, high-throughput tuning

Supported Signatures

Java

Class must be non-final for AOP to work.

Return TypeExample
T (or Void)User getUser(String id)
Optional<T>Optional<User> getUser(String id)
Mono<T> / Flux<T>Project Reactor types (require io.projectreactor:reactor-core)

Kotlin

Class and methods must be open for AOP to work.

Return TypeExample
T / T? / Unitfun getUser(id: String): User?
suspend fun ... : Tsuspend fun getUserAsync(id: String): User (requires kotlinx-coroutines-core)
Flow<T>fun getAllUsers(): Flow<User> (requires kotlinx-coroutines-core)

Common Pitfalls

ProblemSolution
Annotations don't triggerJava: class must be non-final. Kotlin: class and methods must be open
Circuit breaker never opensCheck minimumRequiredCalls — need enough calls to evaluate
Circuit breaker reacts to 404Implement CircuitBreakerPredicate to exclude business errors
Fallback not calledCheck method signature: return type compatible, parameters match subset
Retry makes latency worseEstimate worst-case: attempts × (delay + delayStep). Reduce for latency-sensitive paths
Config not appliedConfig key must match annotation value: @Retry("custom")resilient.retry."custom"

Troubleshooting

Verify AOP is working

# Check generated proxy classes
find build -name "*__AopProxy.java" | head -5

If no proxies are generated:

  • Check the class is non-final (Java) / open (Kotlin), and the method is open in Kotlin.
  • Verify the annotation processor is wired: Java annotationProcessor "ru.tinkoff.kora:annotation-processors", Kotlin ksp "ru.tinkoff.kora:symbol-processors".
  • Clean rebuild: ./gradlew clean build --no-daemon

Enable debug logging

logging.levels {
  "ru.tinkoff.kora.resilient": "DEBUG"
}

Assets

Templates in assets/:

TemplateDescription
ResilientService.java.templateFull resilience stack (Java)
ResilientService.kt.templateFull resilience stack (Kotlin)
RetryService.*.template@Retry pattern
CircuitBreakerService.*.template@CircuitBreaker pattern
TimeoutService.*.template@Timeout pattern
FallbackService.*.template@Fallback pattern

See assets/README.md for usage.


See Also

What ships with it: 16 files

54.3 KB alongside SKILL.md

evals/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.