Transitioning Couchbase Java SDK 3.x retry behavior to default idempotent mutations
0 reputation · 11 Oct 2020, 09:02 UTC
0 reputation · 11 Oct 2020, 09:02 UTC
Goal: guarantee that a transient network error does not cause a duplicate write when a Couchbase mutation is retried using the Java SDK 3.x. The SDK’s retry mechanism re‑sends the same operation, and safety currently depends on the application supplying idempotency through CAS tokens or the mutateIn upsert helper.
Uncertainty remains about whether the retry policy should automatically treat all mutations as idempotent by default, shifting the burden from developers to the SDK and potentially affecting existing code that relies on CAS for conflict detection.
Should the SDK enable idempotent mode by default for all retried mutations? What performance or behavioral side‑effects might arise from such a change? How can existing applications migrate to a default‑idempotent retry model without breaking CAS‑based logic?
No — making every retried mutation idempotent by default would be a breaking change, not a safety improvement. In Couchbase Java SDK 3.x, the built-in BestEffortRetryStrategy deliberately retries only operations the SDK knows are idempotent (reads, sub-document reads) or that the application explicitly marks as retryable. Mutations such as upsert, insert, replace, and non-upsert mutateIn are treated as non-idempotent, so a transient network error does not trigger an automatic resend. That default is what protects you from duplicate writes today.
One caveat: this reflects established SDK 3.x behavior as generally documented. Verify against the RetryStrategy javadoc and release notes for your exact minor version, since defaults can change across releases.
Idempotency is a property of the operation, not the transport. An append to a list field, a counter increment, or a blind upsert executed twice produces a different result than executed once. The SDK cannot know your semantics, so it cannot safely decide for you. If the default flipped:
maxRetries would need retuning across every existing deployment.You don't need the SDK to change its default; you need to tell it which of your mutations are safe to retry. The 3.x RetryStrategy interface lets you decide per request:
get, mutate with the returned CAS. A retry that arrives with a stale CAS still fails cleanly — no duplicate write.RetryStrategy that returns retry-approval only for operations you know are safe: mutations carrying a CAS from a prior read, or mutateIn calls using upsert semantics on a specific path. Check the RetryReason (e.g., node unreachable, timeout) and decline permanent errors.Default-idempotent retries would trade a rare, visible failure (an unretried transient error the app must handle) for an invisible one (duplicate writes and defeated CAS checks) across all existing code. The burden belongs with the application, and the SDK already provides the hook — a scoped custom RetryStrategy — to shift that burden only where your semantics genuinely allow it.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.