Ballerina connector retry policy and duplicate writes on non-idempotent operations
0 reputation · 26 Apr 2022, 11:41 UTC
0 reputation · 26 Apr 2022, 11:41 UTC
Ballerina HTTP client and other connector clients support a retry attribute and a configurable retry policy with back-off and max attempts.
The retry mechanism reissues the same request on transient failure. For non-idempotent write operations this raises uncertainty about duplicate execution, as the runtime does not automatically add idempotency keys or coordinate with transaction boundaries. The transaction keyword exists for database operations, but there is no documented integration between retry policy and transaction scope to prevent duplicate writes.
Documentation does not specify a standard pattern for combining retry with idempotency keys or compensating actions for non-idempotent operations.
Does Ballerina define a supported way to bind an idempotency key to a connector request that is subject to retry? Is there guidance on scoping retry away from non-idempotent writes when idempotency cannot be guaranteed? What is the current design stance on providing a higher-level abstraction for deduplication on retry?
35775 reputation · 26 Apr 2022, 18:41 UTC
As of the Ballerina 2024R1 specification baseline and documented behavior through the 2024–2025 releases, the language does not define a supported way to bind an idempotency key to a connector request that is subject to retry. There is no documented guidance for scoping retry away from non-idempotent writes when idempotency cannot be guaranteed, and the runtime does not provide a higher-level abstraction for deduplication on retry. Retry behavior reissues the identical request without modification, and the transaction construct applies only to database connectors, not to HTTP or generic connector clients. Because connector implementations can vary, confirm the exact retry defaults for the client library you are using.
Ballerina HTTP clients and connector clients accept a retry configuration with backoff, maximum attempts, and status-code filters. When a transient failure occurs, the runtime reissues the same request bytes. The language does not automatically generate or attach an Idempotency-Key header, nor does it correlate the retry with any surrounding transaction boundary.
The transaction keyword and the sql:Transaction API are scoped to Ballerina database connectors such as JDBC or MySQL. They do not extend to HTTP calls or third-party connector clients. A retried HTTP POST or PUT therefore cannot be rolled back by a database transaction, and a database statement inside a transaction block is not retried by the connector retry policy.
Because no built-in binding exists, the supported approach is to implement idempotency at the service level. A common user-space workaround is to wrap the connector call in a function that generates a unique key, adds it as a request header, and configures the retry policy to trigger only on network-level errors rather than on HTTP status codes. The downstream service must honor that key and suppress duplicate side effects.
// Illustrative pattern; verify against your connector's actual API
function writeWithIdempotency(Client c, payload) returns Result|error {
string key = generateUniqueId();
Request req = { headers: { "Idempotency-Key": key } };
return c->write(payload, req);
// Retry policy should be limited to connection failures, not 4xx/5xx
}Behavior can differ between the standard http:Client and specific connector clients. Some connectors internally use http:Client with default retry settings and may retry POST or PUT unless explicitly disabled. To confirm the behavior for your stack, create a minimal Ballerina service that logs incoming headers, call it through a client configured with retry: { maxAttempts: 3 }, and observe whether the same header value is resent on each attempt. Inspect the connector source or generated stub to see if it overrides retry defaults.
The Ballerina specification does not describe a standard pattern for scoping retry away from non-idempotent writes, nor does it define a higher-level deduplication abstraction. The current stance delegates deduplication responsibility to the application architect: either make the remote operation idempotent, or implement custom retry logic with explicit idempotency keys.
Missing diagnostic: If you are using a specific third-party connector rather than the core http:Client, identifying that connector would change the recommendation, because its retry implementation may differ from the standard library behavior described above.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.