Accord: Fix unit tests and improve burn test stability

patch by Benedict; reviewed by Alex Petrov for CASSANDRA-20113
This commit is contained in:
Benedict Elliott Smith 2024-11-19 20:04:11 +00:00 committed by David Capwell
parent f2ea674140
commit ec7c7c9fce
15 changed files with 98 additions and 202 deletions

@ -1 +1 @@
Subproject commit a271897790aa3816c3dea2125b1e374b091bc090
Subproject commit 61f4ebd21ff34b0807081d9df442236d8c334b52

View File

@ -38,6 +38,7 @@ import accord.api.DataStore;
import accord.api.LocalListeners;
import accord.api.ProgressLog;
import accord.api.RoutingKey;
import accord.impl.AbstractSafeCommandStore.CommandStoreCaches;
import accord.impl.TimestampsForKey;
import accord.local.Cleanup;
import accord.local.Command;
@ -119,7 +120,7 @@ public class AccordCommandStore extends CommandStore
}
}
public static final class ExclusiveCaches extends Caches implements AutoCloseable
public static final class ExclusiveCaches extends Caches implements CommandStoreCaches<AccordSafeCommand, AccordSafeTimestampsForKey, AccordSafeCommandsForKey>
{
private final Lock lock;
@ -129,6 +130,25 @@ public class AccordCommandStore extends CommandStore
this.lock = lock;
}
@Override
public AccordSafeCommand acquireIfLoaded(TxnId txnId)
{
return commands().acquireIfLoaded(txnId);
}
@Override
public AccordSafeCommandsForKey acquireIfLoaded(RoutingKey key)
{
return commandsForKeys().acquireIfLoaded(key);
}
@Override
public AccordSafeTimestampsForKey acquireTfkIfLoaded(RoutingKey key)
{
return timestampsForKeys().acquireIfLoaded(key);
}
@Override
public void close()
{

View File

@ -561,7 +561,8 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
private <K, V> void onLoadedExclusive(AccordCacheEntry<K, V> loaded, V value, Throwable fail, boolean isForRange)
{
--activeLoads;
--activeRangeLoads;
if (isForRange)
--activeRangeLoads;
if (loaded.status() != EVICTED)
{

View File

@ -18,9 +18,7 @@
package org.apache.cassandra.service.accord;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
@ -31,26 +29,18 @@ import com.google.common.annotations.VisibleForTesting;
import accord.api.Agent;
import accord.api.DataStore;
import accord.api.Key;
import accord.api.ProgressLog;
import accord.api.RoutingKey;
import accord.impl.AbstractSafeCommandStore;
import accord.impl.CommandsSummary;
import accord.impl.SafeTimestampsForKey;
import accord.local.CommandStores;
import accord.local.CommandStores.RangesForEpoch;
import accord.local.KeyHistory;
import accord.local.NodeCommandStoreService;
import accord.local.PreLoadContext;
import accord.local.RedundantBefore;
import accord.local.SafeCommandStore;
import accord.local.cfk.CommandsForKey;
import accord.primitives.AbstractKeys;
import accord.primitives.AbstractUnseekableKeys;
import accord.primitives.Participants;
import accord.primitives.Ranges;
import accord.primitives.Routable;
import accord.primitives.Routables;
import accord.primitives.RoutingKeys;
import accord.primitives.Timestamp;
import accord.primitives.Txn;
import accord.primitives.TxnId;
@ -58,14 +48,11 @@ import accord.primitives.Unseekables;
import accord.utils.Invariants;
import org.apache.cassandra.service.accord.AccordCommandStore.ExclusiveCaches;
import static accord.local.KeyHistory.TIMESTAMPS;
import static accord.utils.Invariants.illegalArgument;
import static accord.utils.Invariants.illegalState;
public class AccordSafeCommandStore extends SafeCommandStore
public class AccordSafeCommandStore extends AbstractSafeCommandStore<AccordSafeCommand, AccordSafeTimestampsForKey, AccordSafeCommandsForKey, AccordCommandStore.ExclusiveCaches>
{
final AccordTask<?> task;
final PreLoadContext context;
private final @Nullable CommandsForRanges commandsForRanges;
private final AccordCommandStore commandStore;
private RangesForEpoch ranges;
@ -75,7 +62,7 @@ public class AccordSafeCommandStore extends SafeCommandStore
@Nullable CommandsForRanges commandsForRanges,
AccordCommandStore commandStore)
{
this.context = task.preLoadContext();
super(task.preLoadContext());
this.task = task;
this.commandsForRanges = commandsForRanges;
this.commandStore = commandStore;
@ -91,91 +78,6 @@ public class AccordSafeCommandStore extends SafeCommandStore
return new AccordSafeCommandStore(operation, commandsForRanges, commandStore);
}
@Override
public PreLoadContext canExecute(PreLoadContext with)
{
if (with.isEmpty()) return with;
if (with.keys().domain() == Routable.Domain.Range)
return with.isSubsetOf(this.context) ? with : null;
if (!context().keyHistory().satisfies(with.keyHistory()))
return null;
try (ExclusiveCaches caches = commandStore.tryLockCaches())
{
if (caches == null)
return with.isSubsetOf(this.context) ? with : null;
for (TxnId txnId : with.txnIds())
{
if (null != getInternal(txnId))
continue;
AccordSafeCommand safeCommand = caches.commands().acquireIfLoaded(txnId);
if (safeCommand == null)
return null;
add(safeCommand, caches);
}
KeyHistory keyHistory = with.keyHistory();
if (keyHistory == KeyHistory.NONE)
return with;
List<RoutingKey> unavailable = null;
AbstractUnseekableKeys keys = (AbstractUnseekableKeys) context.keys();
if (keys.size() == 0)
return with;
for (int i = 0 ; i < keys.size() ; ++i)
{
RoutingKey key = keys.get(i);
if (keyHistory == TIMESTAMPS)
{
if (null != timestampsForKeyInternal(key))
continue; // already in working set
AccordSafeTimestampsForKey safeTfk = caches.timestampsForKeys().acquireIfLoaded(key);
if (safeTfk != null)
{
add(safeTfk, caches);
continue;
}
}
else
{
if (null != getInternal(key))
continue; // already in working set
AccordSafeCommandsForKey safeCfk = caches.commandsForKeys().acquireIfLoaded(key);
if (safeCfk != null)
{
add(safeCfk, caches);
continue;
}
}
if (unavailable == null)
unavailable = new ArrayList<>();
unavailable.add(key);
}
if (unavailable == null)
return with;
if (unavailable.size() == keys.size())
return null;
Participants<RoutingKey> available = keys.without(RoutingKeys.ofSortedUnique(unavailable));
return PreLoadContext.contextFor(context.primaryTxnId(), context.additionalTxnId(), available, keyHistory);
}
}
@Override
public PreLoadContext context()
{
return context;
}
@VisibleForTesting
public Set<RoutingKey> commandsForKeysKeys()
{
@ -194,22 +96,12 @@ public class AccordSafeCommandStore extends SafeCommandStore
}
@Override
protected AccordSafeCommand ifLoadedAndInitialisedAndNotErasedInternal(TxnId txnId)
protected ExclusiveCaches tryGetCaches()
{
try (ExclusiveCaches caches = commandStore.tryLockCaches())
{
if (caches == null)
return null;
AccordSafeCommand command = caches.commands().acquireIfLoaded(txnId);
if (command == null)
return null;
return add(command, caches);
}
return commandStore.tryLockCaches();
}
private AccordSafeCommand add(AccordSafeCommand safeCommand, ExclusiveCaches caches)
protected AccordSafeCommand add(AccordSafeCommand safeCommand, ExclusiveCaches caches)
{
Object check = task.ensureCommands().putIfAbsent(safeCommand.txnId(), safeCommand);
if (check == null)
@ -224,7 +116,7 @@ public class AccordSafeCommandStore extends SafeCommandStore
}
}
private AccordSafeCommandsForKey add(AccordSafeCommandsForKey safeCfk, ExclusiveCaches caches)
protected AccordSafeCommandsForKey add(AccordSafeCommandsForKey safeCfk, ExclusiveCaches caches)
{
Object check = task.ensureCommandsForKey().putIfAbsent(safeCfk.key(), safeCfk);
if (check == null)
@ -239,7 +131,7 @@ public class AccordSafeCommandStore extends SafeCommandStore
}
}
private AccordSafeTimestampsForKey add(AccordSafeTimestampsForKey safeTfk, ExclusiveCaches caches)
protected AccordSafeTimestampsForKey add(AccordSafeTimestampsForKey safeTfk, ExclusiveCaches caches)
{
Object check = task.ensureTimestampsForKey().putIfAbsent(safeTfk.key(), safeTfk);
if (check == null)
@ -263,42 +155,7 @@ public class AccordSafeCommandStore extends SafeCommandStore
return commandsForKey.get(key);
}
@Override
protected AccordSafeCommandsForKey ifLoadedInternal(RoutingKey key)
{
try (ExclusiveCaches caches = commandStore.tryLockCaches())
{
if (caches == null)
return null;
AccordSafeCommandsForKey safeCfk = caches.commandsForKeys().acquireIfLoaded(key);
if (safeCfk == null)
return null;
Object check = task.ensureCommandsForKey().putIfAbsent(safeCfk.key(), safeCfk);
if (check == null)
{
safeCfk.preExecute();
return safeCfk;
}
else
{
caches.commandsForKeys().release(safeCfk, task);
throw illegalState("Attempted to take a duplicate reference to CFK for %s", key);
}
}
}
@Override
public SafeTimestampsForKey timestampsForKey(RoutingKey key)
{
AccordSafeTimestampsForKey safeTfk = timestampsForKeyInternal(key);
if (safeTfk == null)
throw illegalArgument("%s not referenced in %s", key, context);
return safeTfk;
}
private AccordSafeTimestampsForKey timestampsForKeyInternal(RoutingKey key)
protected AccordSafeTimestampsForKey timestampsForKeyInternal(RoutingKey key)
{
Map<RoutingKey, AccordSafeTimestampsForKey> timestampsForKey = task.timestampsForKey();
if (timestampsForKey == null)

View File

@ -1037,7 +1037,7 @@ public class AccordService implements IAccordService, Shutdownable
{
if (state.knows(blockedBy)) continue;
// need to fetch the state
if (safeStore.ifLoadedAndInitialisedAndNotErased(blockedBy) != null)
if (safeStore.ifLoadedAndInitialised(blockedBy) != null)
{
AsyncChain<Void> chain = populate(state, safeStore, blockedBy);
if (chain != null)
@ -1077,7 +1077,7 @@ public class AccordService implements IAccordService, Shutdownable
blocking = ((CommandsForKey.TxnInfo) blocking).plainTxnId();
state.keys.put(pk, blocking);
if (state.txns.containsKey(blocking)) return null;
if (safeStore.ifLoadedAndInitialisedAndNotErased(blocking) != null) return populate(state, safeStore, blocking);
if (safeStore.ifLoadedAndInitialised(blocking) != null) return populate(state, safeStore, blocking);
return populate(state, safeStore.commandStore(), blocking);
}
@ -1437,7 +1437,7 @@ public class AccordService implements IAccordService, Shutdownable
{
if (tracker.recordSuccess(from) == RequestStatus.Success)
{
node.configService().reportEpochRedundant(exclusiveSyncPoint.route.toRanges(), exclusiveSyncPoint.syncId.epoch());
node.configService().reportEpochRedundant(exclusiveSyncPoint.route.toRanges(), exclusiveSyncPoint.syncId.epoch() - 1);
trySuccess(exclusiveSyncPoint);
}
}

View File

@ -57,7 +57,7 @@ import org.apache.cassandra.index.accord.RoutesSearcher;
import org.apache.cassandra.service.accord.api.AccordRoutingKey;
import static accord.local.SafeCommandStore.TestDep.ANY_DEPS;
import static accord.local.SafeCommandStore.TestDep.WITH;
import static accord.local.SafeCommandStore.TestDep.WITH_OR_INVALIDATED;
import static accord.local.SafeCommandStore.TestStartedAt.STARTED_BEFORE;
import static accord.local.SafeCommandStore.TestStatus.ANY_STATUS;
import static accord.primitives.Routables.Slice.Minimal;
@ -129,6 +129,9 @@ public class CommandsForRanges extends TreeMap<Timestamp, CommandsForRanges.Summ
case IS_STABLE:
if (!summary.saveStatus.hasBeen(Stable) || summary.saveStatus.hasBeen(Truncated))
return;
case IS_STABLE_OR_INVALIDATED:
if (!summary.saveStatus.hasBeen(Stable) || summary.saveStatus.status == Truncated)
return;
}
if (testDep != ANY_DEPS)
@ -152,7 +155,7 @@ public class CommandsForRanges extends TreeMap<Timestamp, CommandsForRanges.Summ
// and so it is safe to execute, when in fact it is only a dependency on a different shard
// (and that other shard, perhaps, does not know that it is a dependency - and so it is not durably known)
// TODO (required): consider this some more
if ((testDep == WITH) == !summary.hasAsDep)
if ((testDep == WITH_OR_INVALIDATED) == (!summary.hasAsDep || summary.saveStatus == SaveStatus.Invalidated))
return;
Invariants.checkState(testTxnId.equals(summary.findAsDep));

View File

@ -33,7 +33,7 @@ import accord.local.Node;
import accord.messages.Apply;
import accord.primitives.Deps;
import accord.primitives.FullRoute;
import accord.primitives.Participants;
import accord.primitives.Route;
import accord.primitives.Timestamp;
import accord.primitives.Txn;
import accord.primitives.TxnId;
@ -89,14 +89,13 @@ public class AccordInteropAdapter extends AbstractTxnAdapter
}
@Override
public void persist(Node node, Topologies all, FullRoute<?> route, Participants<?> sendTo, TxnId txnId, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, BiConsumer<? super Result, Throwable> callback)
public void persist(Node node, Topologies all, FullRoute<?> fullRoute, Route<?> sendTo, TxnId txnId, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, BiConsumer<? super Result, Throwable> callback)
{
// TODO (required): we aren't using sendTo
if (applyKind == Minimal && doInteropPersist(node, all, route, txnId, txn, executeAt, deps, writes, result, callback))
if (applyKind == Minimal && doInteropPersist(node, all, sendTo, txnId, txn, executeAt, deps, writes, result, fullRoute, callback))
return;
if (callback != null) callback.accept(result, null);
new PersistTxn(node, all, txnId, route, txn, executeAt, deps, writes, result)
new PersistTxn(node, all, txnId, sendTo, txn, executeAt, deps, writes, result, fullRoute)
.start(Apply.FACTORY, applyKind, all, writes, result);
}
@ -113,14 +112,14 @@ public class AccordInteropAdapter extends AbstractTxnAdapter
return true;
}
private static boolean doInteropPersist(Node node, Topologies all, FullRoute<?> route, TxnId txnId, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, BiConsumer<? super Result, Throwable> callback)
private static boolean doInteropPersist(Node node, Topologies all, Route<?> sendTo, TxnId txnId, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, FullRoute<?> fullRoute, BiConsumer<? super Result, Throwable> callback)
{
Update update = txn.update();
ConsistencyLevel consistencyLevel = update instanceof AccordUpdate ? ((AccordUpdate) update).cassandraCommitCL() : null;
if (consistencyLevel == null || consistencyLevel == ConsistencyLevel.ANY || writes.isEmpty())
return false;
new AccordInteropPersist(node, all, txnId, route, txn, executeAt, deps, writes, result, consistencyLevel, callback)
new AccordInteropPersist(node, all, txnId, sendTo, txn, executeAt, deps, writes, result, fullRoute, consistencyLevel, callback)
.start(AccordInteropApply.FACTORY, Minimal, all, writes, result);
return true;
}

