Split AsyncChain and AsyncResult; normalise AsyncResult with C* Future

patch by Benedict; reviewed by Alex Petrov for CASSANDRA-20906
This commit is contained in:
Benedict Elliott Smith 2025-09-07 11:08:46 +01:00
parent c7de33c597
commit 0b2012826a
39 changed files with 762 additions and 346 deletions

4
.gitmodules vendored
View File

@ -1,4 +1,4 @@
[submodule "modules/accord"] [submodule "modules/accord"]
path = modules/accord path = modules/accord
url = https://github.com/belliottsmith/cassandra-accord.git url = https://github.com/apache/cassandra-accord.git
branch = 20905-ssverifier branch = trunk

@ -1 +1 @@
Subproject commit ff4119fa45ad9700cfa94cd50b5cd4af9dd0b7a4 Subproject commit 78b84b08e13530722cb785a3b748fd2075c1c449

View File

@ -229,7 +229,7 @@ public class CassandraStreamReceiver implements StreamReceiver
long deadlineNanos = startedAtNanos + timeoutNanos; long deadlineNanos = startedAtNanos + timeoutNanos;
// TODO (expected): use the source bounds for the streams to avoid waiting unnecessarily long // TODO (expected): use the source bounds for the streams to avoid waiting unnecessarily long
AccordService.getBlocking(accordService.maxConflict(accordRanges) AccordService.getBlocking(accordService.maxConflict(accordRanges)
.flatMap(min -> accordService.sync("[Stream #" + session.planId() + ']', min, accordRanges, null, Self, NoRemote, timeoutNanos, NANOSECONDS)) .flatMap(min -> accordService.sync("[Stream #" + session.planId() + ']', min, accordRanges, null, Self, NoRemote, timeoutNanos, NANOSECONDS).chain())
, accordRanges, new LatencyRequestBookkeeping(cfs.metric.accordPostStreamRepair), startedAtNanos, deadlineNanos); , accordRanges, new LatencyRequestBookkeeping(cfs.metric.accordPostStreamRepair), startedAtNanos, deadlineNanos);
} }

View File

@ -146,7 +146,6 @@ import static accord.local.RedundantStatus.Property.LOCALLY_WITNESSED;
import static accord.local.RedundantStatus.Property.QUORUM_APPLIED; import static accord.local.RedundantStatus.Property.QUORUM_APPLIED;
import static accord.local.RedundantStatus.Property.PRE_BOOTSTRAP; import static accord.local.RedundantStatus.Property.PRE_BOOTSTRAP;
import static accord.local.RedundantStatus.Property.SHARD_APPLIED; import static accord.local.RedundantStatus.Property.SHARD_APPLIED;
import static accord.utils.async.AsyncChains.getBlockingAndRethrow;
import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.lang.String.format; import static java.lang.String.format;
import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS;
@ -368,14 +367,14 @@ public class AccordDebugKeyspace extends VirtualKeyspace
List<Entry> cfks = new CopyOnWriteArrayList<>(); List<Entry> cfks = new CopyOnWriteArrayList<>();
PreLoadContext context = PreLoadContext.contextFor(key, LoadKeys.SYNC, LoadKeysFor.READ_WRITE, "commands_for_key table query"); PreLoadContext context = PreLoadContext.contextFor(key, LoadKeys.SYNC, LoadKeysFor.READ_WRITE, "commands_for_key table query");
CommandStores commandStores = AccordService.instance().node().commandStores(); CommandStores commandStores = AccordService.instance().node().commandStores();
getBlockingAndRethrow(commandStores.forEach(context, key, Long.MIN_VALUE, Long.MAX_VALUE, safeStore -> { AccordService.getBlocking(commandStores.forEach(context, key, Long.MIN_VALUE, Long.MAX_VALUE, safeStore -> {
SafeCommandsForKey safeCfk = safeStore.get(key); SafeCommandsForKey safeCfk = safeStore.get(key);
CommandsForKey cfk = safeCfk.current(); CommandsForKey cfk = safeCfk.current();
if (cfk == null) if (cfk == null)
return; return;
cfks.add(new Entry(safeStore.commandStore().id(), cfk)); cfks.add(new Entry(safeStore.commandStore().id(), cfk));
}).beginAsResult()); }));
if (cfks.isEmpty()) if (cfks.isEmpty())
return null; return null;
@ -478,7 +477,7 @@ public class AccordDebugKeyspace extends VirtualKeyspace
List<Entry> cfks = new CopyOnWriteArrayList<>(); List<Entry> cfks = new CopyOnWriteArrayList<>();
PreLoadContext context = PreLoadContext.contextFor(key, LoadKeys.SYNC, LoadKeysFor.READ_WRITE, "commands_for_key_unmanaged table query"); PreLoadContext context = PreLoadContext.contextFor(key, LoadKeys.SYNC, LoadKeysFor.READ_WRITE, "commands_for_key_unmanaged table query");
CommandStores commandStores = AccordService.instance().node().commandStores(); CommandStores commandStores = AccordService.instance().node().commandStores();
getBlockingAndRethrow(commandStores.forEach(context, key, Long.MIN_VALUE, Long.MAX_VALUE, safeStore -> { AccordService.getBlocking(commandStores.forEach(context, key, Long.MIN_VALUE, Long.MAX_VALUE, safeStore -> {
SafeCommandsForKey safeCfk = safeStore.get(key); SafeCommandsForKey safeCfk = safeStore.get(key);
CommandsForKey cfk = safeCfk.current(); CommandsForKey cfk = safeCfk.current();
if (cfk == null) if (cfk == null)
@ -1469,7 +1468,7 @@ public class AccordDebugKeyspace extends VirtualKeyspace
{ {
consumer.accept(command.route(), result); consumer.accept(command.route(), result);
} }
return result; return result.chain();
}); });
} }
@ -1504,12 +1503,10 @@ public class AccordDebugKeyspace extends VirtualKeyspace
private void run(TxnId txnId, int commandStoreId, Function<SafeCommandStore, AsyncChain<Void>> apply) private void run(TxnId txnId, int commandStoreId, Function<SafeCommandStore, AsyncChain<Void>> apply)
{ {
AccordService accord = (AccordService) AccordService.instance(); AccordService accord = (AccordService) AccordService.instance();
AsyncChains.awaitUninterruptibly(accord.node() AccordService.getBlocking(accord.node()
.commandStores() .commandStores()
.forId(commandStoreId) .forId(commandStoreId)
.submit(PreLoadContext.contextFor(txnId, TXN_OPS), apply) .chain(PreLoadContext.contextFor(txnId, TXN_OPS), apply));
.flatMap(i -> i)
.beginAsResult());
} }
private void cleanup(TxnId txnId, int commandStoreId, Cleanup cleanup) private void cleanup(TxnId txnId, int commandStoreId, Cleanup cleanup)

View File

