{"record":{"id":"2832aeb1479c962c","repo":"apache/seatunnel","slug":"jdbc-xa-sink-requires-max-retries-equal-to-0-when","errorCode":null,"errorMessage":"JDBC XA sink requires max_retries equal to 0 when is_exactly_once=true, otherwise it could cause duplicates.","messagePattern":"JDBC XA sink requires max_retries equal to 0 when is_exactly_once=true, otherwise it could cause duplicates\\.","errorType":"validation","errorClass":"OptionValidationException","httpStatus":null,"severity":"error","filePath":"seatunnel-connectors-v2/connector-jdbc/src/main/java/org/apache/seatunnel/connectors/seatunnel/jdbc/sink/JdbcSinkFactory.java","lineNumber":457,"sourceCode":"    /**\n     * Submission-time validator for {@code is_exactly_once=true}.\n     *\n     * <p>JDBC XA sink does not support retries; {@code max_retries} must be 0 when exactly-once is\n     * enabled, otherwise duplicates may occur.\n     */\n    static class ExactlyOnceMaxRetriesValidator implements ConditionExtension<Boolean> {\n        @Override\n        public String description() {\n            return \"is_exactly_once=true requires max_retries=0\";\n        }\n\n        @Override\n        public boolean evaluate(ReadonlyConfig config, Boolean value)\n                throws OptionValidationException {\n            if (Boolean.TRUE.equals(value)) {\n                int maxRetries = config.get(JdbcSinkOptions.MAX_RETRIES);\n                if (maxRetries != 0) {\n                    throw new OptionValidationException(\n                            \"JDBC XA sink requires max_retries equal to 0 when is_exactly_once=true, \"\n                                    + \"otherwise it could cause duplicates.\");\n                }\n            }\n            return true;\n        }\n    }\n}\n","sourceCodeStart":439,"sourceCodeEnd":466,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-connectors-v2/connector-jdbc/src/main/java/org/apache/seatunnel/connectors/seatunnel/jdbc/sink/JdbcSinkFactory.java#L439-L466","documentation":"JdbcSinkFactory validates option combinations at config-evaluation time. When is_exactly_once=true, the XA transactional sink requires MAX_RETRIES to be 0 because retrying a statement inside an XA transaction can replay already-committed batches, causing duplicate rows. The library throws OptionValidationException during option validation to fail fast at job-creation time.","triggerScenarios":"Configuring a JDBC sink with is_exactly_once = true while max_retries is set to any value other than 0; the validator (JdbcSinkFactory option validator evaluate()) is invoked when ReadonlyConfig is validated against the Option definitions.","commonSituations":"Users enable exactly-once semantics for XA-capable databases (MySQL, PostgreSQL, Oracle, DB2) but keep a max_retries value copied from an at-least-once configuration; upgrading from non-XA to XA sink mode without revisiting retry settings.","solutions":["Set max_retries = 0 in the sink configuration when is_exactly_once = true.","If retry behavior is needed, disable is_exactly_once and accept at-least-once semantics with idempotent targets (e.g. primary-key upsert).","Review the sink config block and remove conflicting max_retries overrides from the HOCON/SQL config."],"exampleFix":"// before\nsink {\n  Jdbc {\n    is_exactly_once = \"true\"\n    max_retries = 3\n  }\n}\n// after\nsink {\n  Jdbc {\n    is_exactly_once = \"true\"\n    max_retries = 0\n  }\n}","handlingStrategy":"validation","validationCode":"if (config.getBoolean(\"is_exactly_once\") && config.getInt(\"max_retries\") != 0) {\n  throw new IllegalArgumentException(\"max_retries must be 0 when is_exactly_once=true\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never set max_retries together with is_exactly_once=true","Validate sink option combinations in CI before deploying jobs","Use config templates per delivery-semantics mode (at-least-once vs exactly-once)"],"tags":["jdbc","config-validation","exactly-once","xa-transaction"],"backgroundTag":"conflicting-config-options","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}