static List<Xid> recoverXids(XAResource xaResource, XidSelector selector) throws XAException { List<Xid> ret = new ArrayList<Xid>(); boolean done = false; int flags = XAResource.TMSTARTRSCAN; Xid[] xidsFromLastScan = null; List<Xid> allRecoveredXidsSoFar = new ArrayList<Xid>(); do { xidsFromLastScan = xaResource.recover(flags); flags = XAResource.TMNOFLAGS; done = (xidsFromLastScan == null || xidsFromLastScan.length == 0); if (!done) { // TEMPTATIVELY SET done TO TRUE // TO TOLERATE ORACLE 8.1.7 INFINITE // LOOP (ALWAYS RETURNS SAME RECOVER // SET). IF A NEW SET OF XIDS IS RETURNED // THEN done WILL BE RESET TO FALSE done = true; for (int i = 0; i < xidsFromLastScan.length; i++) { Xid xid = new XID(xidsFromLastScan[i]); // our own XID implements equals and hashCode properly if (!allRecoveredXidsSoFar.contains(xid)) { // a new xid is returned -> we can not be in a recovery loop -> go on allRecoveredXidsSoFar.add(xid); done = false; if (selector.selects(xid)) { ret.add(xid); } } } } } while (!done); return ret; }
@Override public Xid[] recover(int i) throws XAException { return xaResource.recover(i); }
public void testRecoveryHandler() throws Exception { final XAResource xaResource = cache(0).getAdvancedCache().getXAResource(); final Xid[] recover = xaResource.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN); assert recover != null && recover.length == 0; }
/** * Tests that XA RECOVER works as expected. * * @throws Exception if test fails */ public void testRecover() throws Exception { if (!versionMeetsMinimum(5, 0)) { return; } if (versionMeetsMinimum(5, 7) && !versionMeetsMinimum(5, 7, 5)) { // Test is broken in 5.7.0 - 5.7.4 after server bug#14670465 fix which changed the XA RECOVER // output format. // Fixed in 5.7.5 server version return; } // BUG#14670465 fix broke this functionality in 5.7.1 - 5.7.2 if (versionMeetsMinimum(5, 7, 1) && !versionMeetsMinimum(5, 7, 3)) { return; } XAConnection xaConn = null, recoverConn = null; try { xaConn = getXAConnection(); Connection c = xaConn.getConnection(); Xid xid = createXid(); XAResource xaRes = xaConn.getXAResource(); xaRes.start(xid, XAResource.TMNOFLAGS); c.createStatement().executeQuery("SELECT 1"); xaRes.end(xid, XAResource.TMSUCCESS); xaRes.prepare(xid); // Now try and recover recoverConn = getXAConnection(); XAResource recoverRes = recoverConn.getXAResource(); Xid[] recoveredXids = recoverRes.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN); assertTrue(recoveredXids != null); assertTrue(recoveredXids.length > 0); boolean xidFound = false; for (int i = 0; i < recoveredXids.length; i++) { if (recoveredXids[i] != null && recoveredXids[i].equals(xid)) { xidFound = true; break; } } assertTrue(xidFound); recoverRes = recoverConn.getXAResource(); recoveredXids = recoverRes.recover(XAResource.TMSTARTRSCAN); assertTrue(recoveredXids != null); assertTrue(recoveredXids.length > 0); xidFound = false; for (int i = 0; i < recoveredXids.length; i++) { if (recoveredXids[i] != null && recoveredXids[i].equals(xid)) { xidFound = true; break; } } assertTrue(xidFound); // Test flags recoverRes.recover(XAResource.TMSTARTRSCAN); recoverRes.recover(XAResource.TMENDRSCAN); recoverRes.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN); // This should fail try { recoverRes.recover(XAResource.TMSUCCESS); fail("XAException should have been thrown"); } catch (XAException xaEx) { assertEquals(XAException.XAER_INVAL, xaEx.errorCode); } } finally { if (xaConn != null) { xaConn.close(); } if (recoverConn != null) { recoverConn.close(); } } }
/** recover the transaction */ public Xid[] recover(int flag) throws XAException { if (_isXATransaction) return _xaResource.recover(flag); else return null; }
@Override public Xid[] recover(int flags) throws XAException { return resource.recover(flags); }
/** 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)); } }