Retries
Some FTL features allow specifying a retry policy via a Go comment directive. Retries back off exponentially until the maximum is reached.
The directive has the following syntax:
//ftl:retry [<attempts=10>] <min-backoff> [<max-backoff=1hr>] [catch <catchVerb>]
@Retry(attempts = 10, minBackoff = "5s", maxBackoff = "1h", catchVerb = "<catchVerb>", catchModule = "<catchModule>")
@Retry(attempts = 10, minBackoff = "5s", maxBackoff = "1h", catchVerb = "<catchVerb>", catchModule = "<catchModule>")
For example, the following function will retry up to 10 times, with a delay of 5s, 10s, 20s, 40s, 60s, 60s, etc.
//ftl:retry 10 5s 1m
func Process(ctx context.Context, in Invoice) error {
// ...
}
@Retry(count = 10, minBackoff = "5s", maxBackoff = "1m")
fun process(inv: Invoice) {
// ...
}
@Retry(count = 10, minBackoff = "5s", maxBackoff = "1m")
public void process(Invoice in) {
// ...
}
PubSub
Subscribers can have a retry policy. For example:
//ftl:subscribe exampleSubscription
//ftl:retry 5 1s catch recoverPaymentProcessing
func ProcessPayment(ctx context.Context, payment Payment) error {
...
}
@Subscription(topic = "example", module = "publisher", name = "exampleSubscription")
@Retry(count = 5, minBackoff = "1s", catchVerb = "recoverPaymentProcessing")
fun processPayment(payment: Payment) {
// ...
}
@Subscription(topic = "example", module = "publisher", name = "exampleSubscription")
@Retry(count = 5, minBackoff = "1s", catchVerb = "recoverPaymentProcessing")
public void processPayment(Payment payment) {
// ...
}
Catching
After all retries have failed, a catch verb can be used to safely recover.
These catch verbs have a request type of builtin.CatchRequest<Req>
and no response type. If a catch verb returns an error, it will be retried until it succeeds so it is important to handle errors carefully.
//ftl:retry 5 1s catch recoverPaymentProcessing
func ProcessPayment(ctx context.Context, payment Payment) error {
...
}
//ftl:verb
func RecoverPaymentProcessing(ctx context.Context, request builtin.CatchRequest[Payment]) error {
// safely handle final failure of the payment
}
@Retry(count = 5, minBackoff = "1s", catchVerb = "recoverPaymentProcessing")
fun processPayment(payment: Payment) {
// ...
}
@Verb
fun recoverPaymentProcessing(req: CatchRequest<Payment>) {
// safely handle final failure of the payment
}
@Retry(count = 5, minBackoff = "1s", catchVerb = "recoverPaymentProcessing")
public void processPayment(Payment payment) {
// ...
}
@Verb
public void recoverPaymentProcessing(CatchRequest<Payment> req) {
// safely handle final failure of the payment
}