private Object readResolve() { return new XAValueHolder<>(id, creationTime, lastAccessTime, expirationTime, value, valueSerialized); } }
protected XAValueHolder<V> copyForSerialization(Serializer<V> valueSerializer) { ByteBuffer serializedValue = valueSerializer.serialize(value); return new XAValueHolder<>(this, serializedValue); }
protected XAValueHolder<V> copyAfterDeserialization(Serializer<V> valueSerializer) throws ClassNotFoundException { return new XAValueHolder<>(this, valueSerializer.read(ByteBuffer.wrap(valueSerialized))); }
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; }
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; }
@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; }
@Override public ValueHolder<V> putIfAbsent(K key, V value, Consumer<Boolean> put) throws StoreAccessException { checkKey(key); checkValue(value); XATransactionContext<K, V> currentContext = getCurrentContext(); if (currentContext.touched(key)) { V oldValue = currentContext.oldValueOf(key); V newValue = currentContext.newValueOf(key); if (newValue == null) { currentContext.addCommand(key, new StorePutCommand<>(oldValue, new XAValueHolder<>(value, timeSource.getTimeMillis()))); return null; } else { return currentContext.newValueHolderOf(key); } } ValueHolder<SoftLock<V>> softLockValueHolder = getSoftLockValueHolderFromUnderlyingStore(key); if (softLockValueHolder != null) { SoftLock<V> softLock = softLockValueHolder.get(); if (isInDoubt(softLock)) { currentContext.addCommand(key, new StoreEvictCommand<>(softLock.getOldValue())); return null; } else { return new XAValueHolder<>(softLockValueHolder, softLock.getOldValue()); } } else { currentContext.addCommand(key, new StorePutCommand<>(null, new XAValueHolder<>(value, timeSource.getTimeMillis()))); return null; } }
@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 SoftLock<T> copyForWrite(SoftLock<T> obj) { T oldValue = valueCopier.copyForWrite(obj.getOldValue()); XAValueHolder<T> valueHolder = obj.getNewValueHolder(); XAValueHolder<T> newValueHolder = valueHolder == null ? null : new XAValueHolder<>(valueHolder, valueCopier.copyForWrite(valueHolder .get())); return new SoftLock<>(obj.getTransactionId(), oldValue, newValueHolder); }
@Override public SoftLock<T> copyForRead(SoftLock<T> obj) { T oldValue = valueCopier.copyForRead(obj.getOldValue()); XAValueHolder<T> valueHolder = obj.getNewValueHolder(); XAValueHolder<T> newValueHolder = valueHolder == null ? null : new XAValueHolder<>(valueHolder, valueCopier.copyForRead(valueHolder .get())); return new SoftLock<>(obj.getTransactionId(), oldValue, newValueHolder); }
@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())); }
@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 @SuppressWarnings("unchecked") public void testPrepareConflictsEvicts() throws Exception { XATransactionContext<Long, String> xaTransactionContext = getXaTransactionContext(); xaTransactionContext.addCommand(1L, new StorePutCommand<>("one", new XAValueHolder<>("un", timeSource.getTimeMillis()))); xaTransactionContext.addCommand(2L, new StoreRemoveCommand<>("two")); when(underlyingStore.replace(any(Long.class), any(SoftLock.class), any(SoftLock.class))).thenReturn(ReplaceStatus.MISS_NOT_PRESENT); xaTransactionContext.prepare(); verify(underlyingStore).replace(eq(1L), eq(new SoftLock<>(null, "one", null)), eq(new SoftLock<>(new TransactionId(new TestXid(0, 0)), "one", new XAValueHolder<>("un", timeSource .getTimeMillis())))); verify(underlyingStore).remove(1L); verify(underlyingStore).replace(eq(2L), eq(new SoftLock<>(null, "two", null)), eq(new SoftLock<>(new TransactionId(new TestXid(0, 0)), "two", null))); verify(underlyingStore).remove(2L); }
@Test public void testSerialization() throws Exception { long now = System.currentTimeMillis(); XAValueHolder<String> valueHolder = new XAValueHolder<>("value", now - 1000); valueHolder.accessed(now, Duration.ofSeconds(100)); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream outputStream = new ObjectOutputStream(baos); outputStream.writeObject(valueHolder); outputStream.close(); @SuppressWarnings("unchecked") XAValueHolder<String> result = (XAValueHolder<String>) new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())).readObject(); assertThat(result.getId(), is(valueHolder.getId())); assertThat(result.creationTime(), is(valueHolder.creationTime())); assertThat(result.lastAccessTime(), is(valueHolder.lastAccessTime())); assertThat(result.expirationTime(), is(valueHolder.expirationTime())); assertThat(result.get(), is(valueHolder.get())); } }
@Override public SoftLock<String> get() { return new SoftLock<>(new TransactionId(new TestXid(0, 0)), "old1", new XAValueHolder<>("new1", timeSource .getTimeMillis())); verify(underlyingStore, times(1)).replace(eq(1L), eq(new SoftLock<>(new TransactionId(new TestXid(0, 0)), "old1", new XAValueHolder<>("new1", timeSource .getTimeMillis()))), eq(new SoftLock<>(null, "old1", null))); verify(underlyingStore, times(1)).remove(eq(1L));
@Test @SuppressWarnings("unchecked") public void testCommitConflictsEvicts() throws Exception { XATransactionContext<Long, String> xaTransactionContext = getXaTransactionContext(); when(journal.isInDoubt(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(true); when(journal.getInDoubtKeys(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(Arrays.asList(1L, 2L)); when(underlyingStore.get(eq(1L))).thenReturn(new AbstractValueHolder<SoftLock<String>>(-1, -1) { @Override public SoftLock<String> get() { return new SoftLock<>(new TransactionId(new TestXid(0, 0)), "old1", new XAValueHolder<>("new1", timeSource .getTimeMillis())); } }); when(underlyingStore.get(eq(2L))).thenReturn(new AbstractValueHolder<SoftLock<String>>(-1, -1) { @Override public SoftLock<String> get() { return new SoftLock<>(new TransactionId(new TestXid(0, 0)), "old2", null); } }); when(underlyingStore.replace(any(Long.class), any(SoftLock.class), any(SoftLock.class))).thenReturn(ReplaceStatus.MISS_NOT_PRESENT); when(underlyingStore.remove(any(Long.class), any(SoftLock.class))).thenReturn(RemoveStatus.KEY_MISSING); xaTransactionContext.commit(false); verify(underlyingStore, times(1)).replace(eq(1L), eq(new SoftLock<>(new TransactionId(new TestXid(0, 0)), "old1", new XAValueHolder<>("new1", timeSource .getTimeMillis()))), eq(new SoftLock<>(null, "new1", null))); verify(underlyingStore, times(1)).remove(eq(1L)); verify(underlyingStore, times(1)).remove(eq(2L), eq(new SoftLock<>(new TransactionId(new TestXid(0, 0)), "old2", null))); verify(underlyingStore, times(1)).remove(eq(2L)); }
@Test @SuppressWarnings("unchecked") public void testRollbackPhase2() throws Exception { XATransactionContext<Long, String> xaTransactionContext = getXaTransactionContext(); xaTransactionContext.addCommand(1L, new StorePutCommand<>("one", new XAValueHolder<>("un", timeSource.getTimeMillis()))); xaTransactionContext.addCommand(2L, new StoreRemoveCommand<>("two")); when(journal.isInDoubt(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(true); when(journal.getInDoubtKeys(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(Arrays.asList(1L, 2L)); when(underlyingStore.get(1L)).thenReturn(new AbstractValueHolder<SoftLock<String>>(-1, -1) { @Override public SoftLock<String> get() { return new SoftLock<>(new TransactionId(new TestXid(0, 0)), "one", new XAValueHolder<>("un", timeSource.getTimeMillis())); } }); when(underlyingStore.get(2L)).thenReturn(new AbstractValueHolder<SoftLock<String>>(-1, -1) { @Override public SoftLock<String> get() { return new SoftLock<>(new TransactionId(new TestXid(0, 0)), "two", null); } }); when(underlyingStore.replace(any(Long.class), any(SoftLock.class), any(SoftLock.class))).thenReturn(ReplaceStatus.HIT); xaTransactionContext.rollback(false); verify(underlyingStore, times(1)).get(1L); verify(underlyingStore, times(1)).replace(eq(1L), eq(new SoftLock<>(new TransactionId(new TestXid(0, 0)), "one", new XAValueHolder<>("un", timeSource .getTimeMillis()))), eq(new SoftLock<>(null, "one", null))); verify(underlyingStore, times(1)).get(2L); verify(underlyingStore, times(1)).replace(eq(2L), eq(new SoftLock<>(new TransactionId(new TestXid(0, 0)), "two", null)), eq(new SoftLock<>(null, "two", null))); }
@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 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 } }
@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()); }