public XATransactionContext<K, V> getCurrentContext() { if (currentXid == null) { return null; } return transactionContextFactory.get(new TransactionId(currentXid)); }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SoftLock<?> softLock = (SoftLock<?>) o; if (transactionId != null ? !transactionId.equals(softLock.transactionId) : softLock.transactionId != null) return false; if (oldValue != null ? !oldValue.equals(softLock.oldValue) : softLock.oldValue != null) return false; return !(newValueHolder != null ? !newValueHolder.equals(softLock.newValueHolder) : softLock.newValueHolder != null); }
@Override public Xid[] recover(int flags) throws XAException { if (flags != XAResource.TMNOFLAGS && flags != XAResource.TMSTARTRSCAN && flags != XAResource.TMENDRSCAN && flags != (XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN)) { throw new EhcacheXAException("Recover flags not supported : " + xaResourceFlagsToString(flags), XAException.XAER_INVAL); } if ((flags & XAResource.TMSTARTRSCAN) == XAResource.TMSTARTRSCAN) { List<Xid> xids = new ArrayList<>(); Set<TransactionId> transactionIds = journal.recover().keySet(); for (TransactionId transactionId : transactionIds) { // filter-out in-flight tx if (!transactionContextFactory.contains(transactionId)) { xids.add(transactionId.getSerializableXid()); } } return xids.toArray(new Xid[xids.size()]); } return new Xid[0]; }
@Override public int hashCode() { int result = transactionId != null ? transactionId.hashCode() : 0; result = 31 * result + (oldValue != null ? oldValue.hashCode() : 0); result = 31 * result + (newValueHolder != null ? newValueHolder.hashCode() : 0); return result; }
@Override public int hashCode() { int result = transactionId != null ? transactionId.hashCode() : 0; result = 31 * result + (oldValue != null ? oldValue.hashCode() : 0); result = 31 * result + (newValueHolder != null ? newValueHolder.hashCode() : 0); return result; }
@Override public void rollback(Xid xid) throws XAException { if (currentXid != null) { throw new EhcacheXAException("Cannot rollback a non-ended start on : " + xid, XAException.XAER_PROTO); } TransactionId transactionId = new TransactionId(xid); XATransactionContext<K, V> transactionContext = transactionContextFactory.get(transactionId); try { XATransactionContext<K, V> rollbackContext = transactionContext; if (rollbackContext == null) { // recovery rollback rollbackContext = new XATransactionContext<>(new TransactionId(new SerializableXid(xid)), underlyingStore, journal, null, 0L); } rollbackContext.rollback(transactionContext == null); } catch (IllegalStateException ise) { throw new EhcacheXAException("Cannot rollback unknown XID : " + xid, XAException.XAER_NOTA); } catch (StoreAccessException cae) { throw new EhcacheXAException("Cannot rollback XID : " + xid, XAException.XAER_RMERR, cae); } finally { if (transactionContext != null) { transactionContextFactory.destroy(transactionId); } } }
if (preparedSoftLock.getTransactionId() != null && !preparedSoftLock.getTransactionId().equals(transactionId)) { LOGGER.debug("rollback skipping prepared softlock with non-matching TX ID (concurrent modification?)"); evictFromUnderlyingStore(key);
@Override public Xid[] recover(int flags) throws XAException { if (flags != XAResource.TMNOFLAGS && flags != XAResource.TMSTARTRSCAN && flags != XAResource.TMENDRSCAN && flags != (XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN)) { throw new EhcacheXAException("Recover flags not supported : " + xaResourceFlagsToString(flags), XAException.XAER_INVAL); } if ((flags & XAResource.TMSTARTRSCAN) == XAResource.TMSTARTRSCAN) { List<Xid> xids = new ArrayList<>(); Set<TransactionId> transactionIds = journal.recover().keySet(); for (TransactionId transactionId : transactionIds) { // filter-out in-flight tx if (!transactionContextFactory.contains(transactionId)) { xids.add(transactionId.getSerializableXid()); } } return xids.toArray(new Xid[xids.size()]); } return new Xid[0]; }
@Override public void forget(Xid xid) throws XAException { TransactionId transactionId = new TransactionId(xid); if (journal.isInDoubt(transactionId)) { throw new EhcacheXAException("Cannot forget in-doubt XID : " + xid, XAException.XAER_PROTO); } if (!journal.isHeuristicallyTerminated(transactionId)) { throw new EhcacheXAException("Cannot forget unknown XID : " + xid, XAException.XAER_NOTA); } journal.forget(transactionId); }
if (preparedSoftLock.getTransactionId() != null && !preparedSoftLock.getTransactionId().equals(transactionId)) { LOGGER.debug("commit skipping prepared softlock with non-matching TX ID (concurrent modification?)"); evictFromUnderlyingStore(key);
@Test public void testPrepareReadOnly() throws Exception { XATransactionContext<Long, String> xaTransactionContext = getXaTransactionContext(); assertThat(xaTransactionContext.prepare(), is(0)); verify(journal, times(1)).saveInDoubt(eq(new TransactionId(new TestXid(0, 0))), eq(Collections.emptySet())); verify(journal, times(0)).saveCommitted(eq(new TransactionId(new TestXid(0, 0))), anyBoolean()); verify(journal, times(1)).saveRolledBack(eq(new TransactionId(new TestXid(0, 0))), eq(false)); }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SoftLock<?> softLock = (SoftLock<?>) o; if (transactionId != null ? !transactionId.equals(softLock.transactionId) : softLock.transactionId != null) return false; if (oldValue != null ? !oldValue.equals(softLock.oldValue) : softLock.oldValue != null) return false; return !(newValueHolder != null ? !newValueHolder.equals(softLock.newValueHolder) : softLock.newValueHolder != null); }
@Test public void testRecoveryCommit() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(journal.recover()).thenReturn(Collections.singletonMap(new TransactionId(new TestXid(0, 0)), (Collection<Long>) Arrays.asList(1L, 2L, 3L))); when(journal.getInDoubtKeys(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(Arrays.asList(1L, 2L, 3L)); when(journal.isInDoubt(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(true); Xid[] recoveredXids = xaResource.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN); assertThat(recoveredXids.length, is(1)); xaResource.commit(recoveredXids[0], false); verify(xaTransactionContextFactory, times(0)).destroy(eq(new TransactionId(new TestXid(0, 0)))); verify(underlyingStore, times(1)).get(eq(1L)); verify(underlyingStore, times(1)).get(eq(2L)); verify(underlyingStore, times(1)).get(eq(3L)); }
if (preparedSoftLock.getTransactionId() != null && !preparedSoftLock.getTransactionId().equals(transactionId)) { LOGGER.debug("rollback skipping prepared softlock with non-matching TX ID (concurrent modification?)"); evictFromUnderlyingStore(key);
@Test public void testRecoveryRollback() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(journal.isInDoubt(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(true); when(journal.recover()).thenReturn(Collections.singletonMap(new TransactionId(new TestXid(0, 0)), (Collection<Long>) Arrays.asList(1L, 2L, 3L))); when(journal.getInDoubtKeys(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(Arrays.asList(1L, 2L, 3L)); Xid[] recoveredXids = xaResource.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN); assertThat(recoveredXids.length, is(1)); xaResource.rollback(recoveredXids[0]); verify(xaTransactionContextFactory, times(0)).destroy(eq(new TransactionId(new TestXid(0, 0)))); verify(underlyingStore, times(1)).get(eq(1L)); verify(underlyingStore, times(1)).get(eq(2L)); verify(underlyingStore, times(1)).get(eq(3L)); }
if (preparedSoftLock.getTransactionId() != null && !preparedSoftLock.getTransactionId().equals(transactionId)) { LOGGER.debug("commit skipping prepared softlock with non-matching TX ID (concurrent modification?)"); evictFromUnderlyingStore(key);
@Test public void testRecoverIgnoresInFlightTx() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(journal.recover()).thenReturn(Collections.singletonMap(new TransactionId(new TestXid(0, 0)), (Collection<Long>) Arrays.asList(1L, 2L, 3L))); when(xaTransactionContextFactory.contains(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(true); Xid[] recovered = xaResource.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN); assertThat(recovered.length, is(0)); }
@Test public void testPrepareReadOnly() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); when(xaTransactionContext.prepare()).thenReturn(0); int prepareRc = xaResource.prepare(new TestXid(0, 0)); assertThat(prepareRc, is(XAResource.XA_RDONLY)); verify(xaTransactionContextFactory, times(1)).destroy(eq(new TransactionId(new TestXid(0, 0)))); }
@Test public void testGetInDoubtKeysReturnsCorrectKeysAfterSavedCollectionCleared() throws Exception { Collection<Long> keys = new ArrayList<>(Arrays.asList(1L, 2L, 3L)); journal.saveInDoubt(new TransactionId(new TestXid(0, 0)), keys); keys.clear(); Collection<Long> inDoubtKeys = journal.getInDoubtKeys(new TransactionId(new TestXid(0, 0))); assertThat(inDoubtKeys, containsInAnyOrder(1L, 2L, 3L)); }
@Test public void testCommit1Pc() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); xaResource.commit(new TestXid(0, 0), true); verify(xaTransactionContextFactory, times(1)).destroy(eq(new TransactionId(new TestXid(0, 0)))); }