public void rotateLogicalLogs() { for (XaDataSource dataSource : dataSources.values()) { try { dataSource.rotateLogicalLog(); } catch (IOException e) { msgLog.logMessage("Couldn't rotate logical log for " + dataSource.getName(), e); } } }
/** Recover all datasources */ public void recover(Iterator<List<TxLog.Record>> knownDanglingRecordList) { // contains NonCompletedTransaction that needs to be committed List<NonCompletedTransaction> commitList = new ArrayList<NonCompletedTransaction>(); // contains Xids that should be rolledback final List<Xid> rollbackList = new LinkedList<Xid>(); // key = Resource(branchId) value = XAResource final Map<Resource, XaDataSource> resourceMap = new HashMap<Resource, XaDataSource>(); buildRecoveryInfo(commitList, rollbackList, resourceMap, knownDanglingRecordList); // invoke recover on all xa resources found final List<Xid> recoveredXidsList = new LinkedList<Xid>(); try { for (XaDataSource xaDataSource : dataSources.values()) { XAResource xaRes = xaDataSource.getXaConnection().getXaResource(); Xid xids[] = xaRes.recover(XAResource.TMNOFLAGS); for (Xid xid : xids) { if (XidImpl.isThisTm(xid.getGlobalTransactionId())) { // linear search if (rollbackList.contains(xid)) { msgLog.logMessage("TM: Found pre commit " + xid + " rolling back ... ", true); rollbackList.remove(xid); xaRes.rollback(xid); } else { Resource resource = new Resource(xid.getBranchQualifier()); if (!resourceMap.containsKey(resource)) { resourceMap.put(resource, xaDataSource); } recoveredXidsList.add(xid); } } else { msgLog.warn("Unknown xid: " + xid); } } } // sort the commit list after sequence number Collections.sort(commitList); // go through and commit for (NonCompletedTransaction nct : commitList) { int seq = nct.getSequenceNumber(); Xid xids[] = nct.getXids(); msgLog.debug("Marked as commit tx-seq[" + seq + "] branch length: " + xids.length); for (Xid xid : xids) { if (!recoveredXidsList.contains(xid)) { msgLog.debug( "Tx-seq[" + seq + "][" + xid + "] not found in recovered xid list, " + "assuming already committed"); continue; } recoveredXidsList.remove(xid); Resource resource = new Resource(xid.getBranchQualifier()); if (!resourceMap.containsKey(resource)) { final TransactionFailureException ex = new TransactionFailureException("Couldn't find XAResource for " + xid); throw logAndReturn("TM: recovery error", ex); } msgLog.debug("TM: Committing tx " + xid); resourceMap.get(resource).getXaConnection().getXaResource().commit(xid, false); } } // rollback the rest for (Xid xid : recoveredXidsList) { Resource resource = new Resource(xid.getBranchQualifier()); if (!resourceMap.containsKey(resource)) { final TransactionFailureException ex = new TransactionFailureException("Couldn't find XAResource for " + xid); throw logAndReturn("TM: recovery error", ex); } msgLog.debug("TM: no match found for " + xid + " removing"); resourceMap.get(resource).getXaConnection().getXaResource().rollback(xid); } if (rollbackList.size() > 0) { msgLog.debug( "TxLog contained unresolved " + "xids that needed rollback. They couldn't be matched to " + "any of the XAResources recover list. " + "Assuming " + rollbackList.size() + " transactions already rolled back."); } // Rotate the logs of the participated data sources, making sure that // done-records are written so that even if the tm log gets truncated, // which it will be after this recovery, that transaction information // doesn't get lost. for (XaDataSource participant : MapUtil.reverse(resourceMap).keySet()) { participant.rotateLogicalLog(); } } catch (IOException | XAException e) { throw logAndReturn( "TM: recovery failed", new TransactionFailureException("Recovery failed.", e)); } }