View File

@ -62,14 +62,14 @@ public class AccordInteropApply extends Apply implements LocalListeners.ComplexL
public static final Apply.Factory FACTORY = new Apply.Factory()
{
@Override
public Apply create(Kind kind, Id to, Topologies participates, TxnId txnId, FullRoute<?> route, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result)
public Apply create(Kind kind, Id to, Topologies participates, TxnId txnId, Route<?> route, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, FullRoute<?> fullRoute)
{
checkArgument(kind != Kind.Maximal, "Shouldn't need to send a maximal commit with interop support");
ConsistencyLevel commitCL = txn.update() instanceof AccordUpdate ? ((AccordUpdate) txn.update()).cassandraCommitCL() : null;
// Any asynchronous apply option should use the regular Apply that doesn't wait for writes to complete
if (commitCL == null || commitCL == ConsistencyLevel.ANY)
return Apply.FACTORY.create(kind, to, participates, txnId, route, txn, executeAt, deps, writes, result);
return new AccordInteropApply(kind, to, participates, txnId, route, txn, executeAt, deps, writes, result);
return Apply.FACTORY.create(kind, to, participates, txnId, route, txn, executeAt, deps, writes, result, fullRoute);
return new AccordInteropApply(kind, to, participates, txnId, route, txn, executeAt, deps, writes, result, fullRoute);
}
};
@ -91,9 +91,9 @@ public class AccordInteropApply extends Apply implements LocalListeners.ComplexL
super(kind, txnId, route, waitForEpoch, executeAt, deps, txn, fullRoute, writes, result);
}
private AccordInteropApply(Kind kind, Id to, Topologies participates, TxnId txnId, FullRoute<?> route, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result)
private AccordInteropApply(Kind kind, Id to, Topologies participates, TxnId txnId, Route<?> route, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, FullRoute<?> fullRoute)
{
super(kind, to, participates, txnId, route, txn, executeAt, deps, writes, result);
super(kind, to, participates, txnId, route, txn, executeAt, deps, writes, result, fullRoute);
}
@Override