@ -22,7 +22,6 @@ import java.util.List;
import java.util.NavigableMap; import java.util.NavigableMap;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
@ -38,6 +37,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import accord.api.Agent; import accord.api.Agent;
import accord.api.AsyncExecutor;
import accord.api.DataStore; import accord.api.DataStore;
import accord.api.Journal; import accord.api.Journal;
import accord.api.LocalListeners; import accord.api.LocalListeners;
@ -247,7 +247,7 @@ public class AccordCommandStore extends CommandStore
// TODO (desired): we use this for executing callbacks with mutual exclusivity, // TODO (desired): we use this for executing callbacks with mutual exclusivity,
// but we don't need to block the actual CommandStore - could quite easily // but we don't need to block the actual CommandStore - could quite easily
// inflate a separate queue dynamically in AccordExecutor // inflate a separate queue dynamically in AccordExecutor
public Executor taskExecutor() public AsyncExecutor taskExecutor()
{ {
return exclusiveExecutor; return exclusiveExecutor;
} }
@ -336,25 +336,30 @@ public class AccordCommandStore extends CommandStore
return lastSystemTimestampMicros; return lastSystemTimestampMicros;
} }
@Override @Override
public <T> AsyncChain<T> build(PreLoadContext loadCtx, Function<? super SafeCommandStore, T> function) public <T> AsyncChain<T> chain(PreLoadContext loadCtx, Function<? super SafeCommandStore, T> function)
{ {
return AccordTask.create(this, loadCtx, function).chain(); return AccordTask.create(this, loadCtx, function).chain();
} }
@Override @Override
public <T> AsyncChain<T> build(Callable<T> task) public AsyncChain<Void> chain(PreLoadContext preLoadContext, Consumer<? super SafeCommandStore> consumer)
{
return AsyncChains.ofCallable(taskExecutor(), task);
}
@Override
public AsyncChain<Void> build(PreLoadContext preLoadContext, Consumer<? super SafeCommandStore> consumer)
{ {
return AccordTask.create(this, preLoadContext, consumer).chain(); return AccordTask.create(this, preLoadContext, consumer).chain();
} }
public AccordSafeCommandStore begin(AccordTask<?> operation, @Override
@Nullable CommandsForRanges commandsForRanges) public <T> AsyncChain<T> chain(Callable<T> call)
{
return taskExecutor().chain(call);
}
@Override
public void execute(Runnable run)
{
taskExecutor().execute(run);
}
public AccordSafeCommandStore begin(AccordTask<?> operation, @Nullable CommandsForRanges commandsForRanges)
{ {
require(current == null); require(current == null);
current = AccordSafeCommandStore.create(operation, commandsForRanges, this); current = AccordSafeCommandStore.create(operation, commandsForRanges, this);
@ -495,7 +500,7 @@ public class AccordCommandStore extends CommandStore
} }
} }
ready.begin((success, fail) -> { ready.invoke((success, fail) -> {
if (fail != null) if (fail != null)
{ {
logger.error("{}: failed to ensure durability of {} ({})", this, ranges, reportId, fail); logger.error("{}: failed to ensure durability of {} ({})", this, ranges, reportId, fail);
@ -541,7 +546,7 @@ public class AccordCommandStore extends CommandStore
if (onlyNonDurable && !maybeShouldReplay(txnId)) if (onlyNonDurable && !maybeShouldReplay(txnId))
return AsyncChains.success(null); return AsyncChains.success(null);
return store.submit(PreLoadContext.contextFor(txnId, "Replay"), safeStore -> { return store.chain(PreLoadContext.contextFor(txnId, "Replay"), safeStore -> {
if (onlyNonDurable && !shouldReplay(txnId, safeStore.unsafeGet(txnId).current().participants())) if (onlyNonDurable && !shouldReplay(txnId, safeStore.unsafeGet(txnId).current().participants()))
return null; return null;

View File

@ -652,7 +652,7 @@ public class AccordConfigurationService extends AbstractConfigurationService<Acc
public Future<Void> unsafeLocalSyncNotified(long epoch) public Future<Void> unsafeLocalSyncNotified(long epoch)
{ {
AsyncPromise<Void> promise = new AsyncPromise<>(); AsyncPromise<Void> promise = new AsyncPromise<>();
getOrCreateEpochState(epoch).localSyncNotified().begin((result, failure) -> { getOrCreateEpochState(epoch).localSyncNotified().invoke((result, failure) -> {
if (failure != null) promise.tryFailure(failure); if (failure != null) promise.tryFailure(failure);
else promise.trySuccess(result); else promise.trySuccess(result);
}); });

View File

@ -56,6 +56,10 @@ import accord.utils.QuintConsumer;
import accord.utils.TriConsumer; import accord.utils.TriConsumer;
import accord.utils.TriFunction; import accord.utils.TriFunction;
import accord.utils.UnhandledEnum; import accord.utils.UnhandledEnum;
import accord.utils.async.AsyncCallbacks.CallAndCallback;
import accord.utils.async.AsyncCallbacks.FlatCallAndCallback;
import accord.utils.async.AsyncCallbacks.RunAndCallback;
import accord.utils.async.AsyncCallbacks.RunOrFail;
import accord.utils.async.AsyncChain; import accord.utils.async.AsyncChain;
import accord.utils.async.AsyncChains; import accord.utils.async.AsyncChains;
import accord.utils.async.Cancellable; import accord.utils.async.Cancellable;
@ -430,16 +434,10 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
} }
@Override @Override
public <T> AsyncChain<T> build(Callable<T> task) public Cancellable execute(RunOrFail runOrFail)
{ {
return new AsyncChains.Head<>() PlainChain submit = new PlainChain(runOrFail);
{ return submit(submit);
@Override
protected Cancellable start(BiConsumer<? super T, Throwable> callback)
{
return submit(new PlainChain<>(task, callback, null));
}
};
} }
public <T> AsyncChain<T> buildDebuggable(Callable<T> task, Object describe) public <T> AsyncChain<T> buildDebuggable(Callable<T> task, Object describe)
@ -449,7 +447,7 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
@Override @Override
protected Cancellable start(BiConsumer<? super T, Throwable> callback) protected Cancellable start(BiConsumer<? super T, Throwable> callback)
{ {
return submit(new DebuggableChain<>(task, callback, null, describe)); return submit(new DebuggableChain(new CallAndCallback<>(task, callback), null, 0, describe));
} }
}; };
} }
@ -581,7 +579,7 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
void submitExclusive(Runnable runnable) void submitExclusive(Runnable runnable)
{ {
submitPlainExclusive(new PlainRunnable(null, runnable, null)); submitPlainExclusive(new PlainRunnable(null, runnable));
} }
private void submitPlainExclusive(Plain task) private void submitPlainExclusive(Plain task)
@ -774,14 +772,14 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
public Future<?> submit(Runnable run) public Future<?> submit(Runnable run)
{ {
PlainRunnable task = new PlainRunnable(new AsyncPromise<>(), run, null); PlainRunnable task = new PlainRunnable(new AsyncPromise<>(), run);
submit(task); submit(task);
return task.result; return task.result;
} }
public void execute(Runnable command) public void execute(Runnable command)
{ {
submit(new PlainRunnable(null, command, null)); submit(new PlainRunnable(null, command));
} }
private Cancellable submit(Plain task) private Cancellable submit(Plain task)
@ -804,11 +802,6 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
} }
} }
public void execute(Runnable command, AccordCommandStore commandStore)
{
submit(new PlainRunnable(null, command, commandStore.exclusiveExecutor));
}
@Override @Override
public void setCapacity(long bytes) public void setCapacity(long bytes)
{ {
@ -1107,37 +1100,79 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
} }
@Override @Override
public <T> AsyncChain<T> build(Callable<T> call) public AsyncChain<Void> chain(Runnable run)
{ {
int position = inExecutor() && task != null ? task.queuePosition : 0; int position = inheritQueuePosition();
return new AsyncChains.Head<>() return new AsyncChains.Head<>()
{ {
@Override @Override
protected Cancellable start(BiConsumer<? super T, Throwable> callback) protected Cancellable start(BiConsumer<? super Void, Throwable> callback)
{ {
PlainChain<T> submit = new PlainChain<>(call, callback, SequentialExecutor.this); return execute(new RunAndCallback(run, callback), position);
submit.queuePosition = position;
return AccordExecutor.this.submit(submit);
} }
}; };
} }
@Override
public <T> AsyncChain<T> chain(Callable<T> call)
{
int position = inheritQueuePosition();
return new AsyncChains.Head<>()
{
@Override
protected Cancellable start(BiConsumer<? super T, Throwable> callback)
{
return execute(new CallAndCallback<>(call, callback), position);
}
};
}
@Override
public <T> AsyncChain<T> flatChain(Callable<? extends AsyncChain<T>> call)
{
int position = inheritQueuePosition();
return new AsyncChains.Head<>()
{
@Override
protected Cancellable start(BiConsumer<? super T, Throwable> callback)
{
return execute(new FlatCallAndCallback<>(call, callback), position);
}
};
}
@Override
public Cancellable execute(RunOrFail runOrFail)
{
return execute(runOrFail, inheritQueuePosition());
}
private int inheritQueuePosition()
{
return inExecutor() && task != null ? task.queuePosition : 0;
}
private Cancellable execute(RunOrFail runOrFail, int queuePosition)
{
PlainChain submit = new PlainChain(runOrFail, SequentialExecutor.this, queuePosition);
return AccordExecutor.this.submit(submit);
}
@Override @Override
public void execute(Runnable run) public void execute(Runnable run)
{ {
PlainRunnable submit = new PlainRunnable(null, run, this); PlainRunnable submit = new PlainRunnable(null, run, this, inheritQueuePosition());
if (inExecutor() && this.task != null)
submit.queuePosition = this.task.queuePosition;
AccordExecutor.this.submit(submit); AccordExecutor.this.submit(submit);
} }
@Override @Override
public void maybeExecuteImmediately(Runnable run) public boolean tryExecuteImmediately(Runnable run)
{ {
Thread self = Thread.currentThread(); Thread self = Thread.currentThread();
Thread owner = this.owner; Thread owner = this.owner;
if (owner == self || (owner == null && ownerUpdater.compareAndSet(this, null, self))) if (owner != null ? owner != self : !ownerUpdater.compareAndSet(this, null, self))
{ return false;
try { run.run(); } try { run.run(); }
catch (Throwable t) { agent.onUncaughtException(t); } catch (Throwable t) { agent.onUncaughtException(t); }
finally finally
@ -1149,11 +1184,7 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
LockSupport.unpark(waiting); LockSupport.unpark(waiting);
} }
} }
} return true;
else
{
execute(run);
}
} }
} }
@ -1287,14 +1318,20 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
PlainRunnable(Runnable run) PlainRunnable(Runnable run)
{ {
this(null, run, null); this(null, run);
} }
PlainRunnable(AsyncPromise<Void> result, Runnable run, @Nullable SequentialExecutor executor) PlainRunnable(AsyncPromise<Void> result, Runnable run)
{
this(result, run, null, 0);
}
PlainRunnable(AsyncPromise<Void> result, Runnable run, @Nullable SequentialExecutor executor, int queuePosition)
{ {
this.result = result; this.result = result;
this.run = run; this.run = run;
this.executor = executor; this.executor = executor;
this.queuePosition = queuePosition;
} }
@Override @Override
@ -1514,17 +1551,21 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
} }
} }
class PlainChain<T> extends Plain class PlainChain extends Plain
{ {
final Callable<T> call; final RunOrFail runOrFail;
final BiConsumer<? super T, Throwable> callback;
final @Nullable SequentialExecutor executor; final @Nullable SequentialExecutor executor;
PlainChain(Callable<T> call, BiConsumer<? super T, Throwable> callback, @Nullable SequentialExecutor executor) PlainChain(RunOrFail runOrFail)
{ {
this.call = call; this(runOrFail, null, 0);
this.callback = callback; }
PlainChain(RunOrFail runOrFail, @Nullable SequentialExecutor executor, int queuePosition)
{
this.runOrFail = runOrFail;
this.executor = executor; this.executor = executor;
this.queuePosition = queuePosition;
} }
@Override @Override
@ -1536,23 +1577,14 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
@Override @Override
protected void runInternal() protected void runInternal()
{ {
T success;
try (Closeable close = locals.get()) try (Closeable close = locals.get())
{ {
success = call.call(); runOrFail.run();
}
catch (Throwable t)
{
fail(t);
return;
}
try
{
callback.accept(success, null);
} }
catch (Throwable t) catch (Throwable t)
{ {
agent.onUncaughtException(t); agent.onUncaughtException(t);
return;
} }
} }
@ -1561,7 +1593,7 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
{ {
try try
{ {
callback.accept(null, fail); runOrFail.fail(fail);
} }
catch (Throwable t) catch (Throwable t)
{ {
@ -1571,15 +1603,15 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
} }
} }
class DebuggableChain<T> extends PlainChain<T> implements DebuggableTask class DebuggableChain extends PlainChain implements DebuggableTask
{ {
final long createdAtNanos; final long createdAtNanos;
long startedAtNanos; long startedAtNanos;
final Object describe; final Object describe;
DebuggableChain(Callable<T> call, BiConsumer<? super T, Throwable> callback, @Nullable SequentialExecutor executor, Object describe) DebuggableChain(RunOrFail runOrFail, @Nullable SequentialExecutor executor, int queuePosition, Object describe)
{ {
super(call, callback, executor); super(runOrFail, executor, queuePosition);
this.createdAtNanos = MonotonicClock.Global.approxTime.now(); this.createdAtNanos = MonotonicClock.Global.approxTime.now();
this.describe = Invariants.nonNull(describe); this.describe = Invariants.nonNull(describe);
} }

View File

@ -29,6 +29,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -41,6 +42,7 @@ import com.google.common.primitives.Ints;
import org.apache.cassandra.metrics.AccordReplicaMetrics; import org.apache.cassandra.metrics.AccordReplicaMetrics;
import org.apache.cassandra.service.accord.api.AccordViolationHandler; import org.apache.cassandra.service.accord.api.AccordViolationHandler;
import org.apache.cassandra.utils.Clock; import org.apache.cassandra.utils.Clock;
import org.apache.cassandra.utils.concurrent.AsyncFuture;
import org.apache.cassandra.utils.concurrent.ImmediateFuture; import org.apache.cassandra.utils.concurrent.ImmediateFuture;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -133,7 +135,6 @@ import org.apache.cassandra.utils.ExecutorUtils;
import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.concurrent.AsyncPromise; import org.apache.cassandra.utils.concurrent.AsyncPromise;
import org.apache.cassandra.utils.concurrent.Future; import org.apache.cassandra.utils.concurrent.Future;
import org.apache.cassandra.utils.concurrent.Promise;
import org.apache.cassandra.utils.concurrent.UncheckedInterruptedException; import org.apache.cassandra.utils.concurrent.UncheckedInterruptedException;
import static accord.api.Journal.TopologyUpdate; import static accord.api.Journal.TopologyUpdate;
@ -553,7 +554,7 @@ public class AccordService implements IAccordService, Shutdownable
} }
@Override @Override
public AsyncChain<Void> sync(Object requestedBy, Timestamp minBound, Ranges ranges, @Nullable Collection<Id> include, DurabilityService.SyncLocal syncLocal, DurabilityService.SyncRemote syncRemote, long timeout, TimeUnit timeoutUnits) public AsyncResult<Void> sync(Object requestedBy, Timestamp minBound, Ranges ranges, @Nullable Collection<Id> include, DurabilityService.SyncLocal syncLocal, DurabilityService.SyncRemote syncRemote, long timeout, TimeUnit timeoutUnits)
{ {
return node.durability().sync(requestedBy, ExclusiveSyncPoint, minBound, ranges, include, syncLocal, syncRemote, timeout, timeoutUnits); return node.durability().sync(requestedBy, ExclusiveSyncPoint, minBound, ranges, include, syncLocal, syncRemote, timeout, timeoutUnits);
} }
@ -580,8 +581,8 @@ public class AccordService implements IAccordService, Shutdownable
return node.withEpochAtLeast(txnId.epoch(), null, () -> { return node.withEpochAtLeast(txnId.epoch(), null, () -> {
Txn txn = new Txn.InMemory(Write, keys, TxnRead.createNoOpRead(keys), TxnQuery.UNSAFE_EMPTY, TxnUpdate.empty(), new TableMetadatasAndKeys(TableMetadatas.none(), keys)); Txn txn = new Txn.InMemory(Write, keys, TxnRead.createNoOpRead(keys), TxnQuery.UNSAFE_EMPTY, TxnUpdate.empty(), new TableMetadatasAndKeys(TableMetadatas.none(), keys));
return CoordinateTransaction.coordinate(node, route, txnId, txn) return CoordinateTransaction.coordinate(node, route, txnId, txn)
.map(ignore -> (Void) null).beginAsResult(); .mapToNull();
}).beginAsResult(); });
} }
@Override @Override
@ -590,6 +591,53 @@ public class AccordService implements IAccordService, Shutdownable
return CoordinateMaxConflict.maxConflict(node, ranges); return CoordinateMaxConflict.maxConflict(node, ranges);
} }
static class AsyncFutureCallback<V> extends AsyncFuture<V> implements BiConsumer<V, Throwable>
{
@Override
public void accept(V v, Throwable fail)
{
if (fail == null) trySuccess(v);
else tryFailure(fail);
}
}
public static <V> Future<V> toFuture(AsyncChain<V> chain)
{
AsyncFutureCallback<V> future = new AsyncFutureCallback<>();
chain.begin(future);
return future;
}
public static <V> Future<V> toFuture(AsyncResult<V> result)
{
if (result instanceof Future<?>)
return (Future<V>) result;
AsyncPromise<V> promise = new AsyncPromise<>();
result.invoke((success, failure) -> {
if (failure == null) promise.trySuccess(success);
else promise.tryFailure(failure);
});
return promise;
}
public static <V> V getBlocking(AsyncChain<V> async)
{
return syncAndRethrow(toFuture(async)).getNow();
}
public static <V> V getBlocking(AsyncResult<V> async)
{
return syncAndRethrow(toFuture(async)).getNow();
}
private static <V> Future<V> syncAndRethrow(Future<V> future)
{
future.syncThrowUncheckedOnInterrupt()
.rethrowIfFailed();
return future;
}
public static <V> V getBlocking(AsyncChain<V> async, Seekables<?, ?> keysOrRanges, RequestBookkeeping bookkeeping, long startedAt, long deadline, boolean isTxnRequest) public static <V> V getBlocking(AsyncChain<V> async, Seekables<?, ?> keysOrRanges, RequestBookkeeping bookkeeping, long startedAt, long deadline, boolean isTxnRequest)
{ {
return getBlocking(async, null, keysOrRanges, bookkeeping, startedAt, deadline, isTxnRequest); return getBlocking(async, null, keysOrRanges, bookkeeping, startedAt, deadline, isTxnRequest);
@ -602,26 +650,23 @@ public class AccordService implements IAccordService, Shutdownable
return result.awaitAndGet(); return result.awaitAndGet();
} }
public static <V> V getBlocking(AsyncResult<V> async, Seekables<?, ?> keysOrRanges, RequestBookkeeping bookkeeping, long startedAt, long deadline, boolean isTxnRequest)
{
return getBlocking(async, null, keysOrRanges, bookkeeping, startedAt, deadline, isTxnRequest);
}
public static <V> V getBlocking(AsyncResult<V> async, @Nullable TxnId txnId, Seekables<?, ?> keysOrRanges, RequestBookkeeping bookkeeping, long startedAt, long deadline, boolean isTxnRequest)
{
AccordResult<V> result = new AccordResult<>(txnId, keysOrRanges, bookkeeping, startedAt, deadline, isTxnRequest);
async.invoke(result);
return result.awaitAndGet();
}
public static <V> V getBlocking(AsyncChain<V> async, Seekables<?, ?> keysOrRanges, RequestBookkeeping bookkeeping, long startedAt, long deadline) public static <V> V getBlocking(AsyncChain<V> async, Seekables<?, ?> keysOrRanges, RequestBookkeeping bookkeeping, long startedAt, long deadline)
{ {
return getBlocking(async, keysOrRanges, bookkeeping, startedAt, deadline, false); return getBlocking(async, keysOrRanges, bookkeeping, startedAt, deadline, false);
} }
public static <V> V getBlocking(AsyncChain<V> async)
{
return asPromise(async).syncUninterruptibly().getNow();
}
public static <V> Promise<V> asPromise(AsyncChain<V> async)
{
AsyncPromise<V> promise = new AsyncPromise<>();
async.begin((result, failure) -> {
if (failure == null) promise.trySuccess(result);
else promise.tryFailure(failure);
});
return promise;
}
public static Keys intersecting(Keys keys) public static Keys intersecting(Keys keys)
{ {
if (keys.isEmpty()) if (keys.isEmpty())
@ -719,7 +764,7 @@ public class AccordService implements IAccordService, Shutdownable
bookkeeping.metrics.keySize.update(txn.keys().size()); bookkeeping.metrics.keySize.update(txn.keys().size());
long deadlineNanos = requestTime.computeDeadline(timeout); long deadlineNanos = requestTime.computeDeadline(timeout);
AccordResult<TxnResult> result = new AccordResult<>(txnId, txn.keys(), bookkeeping, requestTime.startedAtNanos(), deadlineNanos, true); AccordResult<TxnResult> result = new AccordResult<>(txnId, txn.keys(), bookkeeping, requestTime.startedAtNanos(), deadlineNanos, true);
((AsyncResult)node.coordinate(txnId, txn, minEpoch, deadlineNanos)).begin(result); node.coordinate(txnId, txn, minEpoch, deadlineNanos).begin((BiConsumer) result);
return result; return result;
} }
@ -754,7 +799,7 @@ public class AccordService implements IAccordService, Shutdownable
} }
Ready ready = new Ready(); Ready ready = new Ready();
AccordCommandStores commandStores = (AccordCommandStores) node.commandStores(); AccordCommandStores commandStores = (AccordCommandStores) node.commandStores();
AsyncChains.getBlockingAndRethrow(commandStores.forEach((PreLoadContext.Empty)() -> "Flush Caches", safeStore -> { getBlocking(commandStores.forEach((PreLoadContext.Empty)() -> "Flush Caches", safeStore -> {
AccordCommandStore commandStore = (AccordCommandStore)safeStore.commandStore(); AccordCommandStore commandStore = (AccordCommandStore)safeStore.commandStore();
try (AccordCommandStore.ExclusiveCaches caches = commandStore.lockCaches()) try (AccordCommandStore.ExclusiveCaches caches = commandStore.lockCaches())
{ {
@ -769,7 +814,7 @@ public class AccordService implements IAccordService, Shutdownable
})); }));
ready.decrement(); ready.decrement();
AsyncPromise<Void> result = new AsyncPromise<>(); AsyncPromise<Void> result = new AsyncPromise<>();
ready.begin((success, fail) -> { ready.invoke((success, fail) -> {
if (fail != null) result.tryFailure(fail); if (fail != null) result.tryFailure(fail);
else result.trySuccess(null); else result.trySuccess(null);
}); });
@ -839,19 +884,7 @@ public class AccordService implements IAccordService, Shutdownable
@Override @Override
public List<CommandStoreTxnBlockedGraph> debugTxnBlockedGraph(TxnId txnId) public List<CommandStoreTxnBlockedGraph> debugTxnBlockedGraph(TxnId txnId)
{ {
AsyncChain<List<CommandStoreTxnBlockedGraph>> states = loadDebug(txnId); return getBlocking(loadDebug(txnId));
try
{
return AsyncChains.getBlocking(states);
}
catch (InterruptedException e)
{
throw new UncheckedInterruptedException(e);
}
catch (ExecutionException e)
{
throw new RuntimeException(e.getCause());
}
} }
public AsyncChain<List<CommandStoreTxnBlockedGraph>> loadDebug(TxnId original) public AsyncChain<List<CommandStoreTxnBlockedGraph>> loadDebug(TxnId original)
@ -862,11 +895,11 @@ public class AccordService implements IAccordService, Shutdownable
int[] ids = commandStores.ids(); int[] ids = commandStores.ids();
List<AsyncChain<CommandStoreTxnBlockedGraph>> chains = new ArrayList<>(ids.length); List<AsyncChain<CommandStoreTxnBlockedGraph>> chains = new ArrayList<>(ids.length);
for (int id : ids) for (int id : ids)
chains.add(loadDebug(original, commandStores.forId(id))); chains.add(loadDebug(original, commandStores.forId(id)).chain());
return AsyncChains.allOf(chains); return AsyncChains.allOf(chains);
} }
private AsyncChain<CommandStoreTxnBlockedGraph> loadDebug(TxnId txnId, CommandStore store) private AsyncResult<CommandStoreTxnBlockedGraph> loadDebug(TxnId txnId, CommandStore store)
{ {
CommandStoreTxnBlockedGraph.Builder state = new CommandStoreTxnBlockedGraph.Builder(store.id()); CommandStoreTxnBlockedGraph.Builder state = new CommandStoreTxnBlockedGraph.Builder(store.id());
populateAsync(state, store, txnId); populateAsync(state, store, txnId);
@ -995,7 +1028,7 @@ public class AccordService implements IAccordService, Shutdownable
@Override @Override
public void ensureMinHlc(long minHlc) public void ensureMinHlc(long minHlc)
{ {
asPromise(node.updateMinHlc(minHlc >= 0 ? minHlc : 0)).syncUninterruptibly(); toFuture(node.updateMinHlc(minHlc >= 0 ? minHlc : 0)).syncUninterruptibly();
} }
public AccordJournal journal() public AccordJournal journal()
@ -1006,7 +1039,7 @@ public class AccordService implements IAccordService, Shutdownable
@Override @Override
public Future<Void> epochReady(Epoch epoch) public Future<Void> epochReady(Epoch epoch)
{ {
return asPromise(configService.epochReady(epoch.getEpoch())); return toFuture(configService.epochReady(epoch.getEpoch()));
} }
@Override @Override

View File

@ -28,6 +28,7 @@ import java.util.function.BiConsumer;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import accord.utils.async.AsyncResult;
import org.apache.cassandra.tcm.ClusterMetadata; import org.apache.cassandra.tcm.ClusterMetadata;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -80,7 +81,7 @@ public interface IAccordService
IVerbHandler<? extends Request> requestHandler(); IVerbHandler<? extends Request> requestHandler();
IVerbHandler<? extends Reply> responseHandler(); IVerbHandler<? extends Reply> responseHandler();
AsyncChain<Void> sync(Object requestedBy, @Nullable Timestamp minBound, Ranges ranges, @Nullable Collection<Id> include, SyncLocal syncLocal, SyncRemote syncRemote, long timeout, TimeUnit timeoutUnits); AsyncResult<Void> sync(Object requestedBy, @Nullable Timestamp minBound, Ranges ranges, @Nullable Collection<Id> include, SyncLocal syncLocal, SyncRemote syncRemote, long timeout, TimeUnit timeoutUnits);
AsyncChain<Void> sync(@Nullable Timestamp minBound, Keys keys, SyncLocal syncLocal, SyncRemote syncRemote); AsyncChain<Void> sync(@Nullable Timestamp minBound, Keys keys, SyncLocal syncLocal, SyncRemote syncRemote);
AsyncChain<Timestamp> maxConflict(Ranges ranges); AsyncChain<Timestamp> maxConflict(Ranges ranges);
@ -214,7 +215,7 @@ public interface IAccordService
} }
@Override @Override
public AsyncChain<Void> sync(Object requestedBy, @Nullable Timestamp onOrAfter, Ranges ranges, @Nullable Collection<Id> include, SyncLocal syncLocal, SyncRemote syncRemote, long timeout, TimeUnit timeoutUnits) public AsyncResult<Void> sync(Object requestedBy, @Nullable Timestamp onOrAfter, Ranges ranges, @Nullable Collection<Id> include, SyncLocal syncLocal, SyncRemote syncRemote, long timeout, TimeUnit timeoutUnits)
{ {
throw new UnsupportedOperationException("No accord transaction should be executed when accord.enabled = false in cassandra.yaml"); throw new UnsupportedOperationException("No accord transaction should be executed when accord.enabled = false in cassandra.yaml");
} }
@ -410,7 +411,7 @@ public interface IAccordService
} }
@Override @Override
public AsyncChain<Void> sync(Object requestedBy, @Nullable Timestamp onOrAfter, Ranges ranges, @Nullable Collection<Id> include, SyncLocal syncLocal, SyncRemote syncRemote, long timeout, TimeUnit timeoutUnits) public AsyncResult<Void> sync(Object requestedBy, @Nullable Timestamp onOrAfter, Ranges ranges, @Nullable Collection<Id> include, SyncLocal syncLocal, SyncRemote syncRemote, long timeout, TimeUnit timeoutUnits)
{ {
return delegate.sync(requestedBy, onOrAfter, ranges, include, syncLocal, syncRemote, timeout, timeoutUnits); return delegate.sync(requestedBy, onOrAfter, ranges, include, syncLocal, syncRemote, timeout, timeoutUnits);
} }

