{"record":{"id":"705b8cf994e4d2e7","repo":"apache/seatunnel","slug":"skipping-checkpoint-batch-because-none-of-its-tran","errorCode":null,"errorMessage":"Skipping checkpoint batch because none of its transactions remain in the XA recovery scan; treating it as already resolved: {}","messagePattern":"Skipping checkpoint batch because none of its transactions remain in the XA recovery scan; treating it as already resolved: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"seatunnel-connectors-v2/connector-jdbc/src/main/java/org/apache/seatunnel/connectors/seatunnel/jdbc/sink/JdbcSinkAggregatedCommitter.java","lineNumber":214,"sourceCode":"        }\n        return normalized;\n    }\n\n    /**\n     * Replays one restored checkpoint using the recovery scan as evidence for which transactions\n     * can still be committed safely.\n     *\n     * @param checkpointXids checkpoint-owned prepared transactions in original commit order\n     * @param recoveredXids canonical values returned by the resource manager recovery scan\n     */\n    private void replayRecoveredCheckpoint(\n            List<XidInfo> checkpointXids, Set<XidKey> recoveredXids) {\n        if (checkpointXids.isEmpty()) {\n            return;\n        }\n        int firstRecoveredIndex = findFirstRecoveredIndex(checkpointXids, recoveredXids);\n        if (firstRecoveredIndex < 0) {\n            log.warn(\n                    \"Skipping checkpoint batch because none of its transactions remain in the XA recovery scan; treating it as already resolved: {}\",\n                    checkpointXids);\n            return;\n        }\n        List<XidInfo> stillPrepared = new ArrayList<>();\n        for (int i = firstRecoveredIndex; i < checkpointXids.size(); i++) {\n            XidInfo xidInfo = checkpointXids.get(i);\n            if (!containsEquivalentXid(recoveredXids, xidInfo.getXid())) {\n                throw new JdbcConnectorException(\n                        CommonErrorCodeDeprecated.WRITER_OPERATION_FAILED,\n                        String.format(\n                                \"checkpoint transaction %s is absent from the XA recovery scan after still-prepared transactions in the same commit batch: %s\",\n                                xidInfo.getXid(), checkpointXids));\n            }\n            stillPrepared.add(xidInfo);\n        }\n        commitXidInfos(stillPrepared);\n        if (firstRecoveredIndex > 0) {","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-connectors-v2/connector-jdbc/src/main/java/org/apache/seatunnel/connectors/seatunnel/jdbc/sink/JdbcSinkAggregatedCommitter.java#L196-L232","documentation":"During restoreCommit, JdbcSinkAggregatedCommitter.replayRecoveredCheckpoint() replays checkpointed XA transactions against the set of transactions still found in the database's XA recovery scan (xa_recover). If NONE of the checkpoint's XIDs appear in the recovery scan, the code assumes they were already committed/resolved and skips the batch, logging this WARN.","triggerScenarios":"Restoring a job from checkpoint whose checkpointed Xids are absent from xa_recover — e.g. transactions were resolved by a previous recovery run, the XA record expired/was purged, or the recovery scan hits a different user/database.","commonSituations":"Job restarted after crash; database cleanup jobs purging orphaned XA transactions; connecting with credentials lacking rights to see other users' XIDs; max_retry_timeout exceeded and externally committed.","solutions":["Verify the recovery connection uses the same user/DB so xa_recover returns the transactions","Check whether an earlier recovery pass already committed these Xids (expected, benign)","If Xids are being purged prematurely, extend the DB's orphan-transaction retention / max_commit_timeout","Inspect the DB for lost XA records and rely on downstream idempotency"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// before restore, list what recovery sees\nSet<String> recovered = xaFacade.recover().stream()\n    .map(x -> new String(x.getGlobalTransactionId(), UTF_8))\n    .collect(Collectors.toSet());\ncheckpointXids.forEach(x -> {\n    if (!recovered.contains(keyOf(x)))\n        log.info(\"Xid {} not in recovery scan; presumed resolved\", x);\n});","typeGuard":null,"tryCatchPattern":"try {\n    committer.restoreCommit(checkpointXids);\n} catch (TransientXaException e) {\n    log.warn(\"Recovery scan failed; retry with backoff\", e);\n}","preventionTips":["Use the same DB user for recovery as for commit","Keep idempotent sinks/downstream dedup","Extend orphan XA retention in the database"],"tags":["xa","recovery","checkpoint"],"backgroundTag":"invalid-state-transition","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"}