View File

@ -30,6 +30,7 @@ import accord.local.Node;
import accord.messages.Apply;
import accord.primitives.Deps;
import accord.primitives.FullRoute;
import accord.primitives.Route;
import accord.primitives.Timestamp;
import accord.primitives.Txn;
import accord.primitives.TxnId;
@ -91,9 +92,9 @@ public class AccordInteropPersist extends Persist
private final ConsistencyLevel consistencyLevel;
private CallbackHolder callback;
public AccordInteropPersist(Node node, Topologies topologies, TxnId txnId, FullRoute<?> route, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, ConsistencyLevel consistencyLevel, BiConsumer<? super Result, Throwable> clientCallback)
public AccordInteropPersist(Node node, Topologies topologies, TxnId txnId, Route<?> sendTo, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, FullRoute<?> fullRoute, ConsistencyLevel consistencyLevel, BiConsumer<? super Result, Throwable> clientCallback)
{
super(node, topologies, txnId, route, txn, executeAt, deps, writes, result);
super(node, topologies, txnId, sendTo, txn, executeAt, deps, writes, result, fullRoute);
Invariants.checkArgument(consistencyLevel == ConsistencyLevel.QUORUM || consistencyLevel == ConsistencyLevel.ALL || consistencyLevel == ConsistencyLevel.SERIAL || consistencyLevel == ConsistencyLevel.ONE);
this.consistencyLevel = consistencyLevel;
registerClientCallback(result, clientCallback);

View File

@ -45,7 +45,8 @@ public class AwaitSerializer
CommandSerializers.txnId.serialize(await.txnId, out, version);
KeySerializers.participants.serialize(await.scope, out, version);
out.writeByte(await.blockedUntil.ordinal());
out.writeUnsignedVInt(await.awaitEpoch - await.txnId.epoch());
out.writeUnsignedVInt(await.maxAwaitEpoch - await.txnId.epoch());
out.writeUnsignedVInt(await.maxAwaitEpoch - await.minAwaitEpoch);
out.writeUnsignedVInt32(await.callbackId + 1);
Invariants.checkState(await.callbackId >= -1);
}
@ -56,10 +57,11 @@ public class AwaitSerializer
TxnId txnId = CommandSerializers.txnId.deserialize(in, version);
Participants<?> scope = KeySerializers.participants.deserialize(in, version);
BlockedUntil blockedUntil = BlockedUntil.forOrdinal(in.readByte());
long awaitEpoch = in.readUnsignedVInt() + txnId.epoch();
long maxAwaitEpoch = in.readUnsignedVInt() + txnId.epoch();
long minAwaitEpoch = maxAwaitEpoch - in.readUnsignedVInt();
int callbackId = in.readUnsignedVInt32() - 1;
Invariants.checkState(callbackId >= -1);
return Await.SerializerSupport.create(txnId, scope, blockedUntil, awaitEpoch, callbackId);
return Await.SerializerSupport.create(txnId, scope, blockedUntil, minAwaitEpoch, maxAwaitEpoch, callbackId);
}
@Override
@ -68,7 +70,8 @@ public class AwaitSerializer
return CommandSerializers.txnId.serializedSize(await.txnId, version)
+ KeySerializers.participants.serializedSize(await.scope, version)
+ TypeSizes.BYTE_SIZE
+ VIntCoding.computeUnsignedVIntSize(await.awaitEpoch - await.txnId.epoch())
+ VIntCoding.computeUnsignedVIntSize(await.maxAwaitEpoch - await.txnId.epoch())
+ VIntCoding.computeUnsignedVIntSize(await.maxAwaitEpoch - await.minAwaitEpoch)
+ VIntCoding.computeUnsignedVIntSize(await.callbackId + 1);
}
};

