@Test public void testHasTimedOut() { XATransactionContext<Long, String> xaTransactionContext = new XATransactionContext<>(new TransactionId(new TestXid(0, 0)), null, null, timeSource, timeSource .getTimeMillis() + 30000); assertThat(xaTransactionContext.hasTimedOut(), is(false)); timeSource.advanceTime(30000); assertThat(xaTransactionContext.hasTimedOut(), is(true)); }
private ValueHolder<V> updateCommandForKey(K key, BiFunction<? super K, ? super V, ? extends V> mappingFunction, Supplier<Boolean> replaceEqual, XATransactionContext<K, V> currentContext) { V newValue = mappingFunction.apply(key, currentContext.newValueOf(key)); XAValueHolder<V> xaValueHolder = null; V oldValue = currentContext.oldValueOf(key); if (newValue == null) { if (!(oldValue == null && !replaceEqual.get())) { currentContext.addCommand(key, new StoreRemoveCommand<>(oldValue)); } else { currentContext.removeCommand(key); } } else { checkValue(newValue); xaValueHolder = new XAValueHolder<>(newValue, timeSource.getTimeMillis()); if (!(Objects.equals(oldValue, newValue) && !replaceEqual.get())) { currentContext.addCommand(key, new StorePutCommand<>(oldValue, xaValueHolder)); } } return xaValueHolder; }
public void commitInOnePhase() throws StoreAccessException, IllegalStateException, TransactionTimeoutException { if (journal.isInDoubt(transactionId)) { throw new IllegalStateException("Cannot commit-one-phase transaction that has been prepared : " + transactionId); } int prepared = prepare(); if (prepared > 0) { commit(false); } }
private ValueHolder<V> updateCommandForKey(K key, Function<? super K, ? extends V> mappingFunction, XATransactionContext<K, V> currentContext) { V computed = mappingFunction.apply(key); XAValueHolder<V> xaValueHolder = null; if (computed != null) { checkValue(computed); xaValueHolder = new XAValueHolder<>(computed, timeSource.getTimeMillis()); V oldValue = currentContext.oldValueOf(key); currentContext.addCommand(key, new StorePutCommand<>(oldValue, xaValueHolder)); } // else do nothing return xaValueHolder; }
@Test public void testEvictCommandCannotBeOverridden() { XATransactionContext<Long, String> xaTransactionContext = getXaTransactionContext(); xaTransactionContext.addCommand(1L, new StorePutCommand<>("old", new XAValueHolder<>("new", timeSource.getTimeMillis()))); assertThat(xaTransactionContext.touched(1L), is(true)); assertThat(xaTransactionContext.removed(1L), is(false)); assertThat(xaTransactionContext.updated(1L), is(true)); assertThat(xaTransactionContext.evicted(1L), is(false)); assertThat(xaTransactionContext.newValueHolderOf(1L).get(), equalTo("new")); assertThat(xaTransactionContext.oldValueOf(1L), equalTo("old")); assertThat(xaTransactionContext.newValueOf(1L), equalTo("new")); xaTransactionContext.addCommand(1L, new StoreEvictCommand<>("old")); assertThat(xaTransactionContext.touched(1L), is(true)); assertThat(xaTransactionContext.removed(1L), is(false)); assertThat(xaTransactionContext.updated(1L), is(false)); assertThat(xaTransactionContext.evicted(1L), is(true)); assertThat(xaTransactionContext.newValueHolderOf(1L), is(nullValue())); assertThat(xaTransactionContext.oldValueOf(1L), equalTo("old")); assertThat(xaTransactionContext.newValueOf(1L), is(nullValue())); xaTransactionContext.addCommand(1L, new StorePutCommand<>("old2", new XAValueHolder<>("new2", timeSource.getTimeMillis()))); assertThat(xaTransactionContext.touched(1L), is(true)); assertThat(xaTransactionContext.removed(1L), is(false)); assertThat(xaTransactionContext.updated(1L), is(false)); assertThat(xaTransactionContext.evicted(1L), is(true)); assertThat(xaTransactionContext.newValueHolderOf(1L), is(nullValue())); assertThat(xaTransactionContext.oldValueOf(1L), equalTo("old")); assertThat(xaTransactionContext.newValueOf(1L), is(nullValue())); }
checkKey(key); XATransactionContext<K, V> currentContext = getCurrentContext(); if (currentContext.removed(key)) { return updateCommandForKey(key, mappingFunction, currentContext); if (currentContext.evicted(key)) { return new XAValueHolder<>(currentContext.oldValueOf(key), timeSource.getTimeMillis()); boolean updated = currentContext.touched(key); if (softLockValueHolder == null) { if (updated) { xaValueHolder = currentContext.newValueHolderOf(key); } else { V computed = mappingFunction.apply(key); if (computed != null) { xaValueHolder = new XAValueHolder<>(computed, timeSource.getTimeMillis()); currentContext.addCommand(key, new StorePutCommand<>(null, xaValueHolder)); } else { xaValueHolder = null; currentContext.addCommand(key, new StoreEvictCommand<>(softLockValueHolder.get().getOldValue())); xaValueHolder = new XAValueHolder<>(softLockValueHolder, softLockValueHolder.get().getNewValueHolder().get()); } else { if (updated) { xaValueHolder = currentContext.newValueHolderOf(key); } else { xaValueHolder = new XAValueHolder<>(softLockValueHolder, softLockValueHolder.get().getOldValue());
@Override public ValueHolder<V> get(K key) throws StoreAccessException { checkKey(key); XATransactionContext<K, V> currentContext = getCurrentContext(); if (currentContext.removed(key)) { return null; } XAValueHolder<V> newValueHolder = currentContext.newValueHolderOf(key); if (newValueHolder != null) { return newValueHolder; } ValueHolder<SoftLock<V>> softLockValueHolder = getSoftLockValueHolderFromUnderlyingStore(key); if (softLockValueHolder == null) { return null; } SoftLock<V> softLock = softLockValueHolder.get(); if (isInDoubt(softLock)) { currentContext.addCommand(key, new StoreEvictCommand<>(softLock.getOldValue())); return null; } return new XAValueHolder<>(softLockValueHolder, softLock.getOldValue()); }
@Override public PutStatus put(K key, V value) throws StoreAccessException { checkKey(key); checkValue(value); XATransactionContext<K, V> currentContext = getCurrentContext(); if (currentContext.touched(key)) { V oldValue = currentContext.oldValueOf(key); currentContext.addCommand(key, new StorePutCommand<>(oldValue, new XAValueHolder<>(value, timeSource.getTimeMillis()))); return PutStatus.PUT; } ValueHolder<SoftLock<V>> softLockValueHolder = getSoftLockValueHolderFromUnderlyingStore(key); if (softLockValueHolder != null) { SoftLock<V> softLock = softLockValueHolder.get(); if (isInDoubt(softLock)) { currentContext.addCommand(key, new StoreEvictCommand<>(softLock.getOldValue())); } else { if (currentContext.addCommand(key, new StorePutCommand<>(softLock.getOldValue(), new XAValueHolder<>(value, timeSource .getTimeMillis())))) { return PutStatus.PUT; } } } else { if (currentContext.addCommand(key, new StorePutCommand<>(null, new XAValueHolder<>(value, timeSource.getTimeMillis())))) { return PutStatus.PUT; } } return PutStatus.NOOP; }
transactionContext.commitInOnePhase(); } catch (XATransactionContext.TransactionTimeoutException tte) { throw new EhcacheXAException("Transaction timed out", XAException.XA_RBTIMEOUT); if (commitContext == null) { commitContext = new XATransactionContext<>(new TransactionId(new SerializableXid(xid)), underlyingStore, journal, null, 0L); commitContext.commit(transactionContext == null);
public int prepare() throws StoreAccessException, IllegalStateException, TransactionTimeoutException { try { if (hasTimedOut()) { throw new TransactionTimeoutException(); for (Map.Entry<K, Command<V>> entry : commands.entrySet()) { if (entry.getValue() instanceof StoreEvictCommand) { evictFromUnderlyingStore(entry.getKey()); continue; SoftLock<V> newSoftLock = new SoftLock<>(transactionId, oldValue, entry.getValue().getNewValueHolder()); if (oldSoftLock != null) { boolean replaced = replaceInUnderlyingStore(entry.getKey(), oldSoftLock, newSoftLock); if (!replaced) { LOGGER.debug("prepare failed replace of softlock (concurrent modification?)"); evictFromUnderlyingStore(entry.getKey()); Store.ValueHolder<SoftLock<V>> existing = putIfAbsentInUnderlyingStore(entry, newSoftLock); if (existing != null) { LOGGER.debug("prepare failed putIfAbsent of softlock (concurrent modification?)"); evictFromUnderlyingStore(entry.getKey());
@Test public void testPrepareTimeout() throws Exception { XATransactionContext<Long, String> xaTransactionContext = getXaTransactionContext(); xaTransactionContext.addCommand(1L, new StorePutCommand<>("one", new XAValueHolder<>("un", timeSource.getTimeMillis()))); xaTransactionContext.addCommand(2L, new StoreRemoveCommand<>("two")); timeSource.advanceTime(30000); try { xaTransactionContext.prepare(); fail("expected TransactionTimeoutException"); } catch (XATransactionContext.TransactionTimeoutException tte) { // expected } }
if (currentContext.hasTimedOut()) { throw new XACacheException("Current XA transaction has timed out");
@Override public boolean containsKey(K key) throws StoreAccessException { checkKey(key); if (getCurrentContext().touched(key)) { return getCurrentContext().newValueHolderOf(key) != null; } ValueHolder<SoftLock<V>> softLockValueHolder = getSoftLockValueHolderFromUnderlyingStore(key); return softLockValueHolder != null && softLockValueHolder.get().getTransactionId() == null && softLockValueHolder.get().getOldValue() != null; }
@Test public void testRollbackPhase1() throws Exception { XATransactionContext<Long, String> xaTransactionContext = getXaTransactionContext(); xaTransactionContext.addCommand(1L, new StorePutCommand<>("one", new XAValueHolder<>("un", timeSource.getTimeMillis()))); xaTransactionContext.addCommand(2L, new StoreRemoveCommand<>("two")); xaTransactionContext.rollback(false); verifyNoMoreInteractions(underlyingStore); }
@Test public void testCommitInOnePhaseTimeout() throws Exception { XATransactionContext<Long, String> xaTransactionContext = getXaTransactionContext(); xaTransactionContext.addCommand(1L, new StorePutCommand<>("one", new XAValueHolder<>("un", timeSource.getTimeMillis()))); xaTransactionContext.addCommand(2L, new StoreRemoveCommand<>("two")); timeSource.advanceTime(30000); try { xaTransactionContext.commitInOnePhase(); fail("expected TransactionTimeoutException"); } catch (XATransactionContext.TransactionTimeoutException tte) { // expected } }
@Test public void testCommitNotPreparedInFlightThrows() throws Exception { XATransactionContext<Long, String> xaTransactionContext = getXaTransactionContext(); xaTransactionContext.addCommand(1L, new StorePutCommand<>("one", new XAValueHolder<>("un", timeSource.getTimeMillis()))); xaTransactionContext.addCommand(2L, new StorePutCommand<>("two", new XAValueHolder<>("deux", timeSource.getTimeMillis()))); @SuppressWarnings("unchecked") Store.ValueHolder<SoftLock<String>> mockValueHolder = mock(Store.ValueHolder.class); when(mockValueHolder.get()).thenReturn(new SoftLock<>(null, "two", null)); when(underlyingStore.get(eq(2L))).thenReturn(mockValueHolder); try { xaTransactionContext.commit(false); fail("expected IllegalArgumentException"); } catch (IllegalArgumentException ise) { // expected } }
@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); } } }
checkKey(key); XATransactionContext<K, V> currentContext = getCurrentContext(); if (currentContext.touched(key)) { return updateCommandForKey(key, mappingFunction, replaceEqual, currentContext); currentContext.addCommand(key, new StoreEvictCommand<>(oldValue)); } else { if (xaValueHolder == null) { if (oldValue != null) { currentContext.addCommand(key, new StoreRemoveCommand<>(oldValue)); currentContext.addCommand(key, new StorePutCommand<>(oldValue, xaValueHolder));
@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)); }
@Test public void testCannotCommit1PcPreparedXid() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); doThrow(IllegalStateException.class).when(xaTransactionContext).commitInOnePhase(); try { xaResource.commit(new TestXid(0, 0), true); fail("expected XAException"); } catch (XAException xae) { assertThat(xae.errorCode, is(XAException.XAER_PROTO)); } }