View File

@ -33,11 +33,11 @@ public class ImmediateAsyncExecutor implements AsyncExecutor, BiConsumer<Object,
public static final ImmediateAsyncExecutor INSTANCE = new ImmediateAsyncExecutor(); public static final ImmediateAsyncExecutor INSTANCE = new ImmediateAsyncExecutor();
@Override @Override
public <T> AsyncChain<T> build(Callable<T> task) public <T> AsyncChain<T> chain(Callable<T> call)
{ {
try try
{ {
return AsyncChains.success(task.call()); return AsyncChains.success(call.call());
} }
catch (Throwable t) catch (Throwable t)
{ {

View File

@ -58,8 +58,7 @@ import accord.utils.SortedList;
import accord.utils.UnhandledEnum; import accord.utils.UnhandledEnum;
import accord.utils.async.AsyncChain; import accord.utils.async.AsyncChain;
import accord.utils.async.AsyncChains; import accord.utils.async.AsyncChains;
import accord.utils.async.AsyncResult; import accord.utils.async.Cancellable;
import accord.utils.async.AsyncResults;
import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.exceptions.RequestTimeoutException; import org.apache.cassandra.exceptions.RequestTimeoutException;
import org.apache.cassandra.metrics.AccordCoordinatorMetrics; import org.apache.cassandra.metrics.AccordCoordinatorMetrics;
@ -392,9 +391,15 @@ public class AccordAgent implements Agent
return AsyncChains.success(staleId); return AsyncChains.success(staleId);
logger.debug("Waiting {} micros for {} to be stale", waitMicros, staleId); logger.debug("Waiting {} micros for {} to be stale", waitMicros, staleId);
AsyncResult.Settable<TxnId> result = AsyncResults.settable(); return new AsyncChains.Head<>()
node.scheduler().once(() -> result.setSuccess(staleId), waitMicros, MICROSECONDS); {
return result; @Override
protected @Nullable Cancellable start(BiConsumer<? super TxnId, Throwable> callback)
{
node.scheduler().once(() -> callback.accept(staleId, null), waitMicros, MICROSECONDS);
return null;
}
};
} }
@Override @Override

View File

@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLongFieldUpdater; import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import accord.api.AsyncExecutor;
import accord.api.Data; import accord.api.Data;
import accord.api.Result; import accord.api.Result;
import accord.coordinate.CoordinationAdapter; import accord.coordinate.CoordinationAdapter;
@ -257,7 +258,7 @@ public class AccordInteropExecution implements ReadCoordinator
} }
Group group = Group.one(command); Group group = Group.one(command);
results.add(AsyncChains.ofCallable(Stage.ACCORD_MIGRATION.executor(), () -> { results.add(AsyncExecutor.chain(Stage.ACCORD_MIGRATION.executor(), () -> {
TxnData result = new TxnData(); TxnData result = new TxnData();
// Enforcing limits is redundant since we only have a group of size 1, but checking anyways // Enforcing limits is redundant since we only have a group of size 1, but checking anyways
// documents the requirement here // documents the requirement here
@ -293,7 +294,7 @@ public class AccordInteropExecution implements ReadCoordinator
// TODO (required): To make migration work we need to validate that the range is all on Accord // TODO (required): To make migration work we need to validate that the range is all on Accord
results.add(AsyncChains.ofCallable(Stage.ACCORD_MIGRATION.executor(), () -> { results.add(AsyncExecutor.chain(Stage.ACCORD_MIGRATION.executor(), () -> {
TxnData result = new TxnData(); TxnData result = new TxnData();
try (PartitionIterator iterator = StorageProxy.getRangeSlice(command, consistencyLevel, this, requestTime)) try (PartitionIterator iterator = StorageProxy.getRangeSlice(command, consistencyLevel, this, requestTime))
{ {
@ -395,7 +396,7 @@ public class AccordInteropExecution implements ReadCoordinator
private AsyncChain<Data> executeUnrecoverableRepairUpdate() private AsyncChain<Data> executeUnrecoverableRepairUpdate()
{ {
return AsyncChains.ofCallable(Stage.ACCORD_MIGRATION.executor(), () -> { return AsyncExecutor.chain(Stage.ACCORD_MIGRATION.executor(), () -> {
UnrecoverableRepairUpdate repairUpdate = (UnrecoverableRepairUpdate)txn.update(); UnrecoverableRepairUpdate repairUpdate = (UnrecoverableRepairUpdate)txn.update();
// TODO (expected): We should send the read in the same message as the commit. This requires refactor ReadData.Kind so that it doesn't specify the ordinal encoding // TODO (expected): We should send the read in the same message as the commit. This requires refactor ReadData.Kind so that it doesn't specify the ordinal encoding
// and can be extended similar to MessageType which allows additional types not from Accord to be added // and can be extended similar to MessageType which allows additional types not from Accord to be added

View File

@ -29,6 +29,7 @@ import javax.annotation.Nullable;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import accord.api.AsyncExecutor;
import accord.api.Data; import accord.api.Data;
import accord.local.Node; import accord.local.Node;
import accord.local.SafeCommandStore; import accord.local.SafeCommandStore;
@ -282,7 +283,7 @@ public class AccordInteropRead extends ReadData
return AsyncChains.success(new LocalReadData(new ArrayList<>(), readCommand)); return AsyncChains.success(new LocalReadData(new ArrayList<>(), readCommand));
ReadCommand submit = readCommand.withTransactionalSettings(TxnNamedRead.readsWithoutReconciliation(txnRead.cassandraConsistencyLevel()), nowInSeconds); ReadCommand submit = readCommand.withTransactionalSettings(TxnNamedRead.readsWithoutReconciliation(txnRead.cassandraConsistencyLevel()), nowInSeconds);
return AsyncChains.ofCallable(Stage.READ.executor(), () -> new LocalReadData(key, ReadCommandVerbHandler.instance.doRead(submit, false), command)); return AsyncExecutor.chain(Stage.READ.executor(), () -> new LocalReadData(key, ReadCommandVerbHandler.instance.doRead(submit, false), command));
} }
// This path can have a subrange we have never seen before provided by short read protection or read repair so we need to // This path can have a subrange we have never seen before provided by short read protection or read repair so we need to
@ -297,7 +298,7 @@ public class AccordInteropRead extends ReadData
continue; continue;
ReadCommand submit = TxnNamedRead.commandForSubrange((PartitionRangeReadCommand) command, intersection, txnRead.cassandraConsistencyLevel(), nowInSeconds); ReadCommand submit = TxnNamedRead.commandForSubrange((PartitionRangeReadCommand) command, intersection, txnRead.cassandraConsistencyLevel(), nowInSeconds);
TokenKey routingKey = ((TokenRange)r).start(); TokenKey routingKey = ((TokenRange)r).start();
chains.add(AsyncChains.ofCallable(Stage.READ.executor(), () -> new LocalReadData(routingKey, ReadCommandVerbHandler.instance.doRead(submit, false), command))); chains.add(AsyncExecutor.chain(Stage.READ.executor(), () -> new LocalReadData(routingKey, ReadCommandVerbHandler.instance.doRead(submit, false), command)));
} }
if (chains.isEmpty()) if (chains.isEmpty())

View File

@ -21,6 +21,7 @@ package org.apache.cassandra.service.accord.interop;
import java.io.IOException; import java.io.IOException;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import accord.api.AsyncExecutor;
import accord.api.Data; import accord.api.Data;
import accord.local.Node; import accord.local.Node;
import accord.local.SafeCommandStore; import accord.local.SafeCommandStore;
@ -34,7 +35,6 @@ import accord.primitives.Timestamp;
import accord.primitives.TxnId; import accord.primitives.TxnId;
import accord.topology.Topologies; import accord.topology.Topologies;
import accord.utils.async.AsyncChain; import accord.utils.async.AsyncChain;
import accord.utils.async.AsyncChains;
import org.apache.cassandra.db.Mutation; import org.apache.cassandra.db.Mutation;
import org.apache.cassandra.db.ReadRepairVerbHandler; import org.apache.cassandra.db.ReadRepairVerbHandler;
import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.db.TypeSizes;
@ -110,11 +110,8 @@ public class AccordInteropReadRepair extends ReadData
private static final IVersionedSerializer<Data> noop_data_serializer = new IVersionedSerializer<>() private static final IVersionedSerializer<Data> noop_data_serializer = new IVersionedSerializer<>()
{ {
@Override @Override public void serialize(Data t, DataOutputPlus out, Version version) {}
public void serialize(Data t, DataOutputPlus out, Version version) throws IOException {} @Override public Data deserialize(DataInputPlus in, Version version) { return Data.NOOP_DATA; }
@Override
public Data deserialize(DataInputPlus in, Version version) throws IOException { return Data.NOOP_DATA; }
public long serializedSize(Data t, Version version) { return 0; } public long serializedSize(Data t, Version version) { return 0; }
}; };
@ -148,7 +145,7 @@ public class AccordInteropReadRepair extends ReadData
protected AsyncChain<Data> beginRead(SafeCommandStore safeStore, Timestamp executeAt, PartialTxn txn, Participants<?> execute) protected AsyncChain<Data> beginRead(SafeCommandStore safeStore, Timestamp executeAt, PartialTxn txn, Participants<?> execute)
{ {
// TODO (required): subtract unavailable ranges, either from read or from response (or on coordinator) // TODO (required): subtract unavailable ranges, either from read or from response (or on coordinator)
return AsyncChains.ofCallable(Verb.READ_REPAIR_REQ.stage.executor(), () -> { return AsyncExecutor.chain(Verb.READ_REPAIR_REQ.stage.executor(), () -> {
ReadRepairVerbHandler.instance.applyMutation(mutation); ReadRepairVerbHandler.instance.applyMutation(mutation);
return Data.NOOP_DATA; return Data.NOOP_DATA;
}); });

View File

@ -164,7 +164,7 @@ public class AccordRepair
long timeoutNanos = getAccordRepairTimeoutNanos(); long timeoutNanos = getAccordRepairTimeoutNanos();
long maxHlc = AccordService.getBlocking(service.maxConflict(ranges).flatMap(conflict -> { long maxHlc = AccordService.getBlocking(service.maxConflict(ranges).flatMap(conflict -> {
Timestamp conflictMax = mergeMax(conflict, minForEpoch(this.minEpoch.getEpoch())); Timestamp conflictMax = mergeMax(conflict, minForEpoch(this.minEpoch.getEpoch()));
return service.sync("[repairId #" + repairId + ']', conflictMax, Ranges.of(range), null, NoLocal, syncRemote, timeoutNanos, NANOSECONDS).map(ignored -> conflictMax.hlc()); return service.sync("[repairId #" + repairId + ']', conflictMax, Ranges.of(range), null, NoLocal, syncRemote, timeoutNanos, NANOSECONDS).map(ignored -> conflictMax.hlc()).chain();
}), ranges, bookkeeping, start, start + timeoutNanos); }), ranges, bookkeeping, start, start + timeoutNanos);
waiting = null; waiting = null;

View File

@ -35,7 +35,7 @@ import accord.primitives.Seekable;
import accord.primitives.Timestamp; import accord.primitives.Timestamp;
import accord.utils.Invariants; import accord.utils.Invariants;
import accord.utils.async.AsyncChain; import accord.utils.async.AsyncChain;
import accord.utils.async.AsyncResults; import accord.utils.async.AsyncChains;
import org.apache.cassandra.db.ConsistencyLevel; import org.apache.cassandra.db.ConsistencyLevel;
import org.apache.cassandra.db.PartitionPosition; import org.apache.cassandra.db.PartitionPosition;
import org.apache.cassandra.db.PartitionRangeReadCommand; import org.apache.cassandra.db.PartitionRangeReadCommand;
@ -231,7 +231,7 @@ public class TxnNamedRead extends AbstractParameterisedVersionedSerialized<ReadC
{ {
ReadCommand command = deserialize(tables); ReadCommand command = deserialize(tables);
if (command == null) if (command == null)
return AsyncResults.success(new TxnData()); return AsyncChains.success(new TxnData());
// It's fine for our nowInSeconds to lag slightly our insertion timestamp, as to the user // It's fine for our nowInSeconds to lag slightly our insertion timestamp, as to the user
// this simply looks like the transaction witnessed TTL'd data and the data then expired // this simply looks like the transaction witnessed TTL'd data and the data then expired

View File

@ -27,11 +27,11 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Executor;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import accord.api.AsyncExecutor;
import accord.api.Write; import accord.api.Write;
import accord.local.CommandStore; import accord.local.CommandStore;
import accord.local.SafeCommandStore; import accord.local.SafeCommandStore;
@ -138,13 +138,13 @@ public class TxnWrite extends AbstractKeySorted<TxnWrite.Update> implements Writ
'}'; '}';
} }
public AsyncChain<Void> write(Executor executor, TableMetadatas tables, boolean preserveTimestamps, long timestamp) public AsyncChain<Void> write(AsyncExecutor executor, TableMetadatas tables, boolean preserveTimestamps, long timestamp)
{ {
PartitionUpdate update = deserialize(tables); PartitionUpdate update = deserialize(tables);
if (!preserveTimestamps) if (!preserveTimestamps)
update = new PartitionUpdate.Builder(update, 0).updateAllTimestamp(timestamp).build(); update = new PartitionUpdate.Builder(update, 0).updateAllTimestamp(timestamp).build();
Mutation mutation = new Mutation(update, PotentialTxnConflicts.ALLOW); Mutation mutation = new Mutation(update, PotentialTxnConflicts.ALLOW);
return AsyncChains.ofRunnable(executor, () -> mutation.apply(false, false)); return executor.chain(() -> mutation.apply(false, false));
} }
@Override @Override
@ -479,9 +479,9 @@ public class TxnWrite extends AbstractKeySorted<TxnWrite.Update> implements Writ
return AsyncChains.success(null); return AsyncChains.success(null);
if (results.size() == 1) if (results.size() == 1)
return results.get(0).map(o -> null); return results.get(0).mapToNull();
return AsyncChains.reduce(results, (i1, i2) -> null, (Void)null).map(ignore -> null); return AsyncChains.reduce(results, (i1, i2) -> null, null);
} }
public long estimatedSizeOnHeap() public long estimatedSizeOnHeap()

View File

@ -34,6 +34,7 @@ import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.ListenableFuture; // checkstyle: permit this import import com.google.common.util.concurrent.ListenableFuture; // checkstyle: permit this import
import accord.utils.async.AsyncResult;
import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.internal.ThrowableUtil; import io.netty.util.internal.ThrowableUtil;
import org.apache.cassandra.utils.concurrent.ListenerList.CallbackBiConsumerListener; import org.apache.cassandra.utils.concurrent.ListenerList.CallbackBiConsumerListener;
@ -316,17 +317,6 @@ public abstract class AbstractFuture<V> implements Future<V>
return this; return this;
} }
/**
* Support {@link com.google.common.util.concurrent.Futures#transformAsync(ListenableFuture, AsyncFunction, Executor)} natively
*
* See {@link #addListener(GenericFutureListener)} for ordering semantics.
*/
@Override
public <T> Future<T> map(Function<? super V, ? extends T> mapper)
{
return map(mapper, null);
}
/** /**
* Support more fluid version of {@link com.google.common.util.concurrent.Futures#addCallback} * Support more fluid version of {@link com.google.common.util.concurrent.Futures#addCallback}
* *
@ -366,12 +356,12 @@ public abstract class AbstractFuture<V> implements Future<V>
* *
* See {@link #addListener(GenericFutureListener)} for ordering semantics. * See {@link #addListener(GenericFutureListener)} for ordering semantics.
*/ */
protected <T> Future<T> flatMap(AbstractFuture<T> result, Function<? super V, ? extends Future<T>> flatMapper, @Nullable Executor executor) protected <T> Future<T> flatMap(AbstractFuture<T> result, Function<? super V, ? extends AsyncResult<T>> flatMapper, @Nullable Executor executor)
{ {
addListener(() -> { addListener(() -> {
try try
{ {
if (isSuccess()) flatMapper.apply(getNow()).addListener(propagate(result)); if (isSuccess()) flatMapper.apply(getNow()).invoke(propagateAsConsumer(result));
else result.tryFailure(cause()); else result.tryFailure(cause());
} }
catch (Throwable t) catch (Throwable t)
@ -404,7 +394,7 @@ public abstract class AbstractFuture<V> implements Future<V>
addListener(() -> { addListener(() -> {
try try
{ {
if (isSuccess()) andThen.apply(getNow()).addListener(propagate(result)); if (isSuccess()) andThen.apply(getNow()).addListener(propagateAsListener(result));
else result.tryFailure(cause()); else result.tryFailure(cause());
} }
catch (Throwable t) catch (Throwable t)
@ -553,11 +543,22 @@ public abstract class AbstractFuture<V> implements Future<V>
/** /**
* @return a listener that will propagate to {@code to} the result of the future it is invoked with * @return a listener that will propagate to {@code to} the result of the future it is invoked with
*/ */
private static <V> GenericFutureListener<? extends Future<V>> propagate(AbstractFuture<? super V> to) private static <V> GenericFutureListener<? extends Future<V>> propagateAsListener(AbstractFuture<? super V> to)
{ {
return from -> { return from -> {
if (from.isSuccess()) to.trySuccess(from.getNow()); if (from.isSuccess()) to.trySuccess(from.getNow());
else to.tryFailure(from.cause()); else to.tryFailure(from.cause());
}; };
} }
/**
* @return a listener that will propagate to {@code to} the result of the future it is invoked with
*/
private static <V> BiConsumer<? super V, Throwable> propagateAsConsumer(AbstractFuture<? super V> to)
{
return (success, fail) -> {
if (fail == null) to.trySuccess(success);
else to.tryFailure(fail);
};
}
} }

View File

@ -27,6 +27,7 @@ import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.ListenableFuture; // checkstyle: permit this import import com.google.common.util.concurrent.ListenableFuture; // checkstyle: permit this import
import accord.utils.Invariants; import accord.utils.Invariants;
import accord.utils.async.AsyncResult;
import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.GenericFutureListener;
import org.apache.cassandra.utils.concurrent.ListenerList.Waiting; import org.apache.cassandra.utils.concurrent.ListenerList.Waiting;
@ -146,7 +147,7 @@ public class AsyncFuture<V> extends AbstractFuture<V>
* See {@link #addListener(GenericFutureListener)} for ordering semantics. * See {@link #addListener(GenericFutureListener)} for ordering semantics.
*/ */
@Override @Override
public <T> Future<T> flatMap(Function<? super V, ? extends Future<T>> flatMapper, @Nullable Executor executor) public <T> Future<T> flatMap(Function<? super V, ? extends AsyncResult<T>> flatMapper, @Nullable Executor executor)
{ {
return flatMap(new AsyncFuture<>(), flatMapper, executor); return flatMap(new AsyncFuture<>(), flatMapper, executor);
} }

View File

@ -27,6 +27,8 @@ import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.ListenableFuture; // checkstyle: permit this import import com.google.common.util.concurrent.ListenableFuture; // checkstyle: permit this import
import accord.api.AsyncExecutor;
import accord.utils.async.AsyncResult;
import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
@ -41,7 +43,7 @@ import static org.apache.cassandra.utils.Shared.Scope.SIMULATION;
* integrating also with our {@link Awaitable} abstraction, to overall improve coherency and clarity in the codebase. * integrating also with our {@link Awaitable} abstraction, to overall improve coherency and clarity in the codebase.
*/ */
@Shared(scope = SIMULATION, ancestors = INTERFACES) @Shared(scope = SIMULATION, ancestors = INTERFACES)
public interface Future<V> extends io.netty.util.concurrent.Future<V>, ListenableFuture<V>, Awaitable public interface Future<V> extends io.netty.util.concurrent.Future<V>, ListenableFuture<V>, Awaitable, AsyncResult<V>
{ {
/** /**
* Wait indefinitely for this future to complete, throwing any interrupt * Wait indefinitely for this future to complete, throwing any interrupt
@ -123,6 +125,8 @@ public interface Future<V> extends io.netty.util.concurrent.Future<V>, Listenabl
return awaitUninterruptibly(l, MILLISECONDS); return awaitUninterruptibly(l, MILLISECONDS);
} }
default Future<V> invoke(BiConsumer<? super V, Throwable> callback) { return addCallback(callback); }
/** /**
* Support {@link com.google.common.util.concurrent.Futures#addCallback} natively * Support {@link com.google.common.util.concurrent.Futures#addCallback} natively
*/ */
@ -158,7 +162,12 @@ public interface Future<V> extends io.netty.util.concurrent.Future<V>, Listenabl
*/ */
default <T> Future<T> map(Function<? super V, ? extends T> mapper) default <T> Future<T> map(Function<? super V, ? extends T> mapper)
{ {
return map(mapper, null); return map(mapper, (Executor) null);
}
default <T> AsyncResult<T> map(Function<? super V, ? extends T> mapper, AsyncExecutor executor)
{
return map(mapper, (Executor) executor);
} }
/** /**
@ -169,15 +178,20 @@ public interface Future<V> extends io.netty.util.concurrent.Future<V>, Listenabl
/** /**
* Support {@link com.google.common.util.concurrent.Futures#transformAsync(ListenableFuture, AsyncFunction, Executor)} natively * Support {@link com.google.common.util.concurrent.Futures#transformAsync(ListenableFuture, AsyncFunction, Executor)} natively
*/ */
default <T> Future<T> flatMap(Function<? super V, ? extends Future<T>> flatMapper) default <T> Future<T> flatMap(Function<? super V, ? extends AsyncResult<T>> flatMapper)
{ {
return flatMap(flatMapper, null); return flatMap(flatMapper, (Executor) null);
}
default <T> AsyncResult<T> flatMap(Function<? super V, ? extends AsyncResult<T>> flatMapper, AsyncExecutor executor)
{
return flatMap(flatMapper, (Executor) executor);
} }
/** /**
* Support {@link com.google.common.util.concurrent.Futures#transformAsync(ListenableFuture, AsyncFunction, Executor)} natively * Support {@link com.google.common.util.concurrent.Futures#transformAsync(ListenableFuture, AsyncFunction, Executor)} natively
*/ */
<T> Future<T> flatMap(Function<? super V, ? extends Future<T>> flatMapper, Executor executor); <T> Future<T> flatMap(Function<? super V, ? extends AsyncResult<T>> flatMapper, Executor executor);
/** /**
* Support {@link com.google.common.util.concurrent.Futures#transformAsync(ListenableFuture, AsyncFunction, Executor)} natively * Support {@link com.google.common.util.concurrent.Futures#transformAsync(ListenableFuture, AsyncFunction, Executor)} natively

View File

@ -26,6 +26,7 @@ import javax.annotation.Nullable;
import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.ListenableFuture; // checkstyle: permit this import import com.google.common.util.concurrent.ListenableFuture; // checkstyle: permit this import
import accord.utils.async.AsyncResult;
import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.GenericFutureListener;
import static org.apache.cassandra.utils.concurrent.Awaitable.SyncAwaitable.waitUntil; import static org.apache.cassandra.utils.concurrent.Awaitable.SyncAwaitable.waitUntil;
@ -106,7 +107,7 @@ public class SyncFuture<V> extends AbstractFuture<V>
* See {@link #addListener(GenericFutureListener)} for ordering semantics. * See {@link #addListener(GenericFutureListener)} for ordering semantics.
*/ */
@Override @Override
public <T> Future<T> flatMap(Function<? super V, ? extends Future<T>> flatMapper, @Nullable Executor executor) public <T> Future<T> flatMap(Function<? super V, ? extends AsyncResult<T>> flatMapper, @Nullable Executor executor)
{ {
return flatMap(new SyncFuture<>(), flatMapper, executor); return flatMap(new SyncFuture<>(), flatMapper, executor);
} }

View File

@ -62,10 +62,10 @@ import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.FBUtilities;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import static accord.utils.async.AsyncChains.awaitUninterruptiblyAndRethrow;
import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.collect.Iterables.getOnlyElement;
import static org.apache.cassandra.distributed.api.Feature.GOSSIP; import static org.apache.cassandra.distributed.api.Feature.GOSSIP;
import static org.apache.cassandra.distributed.api.Feature.NETWORK; import static org.apache.cassandra.distributed.api.Feature.NETWORK;
import static org.apache.cassandra.service.accord.AccordService.getBlocking;
public class AccordBootstrapTest extends TestBaseImpl public class AccordBootstrapTest extends TestBaseImpl
{ {
@ -278,7 +278,7 @@ public class AccordBootstrapTest extends TestBaseImpl
Assert.assertTrue(session.getNumKeyspaceTransfers() > 0); Assert.assertTrue(session.getNumKeyspaceTransfers() > 0);
}); });
awaitUninterruptiblyAndRethrow(service().node().commandStores().forEach((PreLoadContext.Empty)()->"Test", safeStore -> { getBlocking(service().node().commandStores().forEach((PreLoadContext.Empty)()->"Test", safeStore -> {
AccordSafeCommandStore ss = (AccordSafeCommandStore) safeStore; AccordSafeCommandStore ss = (AccordSafeCommandStore) safeStore;
Assert.assertEquals(Timestamp.NONE, getOnlyElement(ss.bootstrapBeganAt().keySet())); Assert.assertEquals(Timestamp.NONE, getOnlyElement(ss.bootstrapBeganAt().keySet()));
Assert.assertEquals(Timestamp.NONE, getOnlyElement(ss.safeToReadAt().keySet())); Assert.assertEquals(Timestamp.NONE, getOnlyElement(ss.safeToReadAt().keySet()));
@ -321,7 +321,7 @@ public class AccordBootstrapTest extends TestBaseImpl
Assert.assertEquals(key, row.getInt("c")); Assert.assertEquals(key, row.getInt("c"));
Assert.assertEquals(key, row.getInt("v")); Assert.assertEquals(key, row.getInt("v"));
awaitUninterruptiblyAndRethrow(service().node().commandStores().forEach((PreLoadContext.Empty)()->"Test", safeStore -> { getBlocking(service().node().commandStores().forEach((PreLoadContext.Empty)()->"Test", safeStore -> {
if (safeStore.ranges().currentRanges().contains(partitionKey)) if (safeStore.ranges().currentRanges().contains(partitionKey))
{ {
AccordSafeCommandStore ss = (AccordSafeCommandStore) safeStore; AccordSafeCommandStore ss = (AccordSafeCommandStore) safeStore;
@ -464,7 +464,7 @@ public class AccordBootstrapTest extends TestBaseImpl
PartitionKey partitionKey = new PartitionKey(tableId, dk); PartitionKey partitionKey = new PartitionKey(tableId, dk);
awaitUninterruptiblyAndRethrow(service().node().commandStores().forEach((PreLoadContext.Empty)()->"Test", getBlocking(service().node().commandStores().forEach((PreLoadContext.Empty)()->"Test",
partitionKey.toUnseekable(), moveMax, moveMax, partitionKey.toUnseekable(), moveMax, moveMax,
safeStore -> { safeStore -> {
if (!safeStore.ranges().allAt(preMove).contains(partitionKey)) if (!safeStore.ranges().allAt(preMove).contains(partitionKey))

View File

@ -30,7 +30,6 @@ import accord.primitives.Ranges;
import accord.primitives.Routable; import accord.primitives.Routable;
import accord.primitives.Txn; import accord.primitives.Txn;
import accord.primitives.TxnId; import accord.primitives.TxnId;
import accord.utils.async.AsyncChains;
import org.apache.cassandra.distributed.Cluster; import org.apache.cassandra.distributed.Cluster;
import org.apache.cassandra.distributed.api.ConsistencyLevel; import org.apache.cassandra.distributed.api.ConsistencyLevel;
import org.apache.cassandra.distributed.api.ICoordinator; import org.apache.cassandra.distributed.api.ICoordinator;
@ -48,6 +47,7 @@ import org.assertj.core.api.Assertions;
import static accord.local.LoadKeysFor.READ_WRITE; import static accord.local.LoadKeysFor.READ_WRITE;
import static org.apache.cassandra.config.DatabaseDescriptor.getPartitioner; import static org.apache.cassandra.config.DatabaseDescriptor.getPartitioner;
import static org.apache.cassandra.service.accord.AccordService.getBlocking;
public class AccordDropTableBase extends TestBaseImpl public class AccordDropTableBase extends TestBaseImpl
{ {
@ -138,7 +138,7 @@ public class AccordDropTableBase extends TestBaseImpl
for (int storeId : stores.ids()) for (int storeId : stores.ids())
{ {
AccordCommandStore store = (AccordCommandStore) stores.forId(storeId); AccordCommandStore store = (AccordCommandStore) stores.forId(storeId);
AsyncChains.getUnchecked(store.submit(ctx, input -> { getBlocking(store.chain(ctx, input -> {
AccordSafeCommandStore safe = (AccordSafeCommandStore) input; AccordSafeCommandStore safe = (AccordSafeCommandStore) input;
for (RoutingKey key : safe.commandsForKeysKeys()) for (RoutingKey key : safe.commandsForKeysKeys())
{ {

View File

@ -35,7 +35,7 @@ import accord.primitives.Ranges;
import accord.primitives.Status; import accord.primitives.Status;
import accord.primitives.Timestamp; import accord.primitives.Timestamp;
import accord.primitives.TxnId; import accord.primitives.TxnId;
import accord.utils.async.AsyncChains; import accord.utils.async.AsyncChain;
import accord.utils.async.AsyncResult; import accord.utils.async.AsyncResult;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import org.apache.cassandra.cql3.QueryProcessor; import org.apache.cassandra.cql3.QueryProcessor;
@ -72,6 +72,7 @@ import static accord.local.LoadKeys.SYNC;
import static accord.local.LoadKeysFor.READ_WRITE; import static accord.local.LoadKeysFor.READ_WRITE;
import static java.lang.String.format; import static java.lang.String.format;
import static org.apache.cassandra.distributed.test.accord.AccordTestBase.executeWithRetry; import static org.apache.cassandra.distributed.test.accord.AccordTestBase.executeWithRetry;
import static org.apache.cassandra.service.accord.AccordService.getBlocking;
public class AccordIncrementalRepairTest extends TestBaseImpl public class AccordIncrementalRepairTest extends TestBaseImpl
{ {
@ -92,16 +93,16 @@ public class AccordIncrementalRepairTest extends TestBaseImpl
return delegate.sync(requestedBy, onOrAfter, ranges, include, syncLocal, syncRemote, 10L, TimeUnit.MINUTES).map(v -> { return delegate.sync(requestedBy, onOrAfter, ranges, include, syncLocal, syncRemote, 10L, TimeUnit.MINUTES).map(v -> {
executedBarriers = true; executedBarriers = true;
return v; return v;
}).beginAsResult(); });
} }
@Override @Override
public AsyncResult<Void> sync(@Nullable Timestamp onOrAfter, Keys keys, DurabilityService.SyncLocal syncLocal, DurabilityService.SyncRemote syncRemote) public AsyncChain<Void> sync(@Nullable Timestamp onOrAfter, Keys keys, DurabilityService.SyncLocal syncLocal, DurabilityService.SyncRemote syncRemote)
{ {
return delegate.sync(onOrAfter, keys, syncLocal, syncRemote).map(v -> { return delegate.sync(onOrAfter, keys, syncLocal, syncRemote).map(v -> {
executedBarriers = true; executedBarriers = true;
return v; return v;
}).beginAsResult(); });
} }
public void reset() public void reset()
@ -206,7 +207,7 @@ public class AccordIncrementalRepairTest extends TestBaseImpl
{ {
Node node = accordService().node(); Node node = accordService().node();
AtomicReference<TxnId> waitFor = new AtomicReference<>(null); AtomicReference<TxnId> waitFor = new AtomicReference<>(null);
AsyncChains.awaitUninterruptibly(node.commandStores().ifLocal(PreLoadContext.contextFor(key, SYNC, READ_WRITE, "Test"), key.toUnseekable(), 0, Long.MAX_VALUE, safeStore -> { getBlocking(node.commandStores().ifLocal(PreLoadContext.contextFor(key, SYNC, READ_WRITE, "Test"), key.toUnseekable(), 0, Long.MAX_VALUE, safeStore -> {
AccordSafeCommandStore store = (AccordSafeCommandStore) safeStore; AccordSafeCommandStore store = (AccordSafeCommandStore) safeStore;
SafeCommandsForKey safeCfk = store.ifLoadedAndInitialised(key); SafeCommandsForKey safeCfk = store.ifLoadedAndInitialised(key);
if (safeCfk == null) if (safeCfk == null)
@ -228,7 +229,7 @@ public class AccordIncrementalRepairTest extends TestBaseImpl
long now = Clock.Global.currentTimeMillis(); long now = Clock.Global.currentTimeMillis();
if (now - start > TimeUnit.MINUTES.toMillis(1)) if (now - start > TimeUnit.MINUTES.toMillis(1))
throw new AssertionError("Timeout"); throw new AssertionError("Timeout");
AsyncChains.awaitUninterruptibly(node.commandStores().ifLocal(PreLoadContext.contextFor(txnId, "Test"), key.toUnseekable(), 0, Long.MAX_VALUE, safeStore -> { AccordService.getBlocking(node.commandStores().ifLocal(PreLoadContext.contextFor(txnId, "Test"), key.toUnseekable(), 0, Long.MAX_VALUE, safeStore -> {
SafeCommand command = safeStore.get(txnId, StoreParticipants.empty(txnId)); SafeCommand command = safeStore.get(txnId, StoreParticipants.empty(txnId));
Assert.assertNotNull(command.current()); Assert.assertNotNull(command.current());
if (command.current().status().hasBeen(Status.Applied)) if (command.current().status().hasBeen(Status.Applied))

View File

@ -30,7 +30,6 @@ import org.slf4j.LoggerFactory;
import accord.local.durability.DurabilityService; import accord.local.durability.DurabilityService;
import accord.primitives.Ranges; import accord.primitives.Ranges;
import accord.primitives.Timestamp; import accord.primitives.Timestamp;
import accord.utils.async.AsyncChains;
import net.bytebuddy.ByteBuddy; import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.MethodDelegation; import net.bytebuddy.implementation.MethodDelegation;
@ -53,6 +52,7 @@ import org.apache.cassandra.utils.concurrent.CountDownLatch;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static org.apache.cassandra.schema.SchemaConstants.ACCORD_KEYSPACE_NAME; import static org.apache.cassandra.schema.SchemaConstants.ACCORD_KEYSPACE_NAME;
import static org.apache.cassandra.service.accord.AccordKeyspace.JOURNAL; import static org.apache.cassandra.service.accord.AccordKeyspace.JOURNAL;
import static org.apache.cassandra.service.accord.AccordService.getBlocking;
/** /**
* This is a specific history of {@link StatefulJournalRestartTest}, which offers a more general way of testing this * This is a specific history of {@link StatefulJournalRestartTest}, which offers a more general way of testing this
@ -90,7 +90,7 @@ public class JournalAccessRouteIndexOnStartupRaceTest extends TestBaseImpl
Ranges ranges = Ranges.single(TokenRange.fullRange(metadata.id, metadata.partitioner)); Ranges ranges = Ranges.single(TokenRange.fullRange(metadata.id, metadata.partitioner));
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
AsyncChains.getBlockingAndRethrow(accord.sync(null, Timestamp.NONE, ranges, null, DurabilityService.SyncLocal.Self, DurabilityService.SyncRemote.Quorum, 10L, TimeUnit.MINUTES)); getBlocking(accord.sync(null, Timestamp.NONE, ranges, null, DurabilityService.SyncLocal.Self, DurabilityService.SyncRemote.Quorum, 10L, TimeUnit.MINUTES));
accord.journal().closeCurrentSegmentForTestingIfNonEmpty(); accord.journal().closeCurrentSegmentForTestingIfNonEmpty();
accord.journal().runCompactorForTesting(); accord.journal().runCompactorForTesting();

View File

@ -32,7 +32,6 @@ import accord.primitives.Ranges;
import accord.primitives.Timestamp; import accord.primitives.Timestamp;
import accord.utils.Property; import accord.utils.Property;
import accord.utils.Property.SimpleCommand; import accord.utils.Property.SimpleCommand;
import accord.utils.async.AsyncChains;
import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.distributed.Cluster; import org.apache.cassandra.distributed.Cluster;
import org.apache.cassandra.distributed.api.IInvokableInstance; import org.apache.cassandra.distributed.api.IInvokableInstance;
@ -47,6 +46,7 @@ import static accord.utils.Property.commands;
import static accord.utils.Property.stateful; import static accord.utils.Property.stateful;
import static org.apache.cassandra.schema.SchemaConstants.ACCORD_KEYSPACE_NAME; import static org.apache.cassandra.schema.SchemaConstants.ACCORD_KEYSPACE_NAME;
import static org.apache.cassandra.service.accord.AccordKeyspace.JOURNAL; import static org.apache.cassandra.service.accord.AccordKeyspace.JOURNAL;
import static org.apache.cassandra.service.accord.AccordService.getBlocking;
/** /**
* There are 2 errors blocking this test from being run * There are 2 errors blocking this test from being run
@ -119,7 +119,7 @@ public class StatefulJournalRestartTest extends TestBaseImpl
Ranges ranges = Ranges.single(TokenRange.fullRange(metadata.id, metadata.partitioner)); Ranges ranges = Ranges.single(TokenRange.fullRange(metadata.id, metadata.partitioner));
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
AsyncChains.getBlockingAndRethrow(accord.sync(null, Timestamp.NONE, ranges, null, DurabilityService.SyncLocal.Self, DurabilityService.SyncRemote.Quorum, 10L, TimeUnit.MINUTES)); getBlocking(accord.sync(null, Timestamp.NONE, ranges, null, DurabilityService.SyncLocal.Self, DurabilityService.SyncRemote.Quorum, 10L, TimeUnit.MINUTES));
accord.journal().closeCurrentSegmentForTestingIfNonEmpty(); accord.journal().closeCurrentSegmentForTestingIfNonEmpty();
accord.journal().runCompactorForTesting(); accord.journal().runCompactorForTesting();

View File

@ -94,12 +94,12 @@ import static accord.local.RedundantStatus.SomeStatus.GC_BEFORE_AND_LOCALLY_DURA
import static accord.primitives.Routable.Domain.Range; import static accord.primitives.Routable.Domain.Range;
import static accord.primitives.Timestamp.Flag.HLC_BOUND; import static accord.primitives.Timestamp.Flag.HLC_BOUND;
import static accord.primitives.Timestamp.Flag.SHARD_BOUND; import static accord.primitives.Timestamp.Flag.SHARD_BOUND;
import static accord.utils.async.AsyncChains.getUninterruptibly;
import static org.apache.cassandra.Util.spinAssertEquals; import static org.apache.cassandra.Util.spinAssertEquals;
import static org.apache.cassandra.cql3.statements.schema.CreateTableStatement.parse; import static org.apache.cassandra.cql3.statements.schema.CreateTableStatement.parse;
import static org.apache.cassandra.schema.SchemaConstants.ACCORD_KEYSPACE_NAME; import static org.apache.cassandra.schema.SchemaConstants.ACCORD_KEYSPACE_NAME;
import static org.apache.cassandra.service.accord.AccordKeyspace.COMMANDS_FOR_KEY; import static org.apache.cassandra.service.accord.AccordKeyspace.COMMANDS_FOR_KEY;
import static org.apache.cassandra.service.accord.AccordKeyspace.CFKAccessor; import static org.apache.cassandra.service.accord.AccordKeyspace.CFKAccessor;
import static org.apache.cassandra.service.accord.AccordService.getBlocking;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
@ -314,32 +314,32 @@ public class CompactionAccordIteratorsTest
PartialDeps partialDeps = Deps.NONE.intersecting(AccordTestUtils.fullRange(txn)); PartialDeps partialDeps = Deps.NONE.intersecting(AccordTestUtils.fullRange(txn));
PartialTxn partialTxn = txn.slice(commandStore.unsafeGetRangesForEpoch().currentRanges(), true); PartialTxn partialTxn = txn.slice(commandStore.unsafeGetRangesForEpoch().currentRanges(), true);
Route<?> partialRoute = route.slice(commandStore.unsafeGetRangesForEpoch().currentRanges()); Route<?> partialRoute = route.slice(commandStore.unsafeGetRangesForEpoch().currentRanges());
getUninterruptibly(commandStore.execute(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> { getBlocking(commandStore.execute(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> {
CheckedCommands.preaccept(safe, txnId, partialTxn, route, (a, b) -> {}); CheckedCommands.preaccept(safe, txnId, partialTxn, route, (a, b) -> {});
}).beginAsResult()); }));
flush(commandStore); flush(commandStore);
getUninterruptibly(commandStore.execute(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> { getBlocking(commandStore.execute(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> {
CheckedCommands.accept(safe, txnId, Ballot.ZERO, partialRoute, txnId, partialDeps, (a, b) -> {}); CheckedCommands.accept(safe, txnId, Ballot.ZERO, partialRoute, txnId, partialDeps, (a, b) -> {});
}).beginAsResult()); }));
flush(commandStore); flush(commandStore);
getUninterruptibly(commandStore.execute(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> { getBlocking(commandStore.execute(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> {
CheckedCommands.commit(safe, SaveStatus.Stable, Ballot.ZERO, txnId, route, partialTxn, txnId, partialDeps, (a, b) -> {}); CheckedCommands.commit(safe, SaveStatus.Stable, Ballot.ZERO, txnId, route, partialTxn, txnId, partialDeps, (a, b) -> {});
}).beginAsResult()); }));
flush(commandStore); flush(commandStore);
getUninterruptibly(commandStore.submit(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> { getBlocking(commandStore.chain(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> {
return AccordTestUtils.processTxnResultDirect(safe, txnId, partialTxn, txnId); return AccordTestUtils.processTxnResultDirect(safe, txnId, partialTxn, txnId);
}).flatMap(i -> i).flatMap(result -> commandStore.execute(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> { }).flatMap(i -> i).flatMap(result -> commandStore.chain(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> {
CheckedCommands.apply(safe, txnId, route, txnId, partialDeps, partialTxn, result.left, result.right, (a, b) -> {}); CheckedCommands.apply(safe, txnId, route, txnId, partialDeps, partialTxn, result.left, result.right, (a, b) -> {});
}))); })));
flush(commandStore); flush(commandStore);
// The apply chain is asychronous, so it is easiest to just spin until it is applied // The apply chain is asychronous, so it is easiest to just spin until it is applied
// in order to have the updated state in the system table // in order to have the updated state in the system table
spinAssertEquals(true, 5, () -> { spinAssertEquals(true, 5, () -> {
return getUninterruptibly(commandStore.submit(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> { return getBlocking(commandStore.submit(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> {
StoreParticipants participants = StoreParticipants.all(route); StoreParticipants participants = StoreParticipants.all(route);
Command command = safe.get(txnId, participants).current(); Command command = safe.get(txnId, participants).current();
return command.hasBeen(Status.Applied); return command.hasBeen(Status.Applied);
}).beginAsResult()); }));
}); });
flush(commandStore); flush(commandStore);
} }

View File

@ -18,6 +18,7 @@
package org.apache.cassandra.db.virtual; package org.apache.cassandra.db.virtual;
import java.util.Collections;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@ -41,9 +42,10 @@ import accord.primitives.SaveStatus;
import accord.primitives.Status.Durability.HasOutcome; import accord.primitives.Status.Durability.HasOutcome;
import accord.primitives.Txn; import accord.primitives.Txn;
import accord.primitives.TxnId; import accord.primitives.TxnId;
import accord.utils.async.AsyncChains; import org.apache.cassandra.config.Config;
import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.config.OptionaldPositiveInt; import org.apache.cassandra.config.OptionaldPositiveInt;
import org.apache.cassandra.config.YamlConfigurationLoader;
import org.apache.cassandra.cql3.CQLTester; import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.cql3.UntypedResultSet; import org.apache.cassandra.cql3.UntypedResultSet;
import org.apache.cassandra.dht.Murmur3Partitioner.LongToken; import org.apache.cassandra.dht.Murmur3Partitioner.LongToken;
@ -54,6 +56,7 @@ import org.apache.cassandra.net.Verb;
import org.apache.cassandra.schema.Schema; import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.schema.SchemaConstants; import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.schema.TableId; import org.apache.cassandra.schema.TableId;
import org.apache.cassandra.service.CassandraDaemon;
import org.apache.cassandra.service.accord.AccordService; import org.apache.cassandra.service.accord.AccordService;
import org.apache.cassandra.service.accord.TokenRange; import org.apache.cassandra.service.accord.TokenRange;
import org.apache.cassandra.service.accord.api.TokenKey; import org.apache.cassandra.service.accord.api.TokenKey;
@ -66,6 +69,10 @@ import org.awaitility.Awaitility;
import static accord.api.ProtocolModifiers.Toggles.SendStableMessages.TO_ALL; import static accord.api.ProtocolModifiers.Toggles.SendStableMessages.TO_ALL;
import static accord.primitives.TxnId.FastPath.Unoptimised; import static accord.primitives.TxnId.FastPath.Unoptimised;
import static org.apache.cassandra.Util.spinUntilSuccess; import static org.apache.cassandra.Util.spinUntilSuccess;
import static org.apache.cassandra.net.Verb.ACCORD_APPLY_AND_WAIT_REQ;
import static org.apache.cassandra.net.Verb.ACCORD_APPLY_REQ;
import static org.apache.cassandra.net.Verb.ACCORD_BEGIN_RECOVER_REQ;
import static org.apache.cassandra.service.accord.AccordService.getBlocking;
import static org.apache.cassandra.service.accord.AccordTestUtils.createTxn; import static org.apache.cassandra.service.accord.AccordTestUtils.createTxn;
public class AccordDebugKeyspaceTest extends CQLTester public class AccordDebugKeyspaceTest extends CQLTester
@ -120,15 +127,21 @@ public class AccordDebugKeyspaceTest extends CQLTester
@BeforeClass @BeforeClass
public static void setUpClass() public static void setUpClass()
{ {
Config.setOverrideLoadConfig(() -> {
Config config = new YamlConfigurationLoader().loadConfig();
config.accord.queue_shard_count = new OptionaldPositiveInt(1);
config.concurrent_accord_operations = 1;
config.accord.command_store_shard_count = new OptionaldPositiveInt(1);
config.accord.enable_virtual_debug_only_keyspace = true;
return config;
});
daemonInitialization(); daemonInitialization();
DatabaseDescriptor.getAccord().queue_shard_count = new OptionaldPositiveInt(1);
DatabaseDescriptor.getAccord().command_store_shard_count = new OptionaldPositiveInt(1);
ProtocolModifiers.Toggles.setSendStableMessages(TO_ALL); ProtocolModifiers.Toggles.setSendStableMessages(TO_ALL);
CQLTester.setUpClass(); CQLTester.setUpClass();
CassandraDaemon.getInstanceForTesting().setupVirtualKeyspaces();
AccordService.startup(ClusterMetadata.current().myNodeId()); AccordService.startup(ClusterMetadata.current().myNodeId());
VirtualKeyspaceRegistry.instance.register(AccordDebugKeyspace.instance);
requireNetwork(); requireNetwork();
} }
@ -169,7 +182,8 @@ public class AccordDebugKeyspaceTest extends CQLTester
assertRows(execute(QUERY_TRACE, id.toString(), "WAIT_PROGRESS")); assertRows(execute(QUERY_TRACE, id.toString(), "WAIT_PROGRESS"));
execute(SET_TRACE, 1, id.toString(), "WAIT_PROGRESS"); execute(SET_TRACE, 1, id.toString(), "WAIT_PROGRESS");
assertRows(execute(QUERY_TRACE, id.toString(), "WAIT_PROGRESS"), row(id.toString(), "WAIT_PROGRESS", 1)); assertRows(execute(QUERY_TRACE, id.toString(), "WAIT_PROGRESS"), row(id.toString(), "WAIT_PROGRESS", 1));
accord.node().coordinate(id, txn); accord.node().coordinate(id, txn).beginAsResult();
filter.appliesTo(id);
filter.preAccept.awaitThrowUncheckedOnInterrupt(); filter.preAccept.awaitThrowUncheckedOnInterrupt();
filter.apply.awaitThrowUncheckedOnInterrupt(); filter.apply.awaitThrowUncheckedOnInterrupt();
@ -198,7 +212,7 @@ public class AccordDebugKeyspaceTest extends CQLTester
TxnId syncId2 = new TxnId(101, 300, Txn.Kind.ExclusiveSyncPoint, Routable.Domain.Range, accord.nodeId()); TxnId syncId2 = new TxnId(101, 300, Txn.Kind.ExclusiveSyncPoint, Routable.Domain.Range, accord.nodeId());
Ranges ranges1 = Ranges.of(TokenRange.create(new TokenKey(tableId, new LongToken(1)), new TokenKey(tableId, new LongToken(100)))); Ranges ranges1 = Ranges.of(TokenRange.create(new TokenKey(tableId, new LongToken(1)), new TokenKey(tableId, new LongToken(100))));
Ranges ranges2 = Ranges.of(TokenRange.create(new TokenKey(tableId, new LongToken(100)), new TokenKey(tableId, new LongToken(200)))); Ranges ranges2 = Ranges.of(TokenRange.create(new TokenKey(tableId, new LongToken(100)), new TokenKey(tableId, new LongToken(200))));
AsyncChains.getBlocking(accord.node().commandStores().forEach((PreLoadContext.Empty)() -> "Test", safeStore -> { getBlocking(accord.node().commandStores().forEach((PreLoadContext.Empty)() -> "Test", safeStore -> {
safeStore.commandStore().markShardDurable(safeStore, syncId1, ranges1, HasOutcome.Universal); safeStore.commandStore().markShardDurable(safeStore, syncId1, ranges1, HasOutcome.Universal);
safeStore.commandStore().markShardDurable(safeStore, syncId2, ranges2, HasOutcome.Quorum); safeStore.commandStore().markShardDurable(safeStore, syncId2, ranges2, HasOutcome.Quorum);
})); }));
@ -218,11 +232,11 @@ public class AccordDebugKeyspaceTest extends CQLTester
TxnId id = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.Write, Routable.Domain.Key); TxnId id = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.Write, Routable.Domain.Key);
Txn txn = createTxn(wrapInTxn(String.format("INSERT INTO %s.%s(k, c, v) VALUES (?, ?, ?)", KEYSPACE, tableName)), 0, 0, 0); Txn txn = createTxn(wrapInTxn(String.format("INSERT INTO %s.%s(k, c, v) VALUES (?, ?, ?)", KEYSPACE, tableName)), 0, 0, 0);
String keyStr = txn.keys().get(0).toUnseekable().toString(); String keyStr = txn.keys().get(0).toUnseekable().toString();
AsyncChains.getBlocking(accord.node().coordinate(id, txn)); getBlocking(accord.node().coordinate(id, txn));
spinUntilSuccess(() -> assertRows(execute(QUERY_TXN_BLOCKED_BY, id.toString()), spinUntilSuccess(() -> assertRows(execute(QUERY_TXN_BLOCKED_BY, id.toString()),
row(id.toString(), KEYSPACE, tableName, anyInt(), 0, ByteBufferUtil.EMPTY_BYTE_BUFFER, "Self", any(), null, anyOf(SaveStatus.ReadyToExecute.name(), SaveStatus.Applying.name(), SaveStatus.Applied.name())))); row(id.toString(), KEYSPACE, tableName, anyInt(), 0, ByteBufferUtil.EMPTY_BYTE_BUFFER, "Self", any(), null, anyOf(SaveStatus.ReadyToExecute.name(), SaveStatus.Applying.name(), SaveStatus.Applied.name()))));
assertRows(execute(QUERY_TXN, id.toString()), row(id.toString(), "Applied")); spinUntilSuccess(() -> assertRows(execute(QUERY_TXN, id.toString()), row(id.toString(), "Applied")));
assertRows(execute(QUERY_JOURNAL, id.toString()), row(id.toString(), "PreAccepted"), row(id.toString(), "Applying"), row(id.toString(), "Applied"), row(id.toString(), null)); assertRows(execute(QUERY_JOURNAL, id.toString()), row(id.toString(), "PreAccepted"), row(id.toString(), "Applying"), row(id.toString(), "Applied"), row(id.toString(), null));
assertRows(execute(QUERY_COMMANDS_FOR_KEY, keyStr), row(id.toString(), "APPLIED_DURABLE")); assertRows(execute(QUERY_COMMANDS_FOR_KEY, keyStr), row(id.toString(), "APPLIED_DURABLE"));
} }
@ -245,7 +259,8 @@ public class AccordDebugKeyspaceTest extends CQLTester
" END IF\n" + " END IF\n" +
"COMMIT TRANSACTION", KEYSPACE, tableName, KEYSPACE, tableName); "COMMIT TRANSACTION", KEYSPACE, tableName, KEYSPACE, tableName);
Txn txn = createTxn(insertTxn, 0, 0, 0, 0, 0); Txn txn = createTxn(insertTxn, 0, 0, 0, 0, 0);
accord.node().coordinate(id, txn); filter.appliesTo(id);
accord.node().coordinate(id, txn).beginAsResult();
filter.preAccept.awaitThrowUncheckedOnInterrupt(); filter.preAccept.awaitThrowUncheckedOnInterrupt();
assertRows(execute(QUERY_TXN_BLOCKED_BY, id.toString()), assertRows(execute(QUERY_TXN_BLOCKED_BY, id.toString()),
@ -280,20 +295,22 @@ public class AccordDebugKeyspaceTest extends CQLTester
" INSERT INTO %s.%s (k, c, v) VALUES (?, ?, ?);\n" + " INSERT INTO %s.%s (k, c, v) VALUES (?, ?, ?);\n" +
" END IF\n" + " END IF\n" +
"COMMIT TRANSACTION", KEYSPACE, tableName, KEYSPACE, tableName); "COMMIT TRANSACTION", KEYSPACE, tableName, KEYSPACE, tableName);
accord.node().coordinate(first, createTxn(insertTxn, 0, 0, 0, 0, 0));
filter.appliesTo(first);
accord.node().coordinate(first, createTxn(insertTxn, 0, 0, 0, 0, 0)).beginAsResult();
filter.preAccept.awaitThrowUncheckedOnInterrupt(); filter.preAccept.awaitThrowUncheckedOnInterrupt();
assertRows(execute(QUERY_TXN_BLOCKED_BY, first.toString()), spinUntilSuccess(() ->assertRows(execute(QUERY_TXN_BLOCKED_BY, first.toString()),
row(first.toString(), KEYSPACE, tableName, anyInt(), 0, ByteBufferUtil.EMPTY_BYTE_BUFFER, "Self", any(), null, anyOf(SaveStatus.PreAccepted.name(), SaveStatus.ReadyToExecute.name()))); row(first.toString(), KEYSPACE, tableName, anyInt(), 0, ByteBufferUtil.EMPTY_BYTE_BUFFER, "Self", any(), null, anyOf(SaveStatus.PreAccepted.name(), SaveStatus.ReadyToExecute.name()))));
filter.apply.awaitThrowUncheckedOnInterrupt(); filter.apply.awaitThrowUncheckedOnInterrupt();
assertRows(execute(QUERY_TXN_BLOCKED_BY, first.toString()), spinUntilSuccess(() -> assertRows(execute(QUERY_TXN_BLOCKED_BY, first.toString()),
row(first.toString(), KEYSPACE, tableName, anyInt(), 0, ByteBufferUtil.EMPTY_BYTE_BUFFER, "Self", anyNonNull(), null, SaveStatus.ReadyToExecute.name())); row(first.toString(), KEYSPACE, tableName, anyInt(), 0, ByteBufferUtil.EMPTY_BYTE_BUFFER, "Self", anyNonNull(), null, SaveStatus.ReadyToExecute.name())));
filter.reset();
TxnId second = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.Write, Routable.Domain.Key); TxnId second = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.Write, Routable.Domain.Key);
accord.node().coordinate(second, createTxn(insertTxn, 0, 0, 0, 0, 0)); filter.reset();
filter.appliesTo(second);
accord.node().coordinate(second, createTxn(insertTxn, 0, 0, 0, 0, 0)).beginAsResult();
filter.commit.awaitThrowUncheckedOnInterrupt(); filter.commit.awaitThrowUncheckedOnInterrupt();
@ -357,7 +374,8 @@ public class AccordDebugKeyspaceTest extends CQLTester
tableName); tableName);
AccordService accord = accord(); AccordService accord = accord();
TxnId id = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.Write, Routable.Domain.Key); TxnId id = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.Write, Routable.Domain.Key);
accord.node().coordinate(id, createTxn(insertTxn, 0, 0, 0)); filter.appliesTo(id);
accord.node().coordinate(id, createTxn(insertTxn, 0, 0, 0)).beginAsResult();
filter.preAccept.awaitThrowUncheckedOnInterrupt(); filter.preAccept.awaitThrowUncheckedOnInterrupt();
String QUERY_JOURNAL = String.format("SELECT txn_id, save_status, command_store_id FROM %s.%s WHERE txn_id=?", SchemaConstants.VIRTUAL_ACCORD_DEBUG, AccordDebugKeyspace.JOURNAL); String QUERY_JOURNAL = String.format("SELECT txn_id, save_status, command_store_id FROM %s.%s WHERE txn_id=?", SchemaConstants.VIRTUAL_ACCORD_DEBUG, AccordDebugKeyspace.JOURNAL);
@ -402,6 +420,15 @@ public class AccordDebugKeyspaceTest extends CQLTester
} }
ConcurrentMap<TxnId, ConcurrentSkipListSet<Verb>> txnToVerbs = new ConcurrentHashMap<>(); ConcurrentMap<TxnId, ConcurrentSkipListSet<Verb>> txnToVerbs = new ConcurrentHashMap<>();
Set<Verb> dropVerbs = Set.of(ACCORD_APPLY_REQ, ACCORD_APPLY_AND_WAIT_REQ, ACCORD_BEGIN_RECOVER_REQ);
Set<TxnId> applyTo;
void appliesTo(TxnId txnId)
{
if (applyTo == null)
applyTo = Collections.newSetFromMap(new ConcurrentHashMap<>());
applyTo.add(txnId);
}
@Override @Override
public boolean test(Message<?> msg, InetAddressAndPort to) public boolean test(Message<?> msg, InetAddressAndPort to)
@ -412,6 +439,8 @@ public class AccordDebugKeyspaceTest extends CQLTester
if (msg.payload instanceof TxnRequest) if (msg.payload instanceof TxnRequest)
{ {
txnId = ((TxnRequest<?>) msg.payload).txnId; txnId = ((TxnRequest<?>) msg.payload).txnId;
if (applyTo != null && !applyTo.contains(txnId))
return true;
} }
Set<Verb> seen; Set<Verb> seen;
if (txnId != null) if (txnId != null)
@ -424,32 +453,15 @@ public class AccordDebugKeyspaceTest extends CQLTester
case ACCORD_APPLY_REQ: case ACCORD_APPLY_REQ:
case ACCORD_APPLY_AND_WAIT_REQ: case ACCORD_APPLY_AND_WAIT_REQ:
apply.signalAll(); apply.signalAll();
case ACCORD_BEGIN_RECOVER_REQ: break;
return false;
case ACCORD_PRE_ACCEPT_RSP: case ACCORD_PRE_ACCEPT_RSP:
preAccept.signalAll(); preAccept.signalAll();
return true; break;
case ACCORD_COMMIT_REQ: case ACCORD_COMMIT_REQ:
case ACCORD_STABLE_THEN_READ_REQ: case ACCORD_STABLE_THEN_READ_REQ:
commit.signalAll(); commit.signalAll();
return true; }
case ACCORD_PRE_ACCEPT_REQ: return !dropVerbs.contains(msg.verb());
case ACCORD_ACCEPT_REQ:
case ACCORD_ACCEPT_RSP:
case ACCORD_CHECK_STATUS_REQ:
case ACCORD_CHECK_STATUS_RSP:
case ACCORD_READ_REQ:
case ACCORD_READ_RSP:
case ACCORD_AWAIT_REQ:
case ACCORD_AWAIT_RSP:
case ACCORD_AWAIT_ASYNC_RSP_REQ:
return true;
default:
// many code paths don't log the error...
UnsupportedOperationException e = new UnsupportedOperationException(msg.verb().name());
logger.error("Unexpected verb {}", msg.verb(), e);
throw e;
}
} }
} }
} }

View File

@ -0,0 +1,332 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.service.accord;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import org.junit.Assert;
import org.junit.Test;
import accord.utils.async.AsyncChain;
import accord.utils.async.AsyncChains;
import accord.utils.async.AsyncResult;
import org.apache.cassandra.utils.concurrent.AsyncFuture;
import org.apache.cassandra.utils.concurrent.Future;
import org.apache.cassandra.utils.concurrent.ImmediateFuture;
public class AccordAsyncConversionTest
{
private static final String SUCCESS = "test-success";
private static final RuntimeException EXCEPTION = new RuntimeException("test-failure");
@Test
public void testAsyncChainToFutureSuccess()
{
AsyncChain<String> chain = AsyncChains.success(SUCCESS);
Future<String> future = AccordService.toFuture(chain);
Assert.assertTrue("should be completed", future.isDone());
Assert.assertTrue("should be successful", future.isSuccess());
Assert.assertEquals("should contain expected value", SUCCESS, future.getNow());
}
@Test
public void testAsyncChainToFutureFailure()
{
AsyncChain<String> chain = AsyncChains.failure(EXCEPTION);
Future<String> future = AccordService.toFuture(chain);
Assert.assertTrue("should be completed", future.isDone());
Assert.assertFalse("should be failed", future.isSuccess());
Assert.assertEquals("should contain expected exception", EXCEPTION, future.cause());
}
@Test
public void testAsyncChainToFutureAsyncCompletion() throws InterruptedException
{
CountDownLatch startLatch = new CountDownLatch(1);
AsyncChain<String> chain = new AsyncChains.Head<String>()
{
@Override
protected accord.utils.async.Cancellable start(BiConsumer<? super String, Throwable> callback)
{
Thread t = new Thread(() -> {
try
{
startLatch.await();
callback.accept(SUCCESS, null);
}
catch (InterruptedException e)
{
callback.accept(null, e);
}
});
t.start();
return () -> t.interrupt();
}
};
Future<String> future = AccordService.toFuture(chain);
Assert.assertFalse("should not be completed initially", future.isDone());
startLatch.countDown();
Assert.assertEquals("should complete with expected value", SUCCESS, future.syncUninterruptibly().getNow());
}
@Test
public void testAsyncResultToFutureWhenAlreadyFuture()
{
AsyncFuture<String> original = ImmediateFuture.success(SUCCESS);
Future<String> converted = AccordService.toFuture(original);
Assert.assertSame("Should return the same instance when already a Future", original, converted);
}
@Test
public void testAsyncResultToFutureSuccess()
{
AsyncResult<String> result = new TestAsyncResult<>(SUCCESS, null);
Future<String> future = AccordService.toFuture(result);
Assert.assertTrue("should be completed", future.isDone());
Assert.assertTrue("should be successful", future.isSuccess());
Assert.assertEquals("should contain expected value", SUCCESS, future.getNow());
}
@Test
public void testAsyncResultToFutureFailure()
{
AsyncResult<String> result = new TestAsyncResult<>(null, EXCEPTION);
Future<String> future = AccordService.toFuture(result);
Assert.assertTrue("should be completed", future.isDone());
Assert.assertFalse("should be failed", future.isSuccess());
Assert.assertEquals("should contain expected exception", EXCEPTION, future.cause());
}
@Test
public void testGetBlockingAsyncChainSuccess()
{
AsyncChain<String> chain = AsyncChains.success(SUCCESS);
String result = AccordService.getBlocking(chain);
Assert.assertEquals("Should return expected value", SUCCESS, result);
}
@Test(expected = RuntimeException.class)
public void testGetBlockingAsyncChainFailure()
{
AsyncChain<String> chain = AsyncChains.failure(EXCEPTION);
AccordService.getBlocking(chain);
}
@Test
public void testGetBlockingAsyncResultSuccess()
{
AsyncResult<String> result = new TestAsyncResult<>(SUCCESS, null);
String value = AccordService.getBlocking(result);
Assert.assertEquals("Should return expected value", SUCCESS, value);
}
@Test(expected = RuntimeException.class)
public void testGetBlockingAsyncResultFailure()
{
AsyncResult<String> result = new TestAsyncResult<>(null, EXCEPTION);
AccordService.getBlocking(result);
}
@Test
public void testAsyncChainCancellation() throws InterruptedException
{
AtomicBoolean cancelled = new AtomicBoolean(false);
CountDownLatch startLatch = new CountDownLatch(1);
AsyncChain<String> chain = new AsyncChains.Head<String>()
{
@Override
protected accord.utils.async.Cancellable start(BiConsumer<? super String, Throwable> callback)
{
Thread t = new Thread(() -> {
try
{
startLatch.await();
Thread.sleep(1000);
callback.accept(SUCCESS, null);
}
catch (InterruptedException e)
{
cancelled.set(true);
callback.accept(null, e);
}
});
t.start();
return () -> {
cancelled.set(true);
t.interrupt();
};
}
};
Future<String> future = AccordService.toFuture(chain);
startLatch.countDown();
boolean cancelResult = future.cancel(true);
Thread.sleep(100);
Assert.assertTrue("should be cancellable", cancelResult);
Assert.assertTrue("should be cancelled", future.isCancelled());
}
@Test
public void testMultipleCallbacksOnAsyncResult() throws InterruptedException
{
AtomicReference<String> callback1Result = new AtomicReference<>();
AtomicReference<String> callback2Result = new AtomicReference<>();
CountDownLatch callbackLatch = new CountDownLatch(2);
AsyncResult<String> result = new TestAsyncResult<>(SUCCESS, null);
result.invoke((value, throwable) -> {
callback1Result.set(value);
callbackLatch.countDown();
});
result.invoke((value, throwable) -> {
callback2Result.set(value);
callbackLatch.countDown();
});
Assert.assertTrue("Both callbacks should be invoked", callbackLatch.await(1, TimeUnit.SECONDS));
Assert.assertEquals("First callback should receive correct value", SUCCESS, callback1Result.get());
Assert.assertEquals("Second callback should receive correct value", SUCCESS, callback2Result.get());
}
@Test
public void testNullAsyncResult()
{
try
{
AccordService.toFuture((AsyncResult<String>) null);
Assert.fail("Should throw NullPointerException for null AsyncResult");
}
catch (NullPointerException e)
{
}
}
@Test
public void testNullAsyncChain()
{
try
{
AccordService.toFuture((AsyncChain<String>) null);
Assert.fail("Should throw NullPointerException for null AsyncChain");
}
catch (NullPointerException e)
{
// ignore
}
}
@Test
public void testFutureInterfaceCompliance()
{
AsyncChain<String> chain = AsyncChains.success(SUCCESS);
Future<String> future = AccordService.toFuture(chain);
Assert.assertTrue("should implement AsyncResult", future instanceof AsyncResult);
Assert.assertTrue("should be completion-aware", future.isDone());
Assert.assertNotNull("should have a result", future.getNow());
}
@Test
public void testConcurrentConversion() throws InterruptedException
{
int threadCount = 10;
CountDownLatch startLatch = new CountDownLatch(1);
CountDownLatch completeLatch = new CountDownLatch(threadCount);
AtomicReference<Exception> exception = new AtomicReference<>();
for (int i = 0; i < threadCount; i++)
{
final int threadIndex = i;
Thread t = new Thread(() -> {
try
{
startLatch.await();
AsyncChain<Integer> chain = AsyncChains.success(threadIndex);
Future<Integer> future = AccordService.toFuture(chain);
Assert.assertEquals("Thread " + threadIndex + " should get correct result",
Integer.valueOf(threadIndex), future.getNow());
}
catch (Exception e)
{
exception.set(e);
}
finally
{
completeLatch.countDown();
}
});
t.start();
}
startLatch.countDown();
Assert.assertTrue("All threads should complete", completeLatch.await(5, TimeUnit.SECONDS));
Assert.assertNull("No exceptions should occur during concurrent conversion", exception.get());
}
private static class TestAsyncResult<V> implements AsyncResult<V>
{
private final V successValue;
private final Throwable failureValue;
public TestAsyncResult(V successValue, Throwable failureValue)
{
this.successValue = successValue;
this.failureValue = failureValue;
}
@Override
public boolean isDone()
{
return true;
}
@Override
public boolean isSuccess()
{
return false;
}
@Override
public AsyncResult<V> invoke(BiConsumer<? super V, Throwable> callback)
{
callback.accept(successValue, failureValue);
return null;
}
}
}

View File

@ -50,7 +50,6 @@ import accord.primitives.TxnId;
import accord.primitives.Writes; import accord.primitives.Writes;
import accord.utils.ImmutableBitSet; import accord.utils.ImmutableBitSet;
import accord.utils.LargeBitSet; import accord.utils.LargeBitSet;
import accord.utils.async.AsyncChains;
import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.cql3.QueryProcessor; import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.ColumnFamilyStore;
@ -73,6 +72,7 @@ import org.apache.cassandra.utils.Pair;
import static accord.primitives.Status.Durability.AllQuorums; import static accord.primitives.Status.Durability.AllQuorums;
import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.collect.Iterables.getOnlyElement;
import static org.apache.cassandra.cql3.statements.schema.CreateTableStatement.parse; import static org.apache.cassandra.cql3.statements.schema.CreateTableStatement.parse;
import static org.apache.cassandra.service.accord.AccordService.getBlocking;
import static org.apache.cassandra.service.accord.AccordTestUtils.Commands.preaccepted; import static org.apache.cassandra.service.accord.AccordTestUtils.Commands.preaccepted;
import static org.apache.cassandra.service.accord.AccordTestUtils.ballot; import static org.apache.cassandra.service.accord.AccordTestUtils.ballot;
import static org.apache.cassandra.service.accord.AccordTestUtils.createAccordCommandStore; import static org.apache.cassandra.service.accord.AccordTestUtils.createAccordCommandStore;
@ -133,7 +133,7 @@ public class AccordCommandStoreTest
LargeBitSet waitingOnApply = new LargeBitSet(3); LargeBitSet waitingOnApply = new LargeBitSet(3);
waitingOnApply.set(1); waitingOnApply.set(1);
Command.WaitingOn waitingOn = new Command.WaitingOn(dependencies.keyDeps.keys(), dependencies.rangeDeps, new ImmutableBitSet(waitingOnApply), new ImmutableBitSet(2)); Command.WaitingOn waitingOn = new Command.WaitingOn(dependencies.keyDeps.keys(), dependencies.rangeDeps, new ImmutableBitSet(waitingOnApply), new ImmutableBitSet(2));
Pair<Writes, Result> result = AsyncChains.getBlocking(AccordTestUtils.processTxnResult(commandStore, txnId, txn, executeAt)); Pair<Writes, Result> result = getBlocking(AccordTestUtils.processTxnResult(commandStore, txnId, txn, executeAt));
Command expected = Command.Executed.executed(txnId, SaveStatus.Applied, AllQuorums, StoreParticipants.all(route), Command expected = Command.Executed.executed(txnId, SaveStatus.Applied, AllQuorums, StoreParticipants.all(route),
promised, executeAt, txn, dependencies, accepted, promised, executeAt, txn, dependencies, accepted,

View File

@ -59,8 +59,8 @@ import org.apache.cassandra.utils.ByteBufferUtil;
import static accord.api.ProtocolModifiers.Toggles.filterDuplicateDependenciesFromAcceptReply; import static accord.api.ProtocolModifiers.Toggles.filterDuplicateDependenciesFromAcceptReply;
import static accord.local.LoadKeysFor.READ_WRITE; import static accord.local.LoadKeysFor.READ_WRITE;
import static accord.messages.Accept.Kind.SLOW; import static accord.messages.Accept.Kind.SLOW;
import static accord.utils.async.AsyncChains.getUninterruptibly;
import static org.apache.cassandra.cql3.statements.schema.CreateTableStatement.parse; import static org.apache.cassandra.cql3.statements.schema.CreateTableStatement.parse;
import static org.apache.cassandra.service.accord.AccordService.getBlocking;
import static org.apache.cassandra.service.accord.AccordTestUtils.createAccordCommandStore; import static org.apache.cassandra.service.accord.AccordTestUtils.createAccordCommandStore;
import static org.apache.cassandra.service.accord.AccordTestUtils.createWriteTxn; import static org.apache.cassandra.service.accord.AccordTestUtils.createWriteTxn;
import static org.apache.cassandra.service.accord.AccordTestUtils.fullRange; import static org.apache.cassandra.service.accord.AccordTestUtils.fullRange;
@ -97,7 +97,7 @@ public class AccordCommandTest
public void basicCycleTest() throws Throwable public void basicCycleTest() throws Throwable
{ {
AccordCommandStore commandStore = createAccordCommandStore(clock::incrementAndGet, "ks", "tbl"); AccordCommandStore commandStore = createAccordCommandStore(clock::incrementAndGet, "ks", "tbl");
getUninterruptibly(commandStore.execute((PreLoadContext.Empty)() -> "Test", unused -> commandStore.executor().cacheUnsafe().setCapacity(0))); getBlocking(commandStore.execute((PreLoadContext.Empty)() -> "Test", unused -> commandStore.executor().cacheUnsafe().setCapacity(0)));
TxnId txnId = txnId(1, clock.incrementAndGet(), 1); TxnId txnId = txnId(1, clock.incrementAndGet(), 1);
Txn txn = createWriteTxn(1); Txn txn = createWriteTxn(1);
@ -109,7 +109,7 @@ public class AccordCommandTest
PreAccept preAccept = PreAccept.SerializerSupport.create(txnId, route, 1, 1, 1, partialTxn, null, false, fullRoute); PreAccept preAccept = PreAccept.SerializerSupport.create(txnId, route, 1, 1, 1, partialTxn, null, false, fullRoute);
// Check preaccept // Check preaccept
getUninterruptibly(commandStore.execute(preAccept, safeStore -> { getBlocking(commandStore.execute(preAccept, safeStore -> {
SafeCommand safeCommand = safeStore.get(txnId, StoreParticipants.all(route)); SafeCommand safeCommand = safeStore.get(txnId, StoreParticipants.all(route));
Command before = safeCommand.current(); Command before = safeCommand.current();
PreAccept.PreAcceptReply reply = preAccept.apply(safeStore); PreAccept.PreAcceptReply reply = preAccept.apply(safeStore);
@ -123,7 +123,7 @@ public class AccordCommandTest
AccordTestUtils.appendCommandsBlocking(commandStore, before, after); AccordTestUtils.appendCommandsBlocking(commandStore, before, after);
})); }));
getUninterruptibly(commandStore.execute(preAccept, safeStore -> { getBlocking(commandStore.execute(preAccept, safeStore -> {
Command before = safeStore.ifInitialised(txnId).current(); Command before = safeStore.ifInitialised(txnId).current();
SafeCommand safeCommand = safeStore.get(txnId, StoreParticipants.all(route)); SafeCommand safeCommand = safeStore.get(txnId, StoreParticipants.all(route));
Assert.assertEquals(txnId, before.executeAt()); Assert.assertEquals(txnId, before.executeAt());
@ -147,7 +147,7 @@ public class AccordCommandTest
} }
Accept accept = Accept.SerializerSupport.create(txnId, route, 1, 1, SLOW, Ballot.ZERO, executeAt, deps, false); Accept accept = Accept.SerializerSupport.create(txnId, route, 1, 1, SLOW, Ballot.ZERO, executeAt, deps, false);
getUninterruptibly(commandStore.execute(accept, safeStore -> { getBlocking(commandStore.execute(accept, safeStore -> {
Command before = safeStore.ifInitialised(txnId).current(); Command before = safeStore.ifInitialised(txnId).current();
Accept.AcceptReply reply = accept.apply(safeStore); Accept.AcceptReply reply = accept.apply(safeStore);
Assert.assertTrue(reply.isOk()); Assert.assertTrue(reply.isOk());
@ -156,7 +156,7 @@ public class AccordCommandTest
AccordTestUtils.appendCommandsBlocking(commandStore, before, after); AccordTestUtils.appendCommandsBlocking(commandStore, before, after);
})); }));
getUninterruptibly(commandStore.execute(accept, safeStore -> { getBlocking(commandStore.execute(accept, safeStore -> {
Command before = safeStore.ifInitialised(txnId).current(); Command before = safeStore.ifInitialised(txnId).current();
Assert.assertEquals(executeAt, before.executeAt()); Assert.assertEquals(executeAt, before.executeAt());
Assert.assertEquals(Status.AcceptedSlow, before.status()); Assert.assertEquals(Status.AcceptedSlow, before.status());
@ -170,9 +170,9 @@ public class AccordCommandTest
// check commit // check commit
Commit commit = Commit.SerializerSupport.create(txnId, route, 1, 1, Commit.Kind.StableWithTxnAndDeps, Ballot.ZERO, executeAt, partialTxn, deps, fullRoute); Commit commit = Commit.SerializerSupport.create(txnId, route, 1, 1, Commit.Kind.StableWithTxnAndDeps, Ballot.ZERO, executeAt, partialTxn, deps, fullRoute);
getUninterruptibly(commandStore.execute(commit, commit::apply)); getBlocking(commandStore.execute(commit, commit::apply));
getUninterruptibly(commandStore.execute(PreLoadContext.contextFor(txnId, Keys.of(key).toParticipants(), LoadKeys.SYNC, READ_WRITE, "Test"), safeStore -> { getBlocking(commandStore.execute(PreLoadContext.contextFor(txnId, Keys.of(key).toParticipants(), LoadKeys.SYNC, READ_WRITE, "Test"), safeStore -> {
Command before = safeStore.ifInitialised(txnId).current(); Command before = safeStore.ifInitialised(txnId).current();
Assert.assertEquals(commit.executeAt, before.executeAt()); Assert.assertEquals(commit.executeAt, before.executeAt());
Assert.assertTrue(before.hasBeen(Status.Committed)); Assert.assertTrue(before.hasBeen(Status.Committed));
@ -189,7 +189,7 @@ public class AccordCommandTest
public void computeDeps() throws Throwable public void computeDeps() throws Throwable
{ {
AccordCommandStore commandStore = createAccordCommandStore(clock::incrementAndGet, "ks", "tbl"); AccordCommandStore commandStore = createAccordCommandStore(clock::incrementAndGet, "ks", "tbl");
getUninterruptibly(commandStore.execute((PreLoadContext.Empty)()->"Test", unused -> commandStore.executor().cacheUnsafe().setCapacity(0))); getBlocking(commandStore.execute((PreLoadContext.Empty)()->"Test", unused -> commandStore.executor().cacheUnsafe().setCapacity(0)));
TxnId txnId1 = txnId(1, clock.incrementAndGet(), 1); TxnId txnId1 = txnId(1, clock.incrementAndGet(), 1);
Txn txn = createWriteTxn(2); Txn txn = createWriteTxn(2);
@ -200,7 +200,7 @@ public class AccordCommandTest
PartialTxn partialTxn = txn.intersecting(route, true); PartialTxn partialTxn = txn.intersecting(route, true);
PreAccept preAccept1 = PreAccept.SerializerSupport.create(txnId1, route, 1, 1, 1, partialTxn, null, false, fullRoute); PreAccept preAccept1 = PreAccept.SerializerSupport.create(txnId1, route, 1, 1, 1, partialTxn, null, false, fullRoute);
getUninterruptibly(commandStore.execute(preAccept1, safeStore -> { getBlocking(commandStore.execute(preAccept1, safeStore -> {
persistDiff(commandStore, safeStore, txnId1, route, () -> { persistDiff(commandStore, safeStore, txnId1, route, () -> {
preAccept1.apply(safeStore); preAccept1.apply(safeStore);
}); });
@ -209,7 +209,7 @@ public class AccordCommandTest
// second preaccept should identify txnId1 as a dependency // second preaccept should identify txnId1 as a dependency
TxnId txnId2 = txnId(1, clock.incrementAndGet(), 1); TxnId txnId2 = txnId(1, clock.incrementAndGet(), 1);
PreAccept preAccept2 = PreAccept.SerializerSupport.create(txnId2, route, 1, 1, 1, partialTxn, null, false, fullRoute); PreAccept preAccept2 = PreAccept.SerializerSupport.create(txnId2, route, 1, 1, 1, partialTxn, null, false, fullRoute);
getUninterruptibly(commandStore.execute(preAccept2, safeStore -> { getBlocking(commandStore.execute(preAccept2, safeStore -> {
persistDiff(commandStore, safeStore, txnId2, route, () -> { persistDiff(commandStore, safeStore, txnId2, route, () -> {
PreAccept.PreAcceptReply reply = preAccept2.apply(safeStore); PreAccept.PreAcceptReply reply = preAccept2.apply(safeStore);
Assert.assertTrue(reply.isOk()); Assert.assertTrue(reply.isOk());

View File

@ -23,7 +23,6 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@ -90,8 +89,8 @@ import static accord.local.LoadKeys.SYNC;
import static accord.local.LoadKeysFor.READ_WRITE; import static accord.local.LoadKeysFor.READ_WRITE;
import static accord.local.PreLoadContext.contextFor; import static accord.local.PreLoadContext.contextFor;
import static accord.utils.Property.qt; import static accord.utils.Property.qt;
import static accord.utils.async.AsyncChains.getUninterruptibly;
import static org.apache.cassandra.cql3.statements.schema.CreateTableStatement.parse; import static org.apache.cassandra.cql3.statements.schema.CreateTableStatement.parse;
import static org.apache.cassandra.service.accord.AccordService.getBlocking;
import static org.apache.cassandra.service.accord.AccordTestUtils.createAccordCommandStore; import static org.apache.cassandra.service.accord.AccordTestUtils.createAccordCommandStore;
import static org.apache.cassandra.service.accord.AccordTestUtils.createPartialTxn; import static org.apache.cassandra.service.accord.AccordTestUtils.createPartialTxn;
import static org.apache.cassandra.service.accord.AccordTestUtils.keys; import static org.apache.cassandra.service.accord.AccordTestUtils.keys;
@ -128,7 +127,7 @@ public class AccordTaskTest
AccordCommandStore commandStore = createAccordCommandStore(clock::incrementAndGet, "ks", "tbl"); AccordCommandStore commandStore = createAccordCommandStore(clock::incrementAndGet, "ks", "tbl");
TxnId txnId = txnId(1, clock.incrementAndGet(), 1); TxnId txnId = txnId(1, clock.incrementAndGet(), 1);
getUninterruptibly(commandStore.execute(PreLoadContext.contextFor(txnId, "Test"), instance -> { getBlocking(commandStore.execute(PreLoadContext.contextFor(txnId, "Test"), instance -> {
// TODO review: This change to `ifInitialized` was done in a lot of places and it doesn't preserve this property // 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 // I fixed this reference to point to `ifLoadedAndInitialised` and but didn't update other places
Assert.assertNull(instance.ifInitialised(txnId)); Assert.assertNull(instance.ifInitialised(txnId));
@ -142,7 +141,7 @@ public class AccordTaskTest
AccordCommandStore commandStore = createAccordCommandStore(clock::incrementAndGet, "ks", "tbl"); AccordCommandStore commandStore = createAccordCommandStore(clock::incrementAndGet, "ks", "tbl");
TxnId txnId = txnId(1, clock.incrementAndGet(), 1); TxnId txnId = txnId(1, clock.incrementAndGet(), 1);
getUninterruptibly(commandStore.execute(PreLoadContext.contextFor(txnId, "Test"), safe -> { getBlocking(commandStore.execute(PreLoadContext.contextFor(txnId, "Test"), safe -> {
StoreParticipants participants = StoreParticipants.empty(txnId); StoreParticipants participants = StoreParticipants.empty(txnId);
SafeCommand command = safe.get(txnId, participants); SafeCommand command = safe.get(txnId, participants);
Assert.assertNotNull(command); Assert.assertNotNull(command);
@ -156,7 +155,7 @@ public class AccordTaskTest
Txn txn = AccordTestUtils.createWriteTxn((int)clock.incrementAndGet()); Txn txn = AccordTestUtils.createWriteTxn((int)clock.incrementAndGet());
TokenKey key = ((PartitionKey) Iterables.getOnlyElement(txn.keys())).toUnseekable(); TokenKey key = ((PartitionKey) Iterables.getOnlyElement(txn.keys())).toUnseekable();
getUninterruptibly(commandStore.execute((PreLoadContext.Empty)() -> "Test", instance -> { getBlocking(commandStore.execute((PreLoadContext.Empty)() -> "Test", instance -> {
SafeCommandsForKey cfk = instance.ifLoadedAndInitialised(key); SafeCommandsForKey cfk = instance.ifLoadedAndInitialised(key);
Assert.assertNull(cfk); Assert.assertNull(cfk);
})); }));
@ -199,13 +198,11 @@ public class AccordTaskTest
route.slice(ranges); route.slice(ranges);
PartialDeps deps = PartialDeps.builder(ranges, true).build(); PartialDeps deps = PartialDeps.builder(ranges, true).build();
try Command command = getBlocking(commandStore.submit(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> {
{
Command command = getUninterruptibly(commandStore.submit(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> {
CheckedCommands.preaccept(safe, txnId, partialTxn, route, appendDiffToLog(commandStore)); CheckedCommands.preaccept(safe, txnId, partialTxn, route, appendDiffToLog(commandStore));
CheckedCommands.commit(safe, SaveStatus.Stable, Ballot.ZERO, txnId, route, partialTxn, executeAt, deps, appendDiffToLog(commandStore)); CheckedCommands.commit(safe, SaveStatus.Stable, Ballot.ZERO, txnId, route, partialTxn, executeAt, deps, appendDiffToLog(commandStore));
return safe.ifInitialised(txnId).current(); return safe.ifInitialised(txnId).current();
}).beginAsResult()); }));
// clear cache // clear cache
try (ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();) try (ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();)
@ -220,11 +217,6 @@ public class AccordTaskTest
return command; return command;
} }
catch (ExecutionException e)
{
throw new AssertionError(e);
}
}
private static Command createStableUsingSlowLifeCycle(AccordCommandStore commandStore, TxnId txnId) private static Command createStableUsingSlowLifeCycle(AccordCommandStore commandStore, TxnId txnId)
{ {
@ -249,15 +241,13 @@ public class AccordTaskTest
Route<?> partialRoute = route.slice(ranges); Route<?> partialRoute = route.slice(ranges);
PartialDeps deps = PartialDeps.builder(ranges, true).build(); PartialDeps deps = PartialDeps.builder(ranges, true).build();
try Command command = getBlocking(commandStore.submit(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> {
{
Command command = getUninterruptibly(commandStore.submit(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> {
CheckedCommands.preaccept(safe, txnId, partialTxn, route, appendDiffToLog(commandStore)); CheckedCommands.preaccept(safe, txnId, partialTxn, route, appendDiffToLog(commandStore));
CheckedCommands.accept(safe, txnId, Ballot.ZERO, partialRoute, executeAt, deps, appendDiffToLog(commandStore)); CheckedCommands.accept(safe, txnId, Ballot.ZERO, partialRoute, executeAt, deps, appendDiffToLog(commandStore));
CheckedCommands.commit(safe, SaveStatus.Committed, Ballot.ZERO, txnId, route, partialTxn, executeAt, deps, appendDiffToLog(commandStore)); CheckedCommands.commit(safe, SaveStatus.Committed, Ballot.ZERO, txnId, route, partialTxn, executeAt, deps, appendDiffToLog(commandStore));
CheckedCommands.commit(safe, SaveStatus.Stable, Ballot.ZERO, txnId, route, partialTxn, executeAt, deps, appendDiffToLog(commandStore)); CheckedCommands.commit(safe, SaveStatus.Stable, Ballot.ZERO, txnId, route, partialTxn, executeAt, deps, appendDiffToLog(commandStore));
return safe.ifInitialised(txnId).current(); return safe.ifInitialised(txnId).current();
}).beginAsResult()); }));
// clear cache // clear cache
try (ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();) try (ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();)
@ -272,11 +262,6 @@ public class AccordTaskTest
return command; return command;
} }
catch (ExecutionException e)
{
throw new AssertionError(e);
}
}
@Test @Test
public void loadFail() public void loadFail()
@ -318,7 +303,7 @@ public class AccordTaskTest
}); });
} }
AccordTask<Void> o1 = AccordTask.create(commandStore, ctx, consumer); AccordTask<Void> o1 = AccordTask.create(commandStore, ctx, consumer);
AssertionUtils.assertThatThrownBy(() -> getUninterruptibly(o1.chain())) AssertionUtils.assertThatThrownBy(() -> getBlocking(o1.chain()))
.hasRootCause() .hasRootCause()
.isInstanceOf(NullPointerException.class) .isInstanceOf(NullPointerException.class)
.hasNoSuppressedExceptions(); .hasNoSuppressedExceptions();
@ -343,7 +328,7 @@ public class AccordTaskTest
store.ifInitialised(id).readyToExecute(store); store.ifInitialised(id).readyToExecute(store);
}); });
}); });
getUninterruptibly(o2.chain()); getBlocking(o2.chain());
awaitDone(commandStore, ids, participants); awaitDone(commandStore, ids, participants);
assertNoReferences(commandStore, ids, participants); assertNoReferences(commandStore, ids, participants);
@ -376,7 +361,7 @@ public class AccordTaskTest
AccordTask<Void> operation = AccordTask.create(commandStore, ctx, consumer); AccordTask<Void> operation = AccordTask.create(commandStore, ctx, consumer);
AssertionUtils.assertThatThrownBy(() -> getUninterruptibly(operation.chain())) AssertionUtils.assertThatThrownBy(() -> getBlocking(operation.chain()))
.hasRootCause() .hasRootCause()
.isInstanceOf(NullPointerException.class) .isInstanceOf(NullPointerException.class)
.hasMessage(errorMsg) .hasMessage(errorMsg)

View File

@ -116,9 +116,9 @@ import static accord.primitives.SaveStatus.NotDefined;
import static accord.primitives.SaveStatus.PreAccepted; import static accord.primitives.SaveStatus.PreAccepted;
import static accord.primitives.Status.Durability.NotDurable; import static accord.primitives.Status.Durability.NotDurable;
import static accord.primitives.Txn.Kind.Write; import static accord.primitives.Txn.Kind.Write;
import static accord.utils.async.AsyncChains.getUninterruptibly;
import static java.lang.String.format; import static java.lang.String.format;
import static org.apache.cassandra.service.accord.AccordExecutor.Mode.RUN_WITH_LOCK; import static org.apache.cassandra.service.accord.AccordExecutor.Mode.RUN_WITH_LOCK;
import static org.apache.cassandra.service.accord.AccordService.getBlocking;
public class AccordTestUtils public class AccordTestUtils
{ {
@ -238,7 +238,7 @@ public class AccordTestUtils
public static AsyncChain<Pair<Writes, Result>> processTxnResult(AccordCommandStore commandStore, TxnId txnId, PartialTxn txn, Timestamp executeAt) throws Throwable public static AsyncChain<Pair<Writes, Result>> processTxnResult(AccordCommandStore commandStore, TxnId txnId, PartialTxn txn, Timestamp executeAt) throws Throwable
{ {
AtomicReference<AsyncChain<Pair<Writes, Result>>> result = new AtomicReference<>(); AtomicReference<AsyncChain<Pair<Writes, Result>>> result = new AtomicReference<>();
getUninterruptibly(commandStore.execute((PreLoadContext.Empty)() -> "Test", getBlocking(commandStore.execute((PreLoadContext.Empty)() -> "Test",
safeStore -> result.set(processTxnResultDirect(safeStore, txnId, txn, executeAt)))); safeStore -> result.set(processTxnResultDirect(safeStore, txnId, txn, executeAt))));
return result.get(); return result.get();
} }

View File

@ -690,9 +690,9 @@ public class EpochSyncTest
public AsyncResult<Void> onTopologyUpdate(Topology topology, boolean isLoad, boolean startSync) public AsyncResult<Void> onTopologyUpdate(Topology topology, boolean isLoad, boolean startSync)
{ {
AsyncResult<Void> metadata = schedule(rs.nextInt(1, 10), TimeUnit.SECONDS, (Callable<Void>) () -> null).beginAsResult(); AsyncResult<Void> metadata = schedule(rs.nextInt(1, 10), TimeUnit.SECONDS, (Callable<Void>) () -> null).beginAsResult();
AsyncResult<Void> coordination = metadata.flatMap(ignore -> schedule(rs.nextInt(1, 10), TimeUnit.SECONDS, (Callable<Void>) () -> null)).beginAsResult(); AsyncResult<Void> coordination = metadata.flatMap(ignore -> schedule(rs.nextInt(1, 10), TimeUnit.SECONDS, (Callable<Void>) () -> null).beginAsResult());
AsyncResult<Void> data = coordination.flatMap(ignore -> schedule(rs.nextInt(1, 10), TimeUnit.SECONDS, (Callable<Void>) () -> null)).beginAsResult(); AsyncResult<Void> data = coordination.flatMap(ignore -> schedule(rs.nextInt(1, 10), TimeUnit.SECONDS, (Callable<Void>) () -> null).beginAsResult());
AsyncResult<Void> reads = data.flatMap(ignore -> schedule(rs.nextInt(1, 10), TimeUnit.SECONDS, (Callable<Void>) () -> null)).beginAsResult(); AsyncResult<Void> reads = data.flatMap(ignore -> schedule(rs.nextInt(1, 10), TimeUnit.SECONDS, (Callable<Void>) () -> null).beginAsResult());
EpochReady ready = new EpochReady(topology.epoch(), metadata, coordination, data, reads); EpochReady ready = new EpochReady(topology.epoch(), metadata, coordination, data, reads);
topology().onTopologyUpdate(topology, () -> ready, e -> {}); topology().onTopologyUpdate(topology, () -> ready, e -> {});

View File

@ -77,7 +77,6 @@ import accord.topology.Topology;
import accord.topology.TopologyManager; import accord.topology.TopologyManager;
import accord.utils.Gens; import accord.utils.Gens;
import accord.utils.RandomSource; import accord.utils.RandomSource;
import accord.utils.async.AsyncChains;
import accord.utils.async.AsyncResult; import accord.utils.async.AsyncResult;
import org.apache.cassandra.concurrent.ExecutorFactory; import org.apache.cassandra.concurrent.ExecutorFactory;
import org.apache.cassandra.concurrent.ScheduledExecutorPlus; import org.apache.cassandra.concurrent.ScheduledExecutorPlus;
@ -100,6 +99,7 @@ import org.assertj.core.api.Assertions;
import static org.apache.cassandra.config.DatabaseDescriptor.getPartitioner; import static org.apache.cassandra.config.DatabaseDescriptor.getPartitioner;
import static org.apache.cassandra.db.ColumnFamilyStore.FlushReason.UNIT_TESTS; import static org.apache.cassandra.db.ColumnFamilyStore.FlushReason.UNIT_TESTS;
import static org.apache.cassandra.schema.SchemaConstants.ACCORD_KEYSPACE_NAME; import static org.apache.cassandra.schema.SchemaConstants.ACCORD_KEYSPACE_NAME;
import static org.apache.cassandra.service.accord.AccordService.getBlocking;
import static org.apache.cassandra.utils.AccordGenerators.fromQT; import static org.apache.cassandra.utils.AccordGenerators.fromQT;
public class SimulatedAccordCommandStore implements AutoCloseable public class SimulatedAccordCommandStore implements AutoCloseable
@ -430,7 +430,7 @@ public class SimulatedAccordCommandStore implements AutoCloseable
{ {
var result = processAsync(loadCtx, function); var result = processAsync(loadCtx, function);
processAll(); processAll();
return AsyncChains.getBlocking(result); return getBlocking(result);
} }
public <T extends Reply> AsyncResult<T> processAsync(TxnRequest<T> request) public <T extends Reply> AsyncResult<T> processAsync(TxnRequest<T> request)
@ -440,7 +440,7 @@ public class SimulatedAccordCommandStore implements AutoCloseable
public <T extends Reply> AsyncResult<T> processAsync(PreLoadContext loadCtx, Function<? super SafeCommandStore, T> function) public <T extends Reply> AsyncResult<T> processAsync(PreLoadContext loadCtx, Function<? super SafeCommandStore, T> function)
{ {
return commandStore.submit(loadCtx, function).beginAsResult(); return commandStore.submit(loadCtx, function);
} }
public Pair<TxnId, AsyncResult<PreAccept.PreAcceptOk>> enqueuePreAccept(Txn txn, FullRoute<?> route) public Pair<TxnId, AsyncResult<PreAccept.PreAcceptOk>> enqueuePreAccept(Txn txn, FullRoute<?> route)
@ -464,7 +464,7 @@ public class SimulatedAccordCommandStore implements AutoCloseable
var reply = br.apply(safe); var reply = br.apply(safe);
Assertions.assertThat(reply.kind() == BeginRecovery.RecoverReply.Kind.Ok).isTrue(); Assertions.assertThat(reply.kind() == BeginRecovery.RecoverReply.Kind.Ok).isTrue();
return (BeginRecovery.RecoverOk) reply; return (BeginRecovery.RecoverOk) reply;
}).beginAsResult()); }));
} }
public void processAll() public void processAll()

View File

@ -32,6 +32,7 @@ import com.google.common.collect.Maps;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import accord.api.AsyncExecutor;
import accord.api.RoutingKey; import accord.api.RoutingKey;
import accord.impl.SizeOfIntersectionSorter; import accord.impl.SizeOfIntersectionSorter;
import accord.local.Node; import accord.local.Node;
@ -56,7 +57,6 @@ import accord.topology.Topologies;
import accord.utils.Gen; import accord.utils.Gen;
import accord.utils.Gens; import accord.utils.Gens;
import accord.utils.Invariants; import accord.utils.Invariants;
import accord.utils.async.AsyncChains;
import accord.utils.async.AsyncResult; import accord.utils.async.AsyncResult;
import org.apache.cassandra.ServerTestUtils; import org.apache.cassandra.ServerTestUtils;
import org.apache.cassandra.config.CassandraRelevantProperties; import org.apache.cassandra.config.CassandraRelevantProperties;
@ -77,6 +77,7 @@ import org.apache.cassandra.utils.RangeTree;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import static org.apache.cassandra.schema.SchemaConstants.ACCORD_KEYSPACE_NAME; import static org.apache.cassandra.schema.SchemaConstants.ACCORD_KEYSPACE_NAME;
import static org.apache.cassandra.service.accord.AccordService.getBlocking;
import static org.apache.cassandra.service.accord.AccordTestUtils.createTxn; import static org.apache.cassandra.service.accord.AccordTestUtils.createTxn;
public abstract class SimulatedAccordCommandStoreTestBase extends CQLTester public abstract class SimulatedAccordCommandStoreTestBase extends CQLTester
@ -202,7 +203,7 @@ public abstract class SimulatedAccordCommandStoreTestBase extends CQLTester
{ {
var pair = assertDepsMessageAsync(instance, messageType, txn, route, keyConflicts, rangeConflicts); var pair = assertDepsMessageAsync(instance, messageType, txn, route, keyConflicts, rangeConflicts);
instance.processAll(); instance.processAll();
AsyncChains.getBlocking(pair.right); getBlocking(pair.right);
return pair.left; return pair.left;
} }
@ -241,7 +242,7 @@ public abstract class SimulatedAccordCommandStoreTestBase extends CQLTester
return Pair.create(pair.left, pair.right.map(success -> { return Pair.create(pair.left, pair.right.map(success -> {
assertDeps(success.txnId, success.deps, cloneKeyConflicts, cloneRangeConflicts); assertDeps(success.txnId, success.deps, cloneKeyConflicts, cloneRangeConflicts);
return null; return null;
}).beginAsResult()); }));
} }
protected static Pair<TxnId, AsyncResult<?>> assertBeginRecoveryAsync(SimulatedAccordCommandStore instance, protected static Pair<TxnId, AsyncResult<?>> assertBeginRecoveryAsync(SimulatedAccordCommandStore instance,
@ -260,7 +261,7 @@ public abstract class SimulatedAccordCommandStoreTestBase extends CQLTester
Deps proposeDeps = LatestDeps.mergeProposal(Collections.singletonList(success), ok -> ok.deps); Deps proposeDeps = LatestDeps.mergeProposal(Collections.singletonList(success), ok -> ok.deps);
assertDeps(success.txnId, proposeDeps, cloneKeyConflicts, cloneRangeConflicts); assertDeps(success.txnId, proposeDeps, cloneKeyConflicts, cloneRangeConflicts);
return null; return null;
}).beginAsResult()); }));
} }
protected static Pair<TxnId, AsyncResult<?>> assertBeginRecoveryAfterPreAcceptAsync(SimulatedAccordCommandStore instance, protected static Pair<TxnId, AsyncResult<?>> assertBeginRecoveryAfterPreAcceptAsync(SimulatedAccordCommandStore instance,
@ -285,10 +286,10 @@ public abstract class SimulatedAccordCommandStoreTestBase extends CQLTester
assertDeps(success.txnId, success.deps, cloneKeyConflicts, cloneRangeConflicts); assertDeps(success.txnId, success.deps, cloneKeyConflicts, cloneRangeConflicts);
return success; return success;
}); });
var delay = preAcceptAsync.flatMap(ignore -> AsyncChains.ofCallable(instance.unorderedScheduled, () -> { var delay = preAcceptAsync.flatMap(ignore -> AsyncExecutor.chain(instance.unorderedScheduled, () -> {
Ballot ballot = Ballot.fromValues(instance.storeService.epoch(), instance.storeService.now(), nodeId); Ballot ballot = Ballot.fromValues(instance.storeService.epoch(), instance.storeService.now(), nodeId);
return new BeginRecovery(nodeId, new Topologies.Single(SizeOfIntersectionSorter.SUPPLIER, instance.topology), txnId, null, false, txn, route, ballot); return new BeginRecovery(nodeId, new Topologies.Single(SizeOfIntersectionSorter.SUPPLIER, instance.topology), txnId, null, false, txn, route, ballot);
})); }).beginAsResult());
var recoverAsync = delay.flatMap(br -> instance.processAsync(br, safe -> { var recoverAsync = delay.flatMap(br -> instance.processAsync(br, safe -> {
var reply = br.apply(safe); var reply = br.apply(safe);
Assertions.assertThat(reply.kind() == BeginRecovery.RecoverReply.Kind.Ok).isTrue(); Assertions.assertThat(reply.kind() == BeginRecovery.RecoverReply.Kind.Ok).isTrue();
@ -298,7 +299,7 @@ public abstract class SimulatedAccordCommandStoreTestBase extends CQLTester
return success; return success;
})); }));
return Pair.create(txnId, recoverAsync.beginAsResult()); return Pair.create(txnId, recoverAsync);
} }
protected static void assertDeps(TxnId txnId, Deps deps, protected static void assertDeps(TxnId txnId, Deps deps,

View File

@ -129,7 +129,7 @@ public class SimulatedAccordTaskTest extends SimulatedAccordCommandStoreTestBase
} }
}; };
instance.maybeCacheEvict(txn.keys().toParticipants()); instance.maybeCacheEvict(txn.keys().toParticipants());
instance.processAsync(preAccept).begin(counter); instance.processAsync(preAccept).invoke(counter);
} }
break; break;
default: default:

View File

@ -28,7 +28,6 @@ import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -649,14 +648,15 @@ public class CommandsForKeySerializerTest
} }
@Override public boolean inStore() { return true; } @Override public boolean inStore() { return true; }
@Override public AsyncChain<Void> chain(PreLoadContext context, Consumer<? super SafeCommandStore> consumer) { throw new UnsupportedOperationException();}
@Override public <T> AsyncChain<T> chain(PreLoadContext context, Function<? super SafeCommandStore, T> apply) { throw new UnsupportedOperationException(); }
@Override public Journal.Replayer replayer() { throw new UnsupportedOperationException(); } @Override public Journal.Replayer replayer() { throw new UnsupportedOperationException(); }
@Override protected void ensureDurable(Ranges ranges, RedundantBefore onSuccess) {} @Override protected void ensureDurable(Ranges ranges, RedundantBefore onSuccess) {}
@Override public Agent agent() { return this; } @Override public Agent agent() { return this; }
@Override public AsyncChain<Void> build(PreLoadContext context, Consumer<? super SafeCommandStore> consumer) { return null; } @Override public void execute(Runnable run) {}
@Override public <T> AsyncChain<T> build(PreLoadContext context, Function<? super SafeCommandStore, T> apply) { throw new UnsupportedOperationException(); }
@Override public void shutdown() { } @Override public void shutdown() { }
@Override public <T> AsyncChain<T> build(Callable<T> task) { throw new UnsupportedOperationException(); }
@Override public void onFailedBootstrap(int attempts, String phase, Ranges ranges, Runnable retry, Throwable failure) { throw new UnsupportedOperationException(); } @Override public void onFailedBootstrap(int attempts, String phase, Ranges ranges, Runnable retry, Throwable failure) { throw new UnsupportedOperationException(); }
@Override public void onStale(Timestamp staleSince, Ranges ranges) { throw new UnsupportedOperationException(); } @Override public void onStale(Timestamp staleSince, Ranges ranges) { throw new UnsupportedOperationException(); }
@Override public void onUncaughtException(Throwable t) { throw new UnsupportedOperationException(); } @Override public void onUncaughtException(Throwable t) { throw new UnsupportedOperationException(); }

View File

@ -26,8 +26,6 @@ import org.assertj.core.api.AbstractThrowableAssert;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import org.assertj.core.api.Condition; import org.assertj.core.api.Condition;
import org.assertj.core.api.ThrowableAssert; import org.assertj.core.api.ThrowableAssert;
import org.assertj.core.error.BasicErrorMessageFactory;
import org.assertj.core.internal.Failures;
public class AssertionUtils public class AssertionUtils
{ {
@ -191,8 +189,6 @@ public class AssertionUtils
public ThrowableAssertPlus hasRootCause() public ThrowableAssertPlus hasRootCause()
{ {
Throwable cause = Throwables.getRootCause(actual); Throwable cause = Throwables.getRootCause(actual);
if (cause == actual)
throw Failures.instance().failure(this.info, new BasicErrorMessageFactory("%nExpected a root cause but cause was null", new Object[0]));
return new ThrowableAssertPlus(cause); return new ThrowableAssertPlus(cause);
} }
} }