View File

@ -80,6 +80,7 @@ public class ReadDataSerializers
{
CommandSerializers.txnId.serialize(msg.txnId, out, version);
KeySerializers.participants.serialize(msg.readScope, out, version);
out.writeUnsignedVInt(msg.minEpoch());
CommandSerializers.timestamp.serialize(msg.executeAt, out, version);
KeySerializers.fullRoute.serialize(msg.route, out, version);
CommandSerializers.partialTxn.serialize(msg.txn, out, version);
@ -93,6 +94,7 @@ public class ReadDataSerializers
return ApplyThenWaitUntilApplied.SerializerSupport.create(
CommandSerializers.txnId.deserialize(in, version),
KeySerializers.participants.deserialize(in, version),
in.readUnsignedVInt(),
CommandSerializers.timestamp.deserialize(in, version),
KeySerializers.fullRoute.deserialize(in, version),
CommandSerializers.partialTxn.deserialize(in, version),
@ -106,6 +108,7 @@ public class ReadDataSerializers
{
return CommandSerializers.txnId.serializedSize(msg.txnId, version)
+ KeySerializers.participants.serializedSize(msg.readScope, version)
+ TypeSizes.sizeofUnsignedVInt(msg.minEpoch())
+ CommandSerializers.timestamp.serializedSize(msg.executeAt, version)
+ KeySerializers.fullRoute.serializedSize(msg.route, version)
+ CommandSerializers.partialTxn.serializedSize(msg.txn, version)
@ -279,7 +282,8 @@ public class ReadDataSerializers
{
CommandSerializers.txnId.serialize(waitUntilApplied.txnId, out, version);
KeySerializers.participants.serialize(waitUntilApplied.readScope, out, version);
out.writeUnsignedVInt(waitUntilApplied.executeAtEpoch);
out.writeUnsignedVInt(waitUntilApplied.minEpoch());
out.writeUnsignedVInt(waitUntilApplied.executeAtEpoch - waitUntilApplied.minEpoch());
}
@Override
@ -287,8 +291,9 @@ public class ReadDataSerializers
{
TxnId txnId = CommandSerializers.txnId.deserialize(in, version);
Participants<?> readScope = KeySerializers.participants.deserialize(in, version);
long executeAtEpoch = in.readUnsignedVInt();
return WaitUntilApplied.SerializerSupport.create(txnId, readScope, executeAtEpoch);
long minEpoch = in.readUnsignedVInt();
long executeAtEpoch = minEpoch + in.readUnsignedVInt();
return WaitUntilApplied.SerializerSupport.create(txnId, readScope, minEpoch, executeAtEpoch);
}
@Override
@ -296,7 +301,8 @@ public class ReadDataSerializers
{
return CommandSerializers.txnId.serializedSize(waitUntilApplied.txnId, version)
+ KeySerializers.participants.serializedSize(waitUntilApplied.readScope, version)
+ TypeSizes.sizeofUnsignedVInt(waitUntilApplied.executeAtEpoch);
+ TypeSizes.sizeofUnsignedVInt(waitUntilApplied.minEpoch())
+ TypeSizes.sizeofUnsignedVInt(waitUntilApplied.executeAtEpoch - waitUntilApplied.minEpoch());
}
};
}

View File

@ -207,8 +207,8 @@ public class CompactionAccordIteratorsTest
testAccordCommandsPurger(redundantBefore(GT_TXN_ID), durableBefore(UNIVERSAL), expectAccordCommandsErase());
// Universally durable but not locally; we're stale, but shouldn't erase
testAccordCommandsPurger(redundantBefore(LT_TXN_ID), durableBefore(UNIVERSAL), expectAccordCommandsNoChange());
// With redundantBefore at the txnId there should be no change because it is < not <=
testAccordCommandsPurger(redundantBefore(TXN_ID), durableBefore(MAJORITY), expectAccordCommandsNoChange());
// redundantBefore will be > txnId as must be exclusive sync point, so will truncate
testAccordCommandsPurger(redundantBefore(TXN_ID), durableBefore(MAJORITY), expectAccordCommandsTruncated());
testAccordCommandsPurger(redundantBefore(LT_TXN_ID), durableBefore(MAJORITY), expectAccordCommandsNoChange());
// Durable at a majority can be truncated with minimal data preserved, it must be redundant for this to occur
testAccordCommandsPurger(redundantBefore(GT_TXN_ID), durableBefore(MAJORITY), expectAccordCommandsTruncated());
@ -249,10 +249,11 @@ public class CompactionAccordIteratorsTest
testAccordCommandsForKeyPurger(null, expectedAccordCommandsForKeyNoChange());
testAccordTimestampsForKeyPurger(redundantBefore(LT_TXN_ID), expectedAccordTimestampsForKeyNoChange());
testAccordCommandsForKeyPurger(redundantBefore(LT_TXN_ID), expectedAccordCommandsForKeyNoChange());
testAccordTimestampsForKeyPurger(redundantBefore(TXN_ID), expectedAccordTimestampsForKeyNoChange());
testAccordCommandsForKeyPurger(redundantBefore(TXN_ID), expectedAccordCommandsForKeyNoChange());
testAccordTimestampsForKeyPurger(redundantBefore(GT_TXN_ID), expectedAccordTimestampsForKeyEraseOne());
testAccordCommandsForKeyPurger(redundantBefore(GT_TXN_ID), expectedAccordCommandsForKeyEraseOne());
// will erase one more than expected as converted to ExclusiveSyncPoint id which is > base id
testAccordTimestampsForKeyPurger(redundantBefore(TXN_ID), expectedAccordTimestampsForKeyEraseOne());
testAccordCommandsForKeyPurger(redundantBefore(TXN_ID), expectedAccordCommandsForKeyEraseOne());
testAccordTimestampsForKeyPurger(redundantBefore(GT_TXN_ID), expectedAccordTimestampsForKeyEraseAll());
testAccordCommandsForKeyPurger(redundantBefore(GT_TXN_ID), expectedAccordCommandsForKeyEraseAll());
testAccordTimestampsForKeyPurger(redundantBefore(GT_SECOND_TXN_ID), expectedAccordTimestampsForKeyEraseAll());
testAccordCommandsForKeyPurger(redundantBefore(GT_SECOND_TXN_ID), expectedAccordCommandsForKeyEraseAll());
}
@ -399,7 +400,7 @@ public class CompactionAccordIteratorsTest
private static RedundantBefore redundantBefore(TxnId txnId)
{
Ranges ranges = AccordTestUtils.fullRange(AccordTestUtils.keys(table, 42));
txnId = txnId.as(Kind.Read, Range);
txnId = txnId.as(Kind.ExclusiveSyncPoint, Range);
return RedundantBefore.create(ranges, Long.MIN_VALUE, Long.MAX_VALUE, txnId, txnId, txnId, txnId, LT_TXN_ID.as(Range));
}

