{"record":{"id":"6ac0f1779b16511c","repo":"apache/seatunnel","slug":"rollback-recovered-transactions","errorCode":null,"errorMessage":"rollback {} recovered transactions","messagePattern":"rollback (.+?) recovered transactions","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"seatunnel-connectors-v2/connector-jdbc/src/main/java/org/apache/seatunnel/connectors/seatunnel/jdbc/internal/xa/XaGroupOpsImpl.java","lineNumber":138,"sourceCode":"    public void recoverAndRollback(\n            JobContext context,\n            SinkWriter.Context sinkContext,\n            XidGenerator xidGenerator,\n            Xid excludeXid) {\n        Collection<Xid> recovered =\n                xaFacade.recover().stream()\n                        .map(\n                                x ->\n                                        new XidImpl(\n                                                x.getFormatId(),\n                                                x.getGlobalTransactionId(),\n                                                x.getBranchQualifier()))\n                        .collect(Collectors.toList());\n        recovered.remove(excludeXid);\n        if (recovered.isEmpty()) {\n            return;\n        }\n        LOG.warn(\"rollback {} recovered transactions\", recovered.size());\n        for (Xid xid : recovered) {\n            if (xidGenerator.belongsToSubtask(xid, context, sinkContext)) {\n                try {\n                    xaFacade.rollback(xid);\n                } catch (Exception e) {\n                    LOG.info(\"unable to rollback recovered transaction, xid={}\", xid, e);\n                }\n            }\n        }\n    }\n\n    private static void throwIfAnyReachedMaxAttempts(\n            GroupXaOperationResult<XidInfo> result, int maxAttempts) {\n        List<XidInfo> reached = null;\n        for (XidInfo x : result.getForRetry()) {\n            if (x.getAttempts() >= maxAttempts) {\n                if (reached == null) {\n                    reached = new ArrayList<>();","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-connectors-v2/connector-jdbc/src/main/java/org/apache/seatunnel/connectors/seatunnel/jdbc/internal/xa/XaGroupOpsImpl.java#L120-L156","documentation":"A WARN log in XaGroupOpsImpl.recoverAndRollback(). At sink recovery (or with checkpoint disabled / on startup), recovered XA transactions belonging to this subtask that were prepared by a previous execution are rolled back; this warning announces how many are being cleaned up. Failures per-transaction are only logged at INFO so cleanup continues for the rest.","triggerScenarios":"recoverAndRollback() lists in-doubt XIDs via the facade (xa_recover), filters out the excluded xid and those not belonging to the subtask, then logs 'rollback N recovered transactions' and rolls each belonging xid back.","commonSituations":"Job restarted after a failure with prepared-but-uncommitted transactions left in the DB; manual restart with a different parallelism/uuid changes xid ownership so transactions are skipped; long-lived in-doubt transactions from crashed jobs.","solutions":["Let the recovery proceed — this is normal cleanup after a crash; ensure the DB user has XA recovery privileges (XA_RECOVER_ADMIN or equivalent)","If transactions are skipped, verify the xidGenerator's subtask assignment matches the original job (same parallelism, same sink identifier)","Manually roll back orphaned in-doubt transactions via DB tooling if recovery cannot (e.g. lost job state)","Check the INFO logs for individual rollback failures if transactions persist"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Check for lingering prepared transactions before restarting a job:\n// MySQL:  XA RECOVER;\n// PostgreSQL: SELECT * FROM pg_prepared_xacts;","typeGuard":null,"tryCatchPattern":"try {\n    xaFacade.rollback(xid);\n} catch (Exception e) {\n    LOG.info(\"unable to rollback recovered transaction, xid={}\", xid, e);\n    // continue with remaining recovered xids\n}","preventionTips":["Grant the sink DB user XA recovery privileges (XA_RECOVER_ADMIN / similar)","Keep parallelism and sink identifiers stable across restarts so xid ownership is preserved","Inspect leftover in-doubt transactions if a job is deleted without recovery"],"tags":["jdbc","xa","recovery","rollback"],"backgroundTag":"sql-query-failed","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}