{"record":{"id":"75b303381551dcc0","repo":"apache/seatunnel","slug":"rollback-prepared-transaction-failed-try-to-recov","errorCode":null,"errorMessage":"rollback prepared transaction failed, try to recover pending transactions for current subtask, xid={}","messagePattern":"rollback prepared transaction failed, try to recover pending transactions for current subtask, xid=(.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"seatunnel-connectors-v2/connector-jdbc/src/main/java/org/apache/seatunnel/connectors/seatunnel/jdbc/sink/JdbcExactlyOnceSinkWriter.java","lineNumber":299,"sourceCode":"            prepareXid = null;\n        } catch (Exception rollbackException) {\n            JdbcConnectorException rollbackFailure =\n                    new JdbcConnectorException(\n                            JdbcConnectorErrorCode.XA_OPERATION_FAILED,\n                            String.format(\n                                    \"failed to rollback prepared transaction after begin next transaction failure, xid=%s\",\n                                    xid),\n                            rollbackException);\n            rollbackFailure.addSuppressed(beginTxException);\n            tryRecoverPreparedTransactionsAfterRollbackFailure(xid, rollbackFailure);\n            throw rollbackFailure;\n        }\n    }\n\n    private void tryRecoverPreparedTransactionsAfterRollbackFailure(\n            Xid failedRollbackXid, JdbcConnectorException rollbackFailure) {\n        try {\n            LOG.warn(\n                    \"rollback prepared transaction failed, try to recover pending transactions for current subtask, xid={}\",\n                    failedRollbackXid);\n            xaGroupOps.recoverAndRollback(context, sinkcontext, xidGenerator, null);\n        } catch (Exception recoveryException) {\n            LOG.warn(\n                    \"recovery after rollback prepared transaction failure also failed, xid={}\",\n                    failedRollbackXid,\n                    recoveryException);\n            rollbackFailure.addSuppressed(recoveryException);\n        }\n    }\n\n    private void failAndRollbackCurrentXidQuietly() {\n        if (currentXid == null || !xaFacade.isOpen()) {\n            return;\n        }\n        Xid xid = currentXid;\n        try {","sourceCodeStart":281,"sourceCodeEnd":317,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-connectors-v2/connector-jdbc/src/main/java/org/apache/seatunnel/connectors/seatunnel/jdbc/sink/JdbcExactlyOnceSinkWriter.java#L281-L317","documentation":"Logged by tryRecoverPreparedTransactionsAfterRollbackFailure: when rolling back a prepared XA transaction fails, the writer attempts a best-effort XA recovery (xaGroupOps.recoverAndRollback) for the current subtask to list and roll back all in-doubt transactions. This WARN indicates the direct rollback failed and the recovery fallback is being attempted; it is not itself the failure.","triggerScenarios":"Called from rollbackPrepareXidOrThrow after xaFacade.rollback(failedRollbackXid) throws during prepareCommit; any XA error on the prepared xid (XAER_RMFAIL, connection lost, facade closed) routes here.","commonSituations":"DB restart/failover between prepare and rollback; network partition; resource manager lost the transaction; driver-level XA errors on flaky connections.","solutions":["Wait for/inspect the follow-up log: if recovery succeeds the pending transactions are cleaned up and no action is needed","If recovery also fails (next WARN), fix the underlying DB connectivity and restart the job so recovery runs at startup","Ensure the DB user has XA recovery privileges (XA_RECOVER_ADMIN on MySQL 8, or appropriate Oracle/PG settings) so recoverAndRollback can list xids","Keep job/subtask identity and xidGenerator settings consistent across restarts so recovery can match the prepared xids","Manually inspect in-doubt transactions (xa recover / DBA_RECOVERABLE transactions) and roll them back if automated recovery cannot"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// ensure recovery is possible before it is needed\n// DB user must be allowed to list in-doubt transactions\n-- MySQL: SHOW GRANTS must include XA_RECOVER_ADMIN\n-- psql: SELECT gid FROM pg_prepared_xacts; should be permitted","typeGuard":null,"tryCatchPattern":"// this log is itself the catch/fallback path; guard the caller\ntry {\n    prepareCommit();\n} catch (Exception e) {\n    LOG.warn(\"prepareCommit failed; recovery will run on restart\", e);\n    // let the framework abort so tryRecoverPreparedTransactionsAfterRollbackFailure executes\n}","preventionTips":["Grant XA recovery privileges to the connector's DB account","Keep xid generation stable across restarts (same job id/subtask mapping)","Test failover: kill the DB mid-checkpoint in a staging run and confirm recovery cleans prepared xids","Avoid tight DB firewall/NAT idle cutoffs on the XA connection"],"tags":["jdbc","xa","recovery","distributed-transaction"],"backgroundTag":"xa-rollback-failed","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}