View File

@ -133,7 +133,7 @@ public class AccordTaskTest
// TODO review: This change to `ifInitialized` was done in a lot of places and it doesn't preserve this property
// I fixed this reference to point to `ifLoadedAndInitialised` and but didn't update other places
Assert.assertNull(instance.ifInitialised(txnId));
Assert.assertNull(instance.ifLoadedAndInitialisedAndNotErased(txnId));
Assert.assertNull(instance.ifLoadedAndInitialised(txnId));
}));
UntypedResultSet result = AccordKeyspace.loadCommandRow(commandStore, txnId);

View File

@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
@ -202,6 +203,8 @@ public class CommandsForKeySerializerTest
case Erased:
case ErasedOrVestigial:
return Command.Truncated.erased(txnId, Status.Durability.UniversalOrInvalidated, StoreParticipants.empty(txnId));
case Invalidated:
return Command.SerializerSupport.invalidated(txnId);
}
@ -431,7 +434,8 @@ public class CommandsForKeySerializerTest
}
}
Choices<SaveStatus> saveStatusChoices = Choices.uniform(SaveStatus.values());
// TODO (expected): we currently don't explore TruncatedApply statuses because we don't transition through all phases and therefore don't adopt the Applied status
Choices<SaveStatus> saveStatusChoices = Choices.uniform(EnumSet.complementOf(EnumSet.of(SaveStatus.TruncatedApply, SaveStatus.TruncatedApplyWithOutcome, SaveStatus.TruncatedApplyWithDeps)).toArray(SaveStatus[]::new));
Supplier<SaveStatus> saveStatusSupplier = () -> {
SaveStatus result = saveStatusChoices.choose(source);
while (result == SaveStatus.TruncatedApplyWithDeps) // not a real save status
@ -471,7 +475,8 @@ public class CommandsForKeySerializerTest
while (commands.size() > 0)
{
int next = source.nextInt(commands.size());
cfk = cfk.update(commands.get(next)).cfk();
Command command = commands.get(next);
cfk = cfk.update(command).cfk();
commands.set(next, commands.get(commands.size() - 1));
commands.remove(commands.size() - 1);
}
@ -486,7 +491,7 @@ public class CommandsForKeySerializerTest
}
TxnInfo info = cfk.get(i);
InternalStatus expectStatus = InternalStatus.from(cmd.saveStatus);
if (expectStatus == null) expectStatus = InternalStatus.TRANSITIVELY_KNOWN;
if (expectStatus == null) expectStatus = InternalStatus.TRANSITIVE;
if (expectStatus.hasExecuteAtOrDeps)
Assert.assertEquals(cmd.executeAt, info.executeAt);
Assert.assertEquals(expectStatus, info.status());
@ -545,7 +550,7 @@ public class CommandsForKeySerializerTest
{
int idx = Arrays.binarySearch(ids, u.txnId);
if (idx < 0)
missing.add(TxnInfo.create(u.txnId, InternalStatus.TRANSITIVELY_KNOWN, true, u.txnId, Ballot.ZERO));
missing.add(TxnInfo.create(u.txnId, InternalStatus.TRANSITIVE, true, u.txnId, Ballot.ZERO));
}
if (!missing.isEmpty())
{

View File

@ -443,7 +443,7 @@ public class AccordGenerators
public static Gen<RedundantBefore.Entry> redundantBeforeEntry(IPartitioner partitioner)
{
return redundantBeforeEntry(Gens.bools().all(), range(partitioner), AccordGens.txnIds(Gens.pick(Txn.Kind.SyncPoint, Txn.Kind.ExclusiveSyncPoint), ignore -> Routable.Domain.Range));
return redundantBeforeEntry(Gens.bools().all(), range(partitioner), AccordGens.txnIds(Gens.pick(Txn.Kind.ExclusiveSyncPoint), ignore -> Routable.Domain.Range));
}
public static Gen<RedundantBefore.Entry> redundantBeforeEntry(Gen<Boolean> emptyGen, Gen<Range> rangeGen, Gen<TxnId> txnIdGen)
@ -451,11 +451,11 @@ public class AccordGenerators
return rs -> {
Range range = rangeGen.next(rs);
TxnId locallyWitnessedOrInvalidatedBefore = emptyGen.next(rs) ? TxnId.NONE : txnIdGen.next(rs); // emptyable or range
TxnId locallyAppliedOrInvalidatedBefore = emptyGen.next(rs) ? TxnId.NONE : txnIdGen.next(rs); // emptyable or range
TxnId locallyDecidedAndAppliedOrInvalidatedBefore = locallyAppliedOrInvalidatedBefore;
TxnId shardAppliedOrInvalidatedBefore = emptyGen.next(rs) ? TxnId.NONE : txnIdGen.next(rs); // emptyable or range
TxnId shardOnlyAppliedOrInvalidatedBefore = shardAppliedOrInvalidatedBefore;
TxnId gcBefore = shardAppliedOrInvalidatedBefore;
TxnId locallyAppliedOrInvalidatedBefore = TxnId.nonNullOrMin(locallyWitnessedOrInvalidatedBefore, emptyGen.next(rs) ? TxnId.NONE : txnIdGen.next(rs)); // emptyable or range
TxnId locallyDecidedAndAppliedOrInvalidatedBefore = TxnId.nonNullOrMin(locallyAppliedOrInvalidatedBefore, emptyGen.next(rs) ? TxnId.NONE : txnIdGen.next(rs)); // emptyable or range
TxnId shardOnlyAppliedOrInvalidatedBefore = emptyGen.next(rs) ? TxnId.NONE : txnIdGen.next(rs); // emptyable or range
TxnId shardAppliedOrInvalidatedBefore = TxnId.nonNullOrMin(locallyAppliedOrInvalidatedBefore, TxnId.nonNullOrMin(shardOnlyAppliedOrInvalidatedBefore, emptyGen.next(rs) ? TxnId.NONE : txnIdGen.next(rs))); // emptyable or range
TxnId gcBefore = TxnId.nonNullOrMin(shardAppliedOrInvalidatedBefore, emptyGen.next(rs) ? TxnId.NONE : txnIdGen.next(rs)); // emptyable or range
TxnId bootstrappedAt = txnIdGen.next(rs);
Timestamp staleUntilAtLeast = emptyGen.next(rs) ? null : txnIdGen.next(rs); // nullable
@ -469,7 +469,7 @@ public class AccordGenerators
public static Gen<RedundantBefore> redundantBefore(IPartitioner partitioner)
{
Gen<Ranges> rangeGen = rangesArbitrary(partitioner);
Gen<TxnId> txnIdGen = AccordGens.txnIds(Gens.pick(Txn.Kind.SyncPoint, Txn.Kind.ExclusiveSyncPoint), ignore -> Routable.Domain.Range);
Gen<TxnId> txnIdGen = AccordGens.txnIds(Gens.pick(Txn.Kind.ExclusiveSyncPoint), ignore -> Routable.Domain.Range);
BiFunction<RandomSource, Range, RedundantBefore.Entry> entryGen = (rs, range) -> redundantBeforeEntry(Gens.bools().all(), i -> range, txnIdGen).next(rs);
return AccordGens.redundantBefore(rangeGen, entryGen);
}
@ -477,7 +477,7 @@ public class AccordGenerators
public static Gen<DurableBefore> durableBeforeGen(IPartitioner partitioner)
{
Gen<Ranges> rangeGen = rangesArbitrary(partitioner);
Gen<TxnId> txnIdGen = AccordGens.txnIds(Gens.pick(Txn.Kind.SyncPoint, Txn.Kind.ExclusiveSyncPoint), ignore -> Routable.Domain.Range);
Gen<TxnId> txnIdGen = AccordGens.txnIds(Gens.pick(Txn.Kind.ExclusiveSyncPoint), ignore -> Routable.Domain.Range);
return (rs) -> {
Ranges ranges = rangeGen.next(rs);