mirror of https://github.com/apache/cassandra
Improve:
- Introduce SequentialAgentExecutor - ExecuteEphemeralRead.LocalExecute (matching ExecuteTxn.LocalExecute) - remove AgentExecutor (agent() only used as implementation detail) - Skip CommandStore/CommandsForKey on read or apply, when safe to do so - Implement maybeExecuteImmediately - Faster maxTimestamp Fix: - Marking vestigial without knowing all covering keys - Incorrect size in WaitingOn.none - Cleanup RX that are not known but are no longer needed, due to being defunct - Home should not abort because of local truncation; may still be incomplete on another shard - Invalidate infinite loop due to interpreting Vestigial as a possible decision (as though it were another Truncated state) - Known.isTruncated reporting incorrect answer in some cases, leading to infinite RecoverWithRoute loops as not slicing to correct subset and some shards are truncated and reject the deps collection - BurnTest slowCoordinatorDelay should grow with retryCount - NotAccept should check both ballot and saveStatus; PreCommitted or later can be propagated by Apply that has an earlier ballot - Handle awaiting locally retired epoch - Fix propagate low epoch of sync points - Fix GetEphemeralReadDeps.reduce NPE - Don't use tombstones to ERASE journal entries, as want to produce an Erased SaveStatus until EXPUNGE patch by Benedict; reviewed by Alex Petrov for CASSANDRA-20726
This commit is contained in:
parent
8d641e06fc
commit
6d303e6843
|
|
@ -1 +1 @@
|
|||
Subproject commit 07ba9c85d083b586ff5c2382ef747b0fe71a9c47
|
||||
Subproject commit 154c05f50dcfe9cb69d14e4a67a137ecd0600e88
|
||||
|
|
@ -42,9 +42,14 @@ public interface DebuggableTask
|
|||
public long startTimeNanos();
|
||||
|
||||
public String description();
|
||||
|
||||
|
||||
interface RunnableDebuggableTask extends Runnable, DebuggableTask {}
|
||||
interface CallableDebuggableTask<T> extends Callable<T>, DebuggableTask {}
|
||||
interface DebuggableTaskRunner
|
||||
{
|
||||
DebuggableTask running();
|
||||
String id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a {@link DebuggableTask} to include the name of the thread running it.
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import static org.apache.cassandra.concurrent.SEPExecutor.TakeTaskPermitResult.T
|
|||
import static org.apache.cassandra.config.CassandraRelevantProperties.SET_SEP_THREAD_NAME;
|
||||
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
|
||||
|
||||
final class SEPWorker extends AtomicReference<SEPWorker.Work> implements Runnable
|
||||
final class SEPWorker extends AtomicReference<SEPWorker.Work> implements Runnable, DebuggableTask.DebuggableTaskRunner
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(SEPWorker.class);
|
||||
private static final boolean SET_THREAD_NAME = SET_SEP_THREAD_NAME.getBoolean();
|
||||
|
|
@ -63,10 +63,8 @@ final class SEPWorker extends AtomicReference<SEPWorker.Work> implements Runnabl
|
|||
thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current {@link DebuggableTask}, if one exists
|
||||
*/
|
||||
public DebuggableTask currentDebuggableTask()
|
||||
@Override
|
||||
public DebuggableTask running()
|
||||
{
|
||||
// can change after null check so go off local reference
|
||||
Runnable task = currentTask.get();
|
||||
|
|
@ -77,10 +75,17 @@ final class SEPWorker extends AtomicReference<SEPWorker.Work> implements Runnabl
|
|||
|
||||
if (task instanceof FutureTask)
|
||||
return ((FutureTask<?>) task).debuggableTask();
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// note this is not guaranteed to be in sync with running()
|
||||
@Override
|
||||
public String id()
|
||||
{
|
||||
return thread.getName();
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ import java.util.concurrent.TimeoutException;
|
|||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.cassandra.concurrent.DebuggableTask.RunningDebuggableTask;
|
||||
import org.apache.cassandra.concurrent.DebuggableTask.DebuggableTaskRunner;
|
||||
|
||||
import static org.apache.cassandra.concurrent.ExecutorFactory.Global.executorFactory;
|
||||
import static org.apache.cassandra.concurrent.SEPWorker.Work;
|
||||
|
|
@ -121,12 +121,9 @@ public class SharedExecutorPool
|
|||
allWorkers.remove(worker);
|
||||
}
|
||||
|
||||
public List<RunningDebuggableTask> runningTasks()
|
||||
public Stream<? extends DebuggableTaskRunner> workers()
|
||||
{
|
||||
return allWorkers.stream()
|
||||
.map(worker -> new RunningDebuggableTask(worker.toString(), worker.currentDebuggableTask()))
|
||||
.filter(RunningDebuggableTask::hasTask)
|
||||
.collect(Collectors.toList());
|
||||
return allWorkers.stream();
|
||||
}
|
||||
|
||||
void maybeStartSpinningWorker()
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ import static org.apache.cassandra.config.Config.PaxosStatePurging.legacy;
|
|||
import static org.apache.cassandra.config.DatabaseDescriptor.paxosStatePurging;
|
||||
import static org.apache.cassandra.service.accord.AccordKeyspace.CFKAccessor;
|
||||
import static org.apache.cassandra.service.accord.AccordKeyspace.JournalColumns.getJournalKey;
|
||||
import static org.apache.cassandra.service.accord.AccordKeyspace.JournalColumns.key;
|
||||
|
||||
/**
|
||||
* Merge multiple iterators over the content of sstable into a "compacted" iterator.
|
||||
|
|
@ -1066,8 +1067,6 @@ public class CompactionIterator extends CompactionInfo.Holder implements Unfilte
|
|||
for (int i = 0; i < entries.size() ; ++i)
|
||||
entries.get(i).clear();
|
||||
entries.clear();
|
||||
if (!partition.partitionLevelDeletion().isLive())
|
||||
mainBuilder.addCleanup(false, ERASE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1138,13 +1137,22 @@ public class CompactionIterator extends CompactionInfo.Holder implements Unfilte
|
|||
return newVersion.build().unfilteredIterator();
|
||||
}
|
||||
|
||||
private UnfilteredRowIterator erase(DecoratedKey partitionKey)
|
||||
private UnfilteredRowIterator erase(DecoratedKey partitionKey) throws IOException
|
||||
{
|
||||
return PartitionUpdate.fullPartitionDelete(AccordKeyspace.Journal, partitionKey, Long.MAX_VALUE, nowInSec).unfilteredIterator();
|
||||
AccordCommandRowEntry entry = entries.get(entries.size() - 1);
|
||||
entry.builder.addCleanup(false, ERASE);
|
||||
return PartitionUpdate.singleRowUpdate(AccordKeyspace.Journal, partitionKey, toRow(entry)).unfilteredIterator();
|
||||
}
|
||||
|
||||
private BTreeRow toRow(AccordCommandRowEntry entry) throws IOException
|
||||
{
|
||||
Object[] newRow = rowTemplate.clone();
|
||||
newRow[0] = BufferCell.live(AccordKeyspace.JournalColumns.record, timestamp, entry.builder.asByteBuffer(userVersion));
|
||||
newRow[1] = userVersionCell;
|
||||
return BTreeRow.create(entry.row.clustering(), entry.row.primaryKeyLivenessInfo(), entry.row.deletion(), newRow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class AbortableUnfilteredPartitionTransformation extends Transformation<UnfilteredRowIterator>
|
||||
{
|
||||
private final AbortableUnfilteredRowTransformation abortableIter;
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ import org.apache.cassandra.utils.SearchIterator;
|
|||
import org.apache.cassandra.utils.btree.BTree;
|
||||
import org.apache.cassandra.utils.btree.BTree.Dir;
|
||||
|
||||
import static org.apache.cassandra.db.rows.Rows.EMPTY_STATIC_ROW;
|
||||
import static org.apache.cassandra.utils.btree.BTree.Dir.desc;
|
||||
|
||||
public abstract class AbstractBTreePartition implements Partition, Iterable<Row>
|
||||
|
|
@ -442,4 +443,32 @@ public abstract class AbstractBTreePartition implements Partition, Iterable<Row>
|
|||
|
||||
return BTree.findByIndex(tree, BTree.size(tree) - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum timestamp used in this partition.
|
||||
*
|
||||
* @return the maximum timestamp used in this update.
|
||||
*/
|
||||
public long maxTimestamp()
|
||||
{
|
||||
return maxTimestamp(0);
|
||||
}
|
||||
|
||||
protected long maxTimestamp(long atLeast)
|
||||
{
|
||||
BTreePartitionData holder = holder();
|
||||
long maxTimestamp = BTree.<Row>accumulate(holder.tree, (row, maxRowTs) -> {
|
||||
return row.accumulate((cd, maxCdTs) -> {
|
||||
return Math.max(cd.maxTimestamp(), maxCdTs);
|
||||
}, Math.max(row.primaryKeyLivenessInfo().timestamp(), maxRowTs));
|
||||
}, Math.max(atLeast, holder.deletionInfo.maxTimestamp()));
|
||||
|
||||
Row staticRow = holder.staticRow;
|
||||
if (staticRow != EMPTY_STATIC_ROW)
|
||||
{
|
||||
maxTimestamp = Math.max(maxTimestamp, staticRow.primaryKeyLivenessInfo().timestamp());
|
||||
maxTimestamp = staticRow.accumulate((cd, maxCdTs) -> Math.max(cd.maxTimestamp(), maxCdTs), maxTimestamp);
|
||||
}
|
||||
return maxTimestamp;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ import org.apache.cassandra.db.rows.BTreeRow;
|
|||
import org.apache.cassandra.db.rows.Cell;
|
||||
import org.apache.cassandra.db.rows.CellPath;
|
||||
import org.apache.cassandra.db.rows.ColumnData;
|
||||
import org.apache.cassandra.db.rows.ComplexColumnData;
|
||||
import org.apache.cassandra.db.rows.DeserializationHelper;
|
||||
import org.apache.cassandra.db.rows.EncodingStats;
|
||||
import org.apache.cassandra.db.rows.RangeTombstoneMarker;
|
||||
|
|
@ -87,6 +86,7 @@ import org.apache.cassandra.utils.btree.UpdateFunction;
|
|||
import org.apache.cassandra.utils.vint.VIntCoding;
|
||||
|
||||
import static org.apache.cassandra.db.SerializationHeader.StableHeaderSerializer.STABLE;
|
||||
import static org.apache.cassandra.db.rows.Rows.EMPTY_STATIC_ROW;
|
||||
import static org.apache.cassandra.db.rows.UnfilteredRowIteratorSerializer.IS_EMPTY;
|
||||
|
||||
/**
|
||||
|
|
@ -142,7 +142,7 @@ public class PartitionUpdate extends AbstractBTreePartition
|
|||
public static PartitionUpdate emptyUpdate(TableMetadata metadata, DecoratedKey key)
|
||||
{
|
||||
MutableDeletionInfo deletionInfo = MutableDeletionInfo.live();
|
||||
BTreePartitionData holder = new BTreePartitionData(RegularAndStaticColumns.NONE, BTree.empty(), deletionInfo, Rows.EMPTY_STATIC_ROW, EncodingStats.NO_STATS);
|
||||
BTreePartitionData holder = new BTreePartitionData(RegularAndStaticColumns.NONE, BTree.empty(), deletionInfo, EMPTY_STATIC_ROW, EncodingStats.NO_STATS);
|
||||
return new PartitionUpdate(metadata, metadata.epoch, key, holder, deletionInfo, false);
|
||||
}
|
||||
|
||||
|
|
@ -159,7 +159,7 @@ public class PartitionUpdate extends AbstractBTreePartition
|
|||
public static PartitionUpdate fullPartitionDelete(TableMetadata metadata, DecoratedKey key, long timestamp, long nowInSec)
|
||||
{
|
||||
MutableDeletionInfo deletionInfo = new MutableDeletionInfo(timestamp, nowInSec);
|
||||
BTreePartitionData holder = new BTreePartitionData(RegularAndStaticColumns.NONE, BTree.empty(), deletionInfo, Rows.EMPTY_STATIC_ROW, EncodingStats.NO_STATS);
|
||||
BTreePartitionData holder = new BTreePartitionData(RegularAndStaticColumns.NONE, BTree.empty(), deletionInfo, EMPTY_STATIC_ROW, EncodingStats.NO_STATS);
|
||||
return new PartitionUpdate(metadata, metadata.epoch, key, holder, deletionInfo, false);
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +183,7 @@ public class PartitionUpdate extends AbstractBTreePartition
|
|||
),
|
||||
row == null ? BTree.empty() : BTree.singleton(row),
|
||||
deletionInfo,
|
||||
staticRow == null ? Rows.EMPTY_STATIC_ROW : staticRow,
|
||||
staticRow == null ? EMPTY_STATIC_ROW : staticRow,
|
||||
EncodingStats.NO_STATS
|
||||
);
|
||||
return new PartitionUpdate(metadata, metadata.epoch, key, holder, deletionInfo, false);
|
||||
|
|
@ -222,7 +222,7 @@ public class PartitionUpdate extends AbstractBTreePartition
|
|||
columns = columns.mergeTo(Columns.from(row));
|
||||
|
||||
BTreePartitionData holder = new BTreePartitionData(new RegularAndStaticColumns(Columns.NONE, columns),
|
||||
BTree.build(rows), deletionInfo, Rows.EMPTY_STATIC_ROW,
|
||||
BTree.build(rows), deletionInfo, EMPTY_STATIC_ROW,
|
||||
EncodingStats.NO_STATS);
|
||||
return new PartitionUpdate(metadata, metadata.epoch, key, holder, deletionInfo, false);
|
||||
}
|
||||
|
|
@ -484,44 +484,7 @@ public class PartitionUpdate extends AbstractBTreePartition
|
|||
*/
|
||||
public long maxTimestamp()
|
||||
{
|
||||
long maxTimestamp = deletionInfo.maxTimestamp();
|
||||
for (Row row : this)
|
||||
{
|
||||
maxTimestamp = Math.max(maxTimestamp, row.primaryKeyLivenessInfo().timestamp());
|
||||
for (ColumnData cd : row)
|
||||
{
|
||||
if (cd.column().isSimple())
|
||||
{
|
||||
maxTimestamp = Math.max(maxTimestamp, ((Cell<?>)cd).timestamp());
|
||||
}
|
||||
else
|
||||
{
|
||||
ComplexColumnData complexData = (ComplexColumnData)cd;
|
||||
maxTimestamp = Math.max(maxTimestamp, complexData.complexDeletion().markedForDeleteAt());
|
||||
for (Cell<?> cell : complexData)
|
||||
maxTimestamp = Math.max(maxTimestamp, cell.timestamp());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.holder.staticRow != null)
|
||||
{
|
||||
for (ColumnData cd : this.holder.staticRow.columnData())
|
||||
{
|
||||
if (cd.column().isSimple())
|
||||
{
|
||||
maxTimestamp = Math.max(maxTimestamp, ((Cell<?>) cd).timestamp());
|
||||
}
|
||||
else
|
||||
{
|
||||
ComplexColumnData complexData = (ComplexColumnData) cd;
|
||||
maxTimestamp = Math.max(maxTimestamp, complexData.complexDeletion().markedForDeleteAt());
|
||||
for (Cell<?> cell : complexData)
|
||||
maxTimestamp = Math.max(maxTimestamp, cell.timestamp());
|
||||
}
|
||||
}
|
||||
}
|
||||
return maxTimestamp;
|
||||
return maxTimestamp(deletionInfo.maxTimestamp());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1003,7 +966,7 @@ public class PartitionUpdate extends AbstractBTreePartition
|
|||
private BTree.Builder<Row> rowBuilder;
|
||||
|
||||
private final int initialRowCapacity;
|
||||
private Row staticRow = Rows.EMPTY_STATIC_ROW;
|
||||
private Row staticRow = EMPTY_STATIC_ROW;
|
||||
private final RegularAndStaticColumns columns;
|
||||
private boolean isBuilt = false;
|
||||
|
||||
|
|
@ -1013,7 +976,7 @@ public class PartitionUpdate extends AbstractBTreePartition
|
|||
int initialRowCapacity,
|
||||
boolean canHaveShadowedData)
|
||||
{
|
||||
this(metadata, key, columns, initialRowCapacity, canHaveShadowedData, Rows.EMPTY_STATIC_ROW, MutableDeletionInfo.live(), BTree.empty());
|
||||
this(metadata, key, columns, initialRowCapacity, canHaveShadowedData, EMPTY_STATIC_ROW, MutableDeletionInfo.live(), BTree.empty());
|
||||
}
|
||||
|
||||
public Builder(TableMetadata metadata,
|
||||
|
|
|
|||
|
|
@ -330,7 +330,6 @@ public class BTreeRow extends AbstractRow
|
|||
if (!mayFilterColumns && !mayHaveShadowed && droppedColumns.isEmpty())
|
||||
return this;
|
||||
|
||||
|
||||
LivenessInfo newInfo = primaryKeyLivenessInfo;
|
||||
Deletion newDeletion = deletion;
|
||||
if (mayHaveShadowed)
|
||||
|
|
|
|||
|
|
@ -286,10 +286,7 @@ public class ComplexColumnData extends ColumnData implements Iterable<Cell<?>>
|
|||
|
||||
public long maxTimestamp()
|
||||
{
|
||||
long timestamp = complexDeletion.markedForDeleteAt();
|
||||
for (Cell<?> cell : this)
|
||||
timestamp = Math.max(timestamp, cell.timestamp());
|
||||
return timestamp;
|
||||
return BTree.<Cell>accumulate(cells, (cell, ts) -> Math.max(ts, cell.timestamp()), complexDeletion.markedForDeleteAt());
|
||||
}
|
||||
|
||||
// This is the partner in crime of ArrayBackedRow.setValue. The exact warning apply. The short
|
||||
|
|
|
|||
|
|
@ -17,12 +17,16 @@
|
|||
*/
|
||||
package org.apache.cassandra.db.virtual;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.cassandra.concurrent.DebuggableTask;
|
||||
import org.apache.cassandra.concurrent.SharedExecutorPool;
|
||||
import org.apache.cassandra.db.marshal.LongType;
|
||||
import org.apache.cassandra.db.marshal.UTF8Type;
|
||||
import org.apache.cassandra.dht.LocalPartitioner;
|
||||
import org.apache.cassandra.schema.TableMetadata;
|
||||
import org.apache.cassandra.service.accord.AccordExecutor;
|
||||
import org.apache.cassandra.service.accord.AccordService;
|
||||
|
||||
import static java.lang.Long.max;
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
|
|
@ -72,24 +76,29 @@ final class QueriesTable extends AbstractVirtualTable
|
|||
public DataSet data()
|
||||
{
|
||||
SimpleDataSet result = new SimpleDataSet(metadata());
|
||||
|
||||
for (DebuggableTask.RunningDebuggableTask task : SharedExecutorPool.SHARED.runningTasks())
|
||||
{
|
||||
if (!task.hasTask()) continue;
|
||||
|
||||
|
||||
Stream<? extends DebuggableTask.DebuggableTaskRunner> streams = Stream.concat(
|
||||
SharedExecutorPool.SHARED.workers(),
|
||||
AccordService.started() ? AccordService.instance().executors().stream().flatMap(AccordExecutor::active) : Stream.of()
|
||||
);
|
||||
streams.forEach(runner -> {
|
||||
DebuggableTask task = runner.running();
|
||||
if (task == null) return;
|
||||
|
||||
String id = runner.id();
|
||||
long creationTimeNanos = task.creationTimeNanos();
|
||||
long startTimeNanos = task.startTimeNanos();
|
||||
long now = nanoTime();
|
||||
|
||||
long queuedMicros = NANOSECONDS.toMicros(max((startTimeNanos > 0 ? startTimeNanos : now) - creationTimeNanos, 0));
|
||||
long runningMicros = startTimeNanos > 0 ? NANOSECONDS.toMicros(now - startTimeNanos) : 0;
|
||||
|
||||
result.row(task.threadId())
|
||||
|
||||
result.row(id)
|
||||
.column(QUEUED, queuedMicros)
|
||||
.column(RUNNING, runningMicros)
|
||||
.column(DESC, task.description());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public class AccordMetrics
|
|||
public final static AccordMetrics writeMetrics = new AccordMetrics("rw");
|
||||
|
||||
public static final String STABLE_LATENCY = "StableLatency";
|
||||
public static final String EXECUTE_LATENCY = "ExecuteLatency";
|
||||
public static final String PREAPPLY_LATENCY = "PreApplyLatency";
|
||||
public static final String APPLY_LATENCY = "ApplyLatency";
|
||||
public static final String APPLY_DURATION = "ApplyDuration";
|
||||
public static final String PARTIAL_DEPENDENCIES = "PartialDependencies";
|
||||
|
|
@ -67,9 +67,9 @@ public class AccordMetrics
|
|||
public final Timer stableLatency;
|
||||
|
||||
/**
|
||||
* The time between start on the coordinator and execution on this replica.
|
||||
* The time between start on the coordinator and arrival of the result on this replica.
|
||||
*/
|
||||
public final Timer executeLatency;
|
||||
public final Timer preapplyLatency;
|
||||
|
||||
/**
|
||||
* The time between start on the coordinator and application on this replica.
|
||||
|
|
@ -77,21 +77,26 @@ public class AccordMetrics
|
|||
public final Timer applyLatency;
|
||||
|
||||
/**
|
||||
* TODO (expected): probably more interesting is latency from preapplied to apply;
|
||||
* we already track local write latencies, whch this effectively duplicates (but including queueing latencies)
|
||||
* Duration of applying changes.
|
||||
*/
|
||||
public final Timer applyDuration;
|
||||
|
||||
/**
|
||||
* A histogram of the number of dependencies per partial transaction at this replica.
|
||||
* A histogram of the number of dependencies per transaction at this replica.
|
||||
*/
|
||||
public final Histogram partialDependencies;
|
||||
public final Histogram localDependencies;
|
||||
|
||||
/**
|
||||
* TODO (expected): this should be a Gauge
|
||||
*/
|
||||
public final Meter progressLogSize;
|
||||
|
||||
/**
|
||||
* A histogram of the number of dependencies per transaction at this coordinator.
|
||||
*/
|
||||
public final Histogram dependencies;
|
||||
public final Histogram coordinatorDependencies;
|
||||
|
||||
/**
|
||||
* The number of fast path transactions executed on this coordinator.
|
||||
|
|
@ -137,14 +142,14 @@ public class AccordMetrics
|
|||
{
|
||||
DefaultNameFactory replica = new DefaultNameFactory(ACCORD_REPLICA, scope);
|
||||
stableLatency = Metrics.timer(replica.createMetricName(STABLE_LATENCY));
|
||||
executeLatency = Metrics.timer(replica.createMetricName(EXECUTE_LATENCY));
|
||||
preapplyLatency = Metrics.timer(replica.createMetricName(PREAPPLY_LATENCY));
|
||||
applyLatency = Metrics.timer(replica.createMetricName(APPLY_LATENCY));
|
||||
applyDuration = Metrics.timer(replica.createMetricName(APPLY_DURATION));
|
||||
partialDependencies = Metrics.histogram(replica.createMetricName(PARTIAL_DEPENDENCIES), true);
|
||||
localDependencies = Metrics.histogram(replica.createMetricName(PARTIAL_DEPENDENCIES), true);
|
||||
progressLogSize = Metrics.meter(replica.createMetricName(PROGRESS_LOG_SIZE));
|
||||
|
||||
DefaultNameFactory coordinator = new DefaultNameFactory(ACCORD_COORDINATOR, scope);
|
||||
dependencies = Metrics.histogram(coordinator.createMetricName(DEPENDENCIES), true);
|
||||
coordinatorDependencies = Metrics.histogram(coordinator.createMetricName(DEPENDENCIES), true);
|
||||
fastPaths = Metrics.meter(coordinator.createMetricName(FAST_PATHS));
|
||||
slowPaths = Metrics.meter(coordinator.createMetricName(SLOW_PATHS));
|
||||
preempts = Metrics.meter(coordinator.createMetricName(PREEMPTS));
|
||||
|
|
@ -217,16 +222,16 @@ public class AccordMetrics
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onExecuted(Command cmd)
|
||||
public void onPreApplied(Command cmd)
|
||||
{
|
||||
long now = AccordTimeService.nowMicros();
|
||||
AccordMetrics metrics = forTransaction(cmd.txnId());
|
||||
if (metrics != null)
|
||||
{
|
||||
Timestamp trxTimestamp = cmd.txnId();
|
||||
metrics.executeLatency.update(now - trxTimestamp.hlc(), TimeUnit.MICROSECONDS);
|
||||
metrics.preapplyLatency.update(now - trxTimestamp.hlc(), TimeUnit.MICROSECONDS);
|
||||
PartialDeps deps = cmd.partialDeps();
|
||||
metrics.partialDependencies.update(deps != null ? deps.txnIdCount() : 0);
|
||||
metrics.localDependencies.update(deps != null ? deps.txnIdCount() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -239,7 +244,8 @@ public class AccordMetrics
|
|||
{
|
||||
Timestamp trxTimestamp = cmd.txnId();
|
||||
metrics.applyLatency.update(now - trxTimestamp.hlc(), TimeUnit.MICROSECONDS);
|
||||
metrics.applyDuration.update(now - applyStartTimestamp, TimeUnit.MICROSECONDS);
|
||||
if (applyStartTimestamp > 0)
|
||||
metrics.applyDuration.update(now - applyStartTimestamp, TimeUnit.MICROSECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -250,7 +256,7 @@ public class AccordMetrics
|
|||
if (metrics != null)
|
||||
{
|
||||
metrics.fastPaths.mark();
|
||||
metrics.dependencies.update(deps.txnIdCount());
|
||||
metrics.coordinatorDependencies.update(deps.txnIdCount());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -261,7 +267,7 @@ public class AccordMetrics
|
|||
if (metrics != null)
|
||||
{
|
||||
metrics.slowPaths.mark();
|
||||
metrics.dependencies.update(deps.txnIdCount());
|
||||
metrics.coordinatorDependencies.update(deps.txnIdCount());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ import org.apache.cassandra.service.accord.serializers.InformDurableSerializers;
|
|||
import org.apache.cassandra.service.accord.serializers.LatestDepsSerializers;
|
||||
import org.apache.cassandra.service.accord.serializers.PreacceptSerializers;
|
||||
import org.apache.cassandra.service.accord.serializers.GetDurableBeforeSerializers;
|
||||
import org.apache.cassandra.service.accord.serializers.ReadDataSerializers;
|
||||
import org.apache.cassandra.service.accord.serializers.ReadDataSerializer;
|
||||
import org.apache.cassandra.service.accord.serializers.RecoverySerializers;
|
||||
import org.apache.cassandra.service.accord.serializers.SetDurableSerializers;
|
||||
import org.apache.cassandra.service.accord.serializers.Version;
|
||||
|
|
@ -318,14 +318,14 @@ public enum Verb
|
|||
ACCORD_ACCEPT_RSP (122, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(AcceptSerializers.reply), AccordService::responseHandlerOrNoop ),
|
||||
ACCORD_ACCEPT_REQ (123, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(AcceptSerializers.request), AccordService::requestHandlerOrNoop, ACCORD_ACCEPT_RSP ),
|
||||
ACCORD_NOT_ACCEPT_REQ (124, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(AcceptSerializers.notAccept), AccordService::requestHandlerOrNoop, ACCORD_ACCEPT_RSP ),
|
||||
ACCORD_READ_RSP (125, P2, readTimeout, IMMEDIATE, () -> accordEmbedded(ReadDataSerializers.reply), AccordService::responseHandlerOrNoop ),
|
||||
ACCORD_READ_REQ (126, P2, readTimeout, IMMEDIATE, () -> accordEmbedded(ReadDataSerializers.readData), AccordService::requestHandlerOrNoop, ACCORD_READ_RSP ),
|
||||
ACCORD_STABLE_THEN_READ_REQ (127, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(ReadDataSerializers.stableThenRead), AccordService::requestHandlerOrNoop, ACCORD_READ_RSP ),
|
||||
ACCORD_READ_RSP (125, P2, readTimeout, IMMEDIATE, () -> accordEmbedded(ReadDataSerializer.reply), AccordService::responseHandlerOrNoop ),
|
||||
ACCORD_READ_REQ (126, P2, readTimeout, IMMEDIATE, () -> accordEmbedded(ReadDataSerializer.request), AccordService::requestHandlerOrNoop, ACCORD_READ_RSP ),
|
||||
ACCORD_STABLE_THEN_READ_REQ (127, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(ReadDataSerializer.request), AccordService::requestHandlerOrNoop, ACCORD_READ_RSP ),
|
||||
ACCORD_COMMIT_REQ (128, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(CommitSerializers.request), AccordService::requestHandlerOrNoop, ACCORD_READ_RSP ),
|
||||
ACCORD_COMMIT_INVALIDATE_REQ (129, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(CommitSerializers.invalidate), AccordService::requestHandlerOrNoop ),
|
||||
ACCORD_APPLY_RSP (130, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(ApplySerializers.reply), AccordService::responseHandlerOrNoop ),
|
||||
ACCORD_APPLY_REQ (131, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(ApplySerializers.request), AccordService::requestHandlerOrNoop, ACCORD_APPLY_RSP ),
|
||||
ACCORD_APPLY_AND_WAIT_REQ (132, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(ReadDataSerializers.readData), AccordService::requestHandlerOrNoop, ACCORD_READ_RSP),
|
||||
ACCORD_APPLY_AND_WAIT_REQ (132, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(ReadDataSerializer.request), AccordService::requestHandlerOrNoop, ACCORD_READ_RSP),
|
||||
ACCORD_BEGIN_RECOVER_RSP (133, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(RecoverySerializers.reply), AccordService::responseHandlerOrNoop ),
|
||||
ACCORD_BEGIN_RECOVER_REQ (134, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(RecoverySerializers.request), AccordService::requestHandlerOrNoop, ACCORD_BEGIN_RECOVER_RSP ),
|
||||
ACCORD_BEGIN_INVALIDATE_RSP (135, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(BeginInvalidationSerializers.reply), AccordService::responseHandlerOrNoop ),
|
||||
|
|
@ -333,7 +333,7 @@ public enum Verb
|
|||
ACCORD_AWAIT_RSP (137, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(AwaitSerializers.syncReply), AccordService::responseHandlerOrNoop ),
|
||||
ACCORD_AWAIT_REQ (138, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(AwaitSerializers.request), AccordService::requestHandlerOrNoop, ACCORD_AWAIT_RSP ),
|
||||
ACCORD_AWAIT_ASYNC_RSP_REQ (139, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(AwaitSerializers.asyncReply), AccordService::requestHandlerOrNoop ),
|
||||
ACCORD_WAIT_UNTIL_APPLIED_REQ (140, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(ReadDataSerializers.waitUntilApplied), AccordService::requestHandlerOrNoop, ACCORD_READ_RSP ),
|
||||
ACCORD_WAIT_UNTIL_APPLIED_REQ (140, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(ReadDataSerializer.request), AccordService::requestHandlerOrNoop, ACCORD_READ_RSP ),
|
||||
ACCORD_RECOVER_AWAIT_RSP (141, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(AwaitSerializers.recoverReply), AccordService::responseHandlerOrNoop ),
|
||||
ACCORD_RECOVER_AWAIT_REQ (142, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(AwaitSerializers.recoverRequest), AccordService::requestHandlerOrNoop, ACCORD_RECOVER_AWAIT_RSP),
|
||||
ACCORD_INFORM_DURABLE_REQ (143, P2, writeTimeout, IMMEDIATE, () -> accordEmbedded(InformDurableSerializers.request), AccordService::requestHandlerOrNoop, ACCORD_SIMPLE_RSP ),
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ public final class TableParams
|
|||
&& extensions.equals(p.extensions)
|
||||
&& cdc == p.cdc
|
||||
&& readRepair == p.readRepair
|
||||
&& fastPath.equals(fastPath)
|
||||
&& fastPath.equals(p.fastPath)
|
||||
&& transactionalMode == p.transactionalMode
|
||||
&& transactionalMigrationFrom == p.transactionalMigrationFrom
|
||||
&& pendingDrop == p.pendingDrop
|
||||
|
|
|
|||
|
|
@ -326,18 +326,32 @@ public class AccordCacheEntry<K, V> extends IntrusiveLinkedListNode
|
|||
public <P> Loading load(BiFunction<P, Runnable, Cancellable> loadExecutor, P param, Adapter<K, V, ?> adapter, OnLoaded onLoaded)
|
||||
{
|
||||
Invariants.require(is(WAITING_TO_LOAD), "%s", this);
|
||||
Loading loading = ((WaitingToLoad)state).load(loadExecutor.apply(param, () -> {
|
||||
V result;
|
||||
try
|
||||
|
||||
WaitingToLoad cur = (WaitingToLoad)state;
|
||||
Collection<AccordTask<?>> waiters = cur.waiters;
|
||||
Loading loading = cur.load(loadExecutor.apply(param, new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
result = adapter.load(owner.commandStore, key);
|
||||
V result;
|
||||
try
|
||||
{
|
||||
result = adapter.load(owner.commandStore, key);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
onLoaded.onLoaded(AccordCacheEntry.this, null, t);
|
||||
throw t;
|
||||
}
|
||||
onLoaded.onLoaded(AccordCacheEntry.this, result, null);
|
||||
}
|
||||
catch (Throwable t)
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
onLoaded.onLoaded(this, null, t);
|
||||
throw t;
|
||||
return "Loading " + key + " on " + owner.commandStore + " for " + waiters;
|
||||
}
|
||||
onLoaded.onLoaded(this, result, null);
|
||||
}));
|
||||
setStatus(LOADING);
|
||||
state = loading;
|
||||
|
|
@ -459,17 +473,28 @@ public class AccordCacheEntry<K, V> extends IntrusiveLinkedListNode
|
|||
{
|
||||
setStatus(SAVING);
|
||||
Object identity = new Object();
|
||||
Cancellable saving = saveExecutor.apply(() -> {
|
||||
try
|
||||
Cancellable saving = saveExecutor.apply(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
save.run();
|
||||
try
|
||||
{
|
||||
save.run();
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
onSaved.onSaved(AccordCacheEntry.this, identity, t);
|
||||
throw t;
|
||||
}
|
||||
onSaved.onSaved(AccordCacheEntry.this, identity, null);
|
||||
}
|
||||
catch (Throwable t)
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
onSaved.onSaved(this, identity, t);
|
||||
throw t;
|
||||
return "Saving " + key + " for Accord cache eviction";
|
||||
}
|
||||
onSaved.onSaved(this, identity, null);
|
||||
});
|
||||
state = new Saving(saving, identity, state);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import java.util.List;
|
|||
import java.util.NavigableMap;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
|
@ -68,7 +67,6 @@ import org.apache.cassandra.service.accord.IAccordService.AccordCompactionInfo;
|
|||
import org.apache.cassandra.service.accord.api.TokenKey;
|
||||
import org.apache.cassandra.service.accord.txn.TxnRead;
|
||||
import org.apache.cassandra.utils.Clock;
|
||||
import org.apache.cassandra.utils.concurrent.UncheckedInterruptedException;
|
||||
|
||||
import static accord.api.Journal.CommandUpdate;
|
||||
import static accord.api.Journal.FieldUpdates;
|
||||
|
|
@ -145,8 +143,8 @@ public class AccordCommandStore extends CommandStore
|
|||
public final String loggingId;
|
||||
private final Journal journal;
|
||||
private final RangeSearcher rangeSearcher;
|
||||
private final AccordExecutor executor;
|
||||
private final Executor taskExecutor;
|
||||
private final AccordExecutor sharedExecutor;
|
||||
final AccordExecutor.SequentialExecutor exclusiveExecutor;
|
||||
private final ExclusiveCaches caches;
|
||||
private long lastSystemTimestampMicros = Long.MIN_VALUE;
|
||||
private final CommandsForRanges.Manager commandsForRanges;
|
||||
|
|
@ -154,7 +152,6 @@ public class AccordCommandStore extends CommandStore
|
|||
volatile SafeRedundantBefore safeRedundantBefore;
|
||||
|
||||
private AccordSafeCommandStore current;
|
||||
private Thread currentThread;
|
||||
|
||||
private final CommandStoreLoader loader;
|
||||
|
||||
|
|
@ -166,24 +163,24 @@ public class AccordCommandStore extends CommandStore
|
|||
LocalListeners.Factory listenerFactory,
|
||||
EpochUpdateHolder epochUpdateHolder,
|
||||
Journal journal,
|
||||
AccordExecutor executor)
|
||||
AccordExecutor sharedExecutor)
|
||||
{
|
||||
super(id, node, agent, dataStore, progressLogFactory, listenerFactory, epochUpdateHolder);
|
||||
this.loggingId = String.format("[%s]", id);
|
||||
this.journal = journal;
|
||||
this.rangeSearcher = RangeSearcher.extractRangeSearcher(journal);
|
||||
this.executor = executor;
|
||||
this.sharedExecutor = sharedExecutor;
|
||||
|
||||
final AccordCache.Type<TxnId, Command, AccordSafeCommand>.Instance commands;
|
||||
final AccordCache.Type<RoutingKey, CommandsForKey, AccordSafeCommandsForKey>.Instance commandsForKey;
|
||||
try (AccordExecutor.ExclusiveGlobalCaches exclusive = executor.lockCaches())
|
||||
try (AccordExecutor.ExclusiveGlobalCaches exclusive = sharedExecutor.lockCaches())
|
||||
{
|
||||
commands = exclusive.commands.newInstance(this);
|
||||
commandsForKey = exclusive.commandsForKey.newInstance(this);
|
||||
this.caches = new ExclusiveCaches(executor.lock, exclusive.global, commands, commandsForKey);
|
||||
this.caches = new ExclusiveCaches(sharedExecutor.lock, exclusive.global, commands, commandsForKey);
|
||||
}
|
||||
|
||||
this.taskExecutor = executor.executor(this);
|
||||
this.exclusiveExecutor = sharedExecutor.executor();
|
||||
this.commandsForRanges = new CommandsForRanges.Manager(this);
|
||||
this.loader = new CommandStoreLoader(this);
|
||||
|
||||
|
|
@ -212,7 +209,7 @@ public class AccordCommandStore extends CommandStore
|
|||
new AccordCommandStore(id, node, agent, dataStore, progressLogFactory, listenerFactory, rangesForEpoch, journal, executorFactory.apply(id));
|
||||
}
|
||||
|
||||
public CommandsForRanges.Manager diskCommandsForRanges()
|
||||
public CommandsForRanges.Manager commandsForRanges()
|
||||
{
|
||||
return commandsForRanges;
|
||||
}
|
||||
|
|
@ -221,7 +218,7 @@ public class AccordCommandStore extends CommandStore
|
|||
public void markShardDurable(SafeCommandStore safeStore, TxnId globalSyncId, Ranges ranges, Status.Durability durability)
|
||||
{
|
||||
if (durability.compareTo(Status.Durability.UniversalOrInvalidated) >= 0)
|
||||
store.snapshot(ranges, globalSyncId);
|
||||
dataStore.snapshot(ranges, globalSyncId);
|
||||
super.markShardDurable(safeStore, globalSyncId, ranges, durability);
|
||||
if (durability.compareTo(Status.Durability.UniversalOrInvalidated) >= 0)
|
||||
commandsForRanges.gcBefore(globalSyncId, ranges);
|
||||
|
|
@ -230,7 +227,7 @@ public class AccordCommandStore extends CommandStore
|
|||
@Override
|
||||
public boolean inStore()
|
||||
{
|
||||
return currentThread == Thread.currentThread();
|
||||
return exclusiveExecutor.inExecutor();
|
||||
}
|
||||
|
||||
void tryPreSetup(AccordTask<?> task)
|
||||
|
|
@ -246,7 +243,7 @@ public class AccordCommandStore extends CommandStore
|
|||
|
||||
public AccordExecutor executor()
|
||||
{
|
||||
return executor;
|
||||
return sharedExecutor;
|
||||
}
|
||||
|
||||
// TODO (desired): we use this for executing callbacks with mutual exclusivity,
|
||||
|
|
@ -254,7 +251,7 @@ public class AccordCommandStore extends CommandStore
|
|||
// inflate a separate queue dynamically in AccordExecutor
|
||||
public Executor taskExecutor()
|
||||
{
|
||||
return taskExecutor;
|
||||
return exclusiveExecutor;
|
||||
}
|
||||
|
||||
public ExclusiveCaches lockCaches()
|
||||
|
|
@ -273,7 +270,7 @@ public class AccordCommandStore extends CommandStore
|
|||
|
||||
public Caches cachesExclusive()
|
||||
{
|
||||
Invariants.require(executor.isOwningThread());
|
||||
Invariants.require(sharedExecutor.isOwningThread());
|
||||
return caches;
|
||||
}
|
||||
|
||||
|
|
@ -346,43 +343,12 @@ public class AccordCommandStore extends CommandStore
|
|||
return AsyncChains.ofCallable(taskExecutor(), task);
|
||||
}
|
||||
|
||||
public DataStore dataStore()
|
||||
{
|
||||
return store;
|
||||
}
|
||||
|
||||
NodeCommandStoreService node()
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
ProgressLog progressLog()
|
||||
{
|
||||
return progressLog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncChain<Void> build(PreLoadContext preLoadContext, Consumer<? super SafeCommandStore> consumer)
|
||||
{
|
||||
return AccordTask.create(this, preLoadContext, consumer).chain();
|
||||
}
|
||||
|
||||
public void executeBlocking(Runnable runnable)
|
||||
{
|
||||
try
|
||||
{
|
||||
executor.submit(runnable).get();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
throw new UncheckedInterruptedException(e);
|
||||
}
|
||||
catch (ExecutionException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public AccordSafeCommandStore begin(AccordTask<?> operation,
|
||||
@Nullable CommandsForRanges commandsForRanges)
|
||||
{
|
||||
|
|
@ -391,18 +357,21 @@ public class AccordCommandStore extends CommandStore
|
|||
return current;
|
||||
}
|
||||
|
||||
void setOwner(Thread thread, Thread self)
|
||||
{
|
||||
Invariants.require(thread == null ? currentThread == self : currentThread == null);
|
||||
currentThread = thread;
|
||||
if (thread != null) CommandStore.register(this);
|
||||
}
|
||||
|
||||
public boolean hasSafeStore()
|
||||
{
|
||||
return current != null;
|
||||
}
|
||||
|
||||
DataStore dataStore()
|
||||
{
|
||||
return dataStore;
|
||||
}
|
||||
|
||||
ProgressLog progressLog()
|
||||
{
|
||||
return progressLog;
|
||||
}
|
||||
|
||||
public void complete(AccordSafeCommandStore store)
|
||||
{
|
||||
require(current == store);
|
||||
|
|
@ -412,7 +381,6 @@ public class AccordCommandStore extends CommandStore
|
|||
|
||||
public void abort(AccordSafeCommandStore store)
|
||||
{
|
||||
checkInStore();
|
||||
Invariants.require(store == current);
|
||||
current = null;
|
||||
}
|
||||
|
|
@ -454,7 +422,7 @@ public class AccordCommandStore extends CommandStore
|
|||
if (addRanges.intersects(coordinateRanges)) continue;
|
||||
addRanges = redundantBefore.removeGcBefore(txnId, txnId, addRanges);
|
||||
if (addRanges.isEmpty()) continue;
|
||||
diskCommandsForRanges().mergeTransitive(txnId, addRanges, Ranges::with);
|
||||
commandsForRanges().mergeTransitive(txnId, addRanges, Ranges::with);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import accord.api.ProgressLog;
|
|||
import accord.local.CommandStores;
|
||||
import accord.local.Node;
|
||||
import accord.local.NodeCommandStoreService;
|
||||
import accord.local.SequentialAsyncExecutor;
|
||||
import accord.local.ShardDistributor;
|
||||
import accord.primitives.Range;
|
||||
import accord.topology.Topology;
|
||||
|
|
@ -61,6 +62,7 @@ public class AccordCommandStores extends CommandStores implements CacheSize
|
|||
|
||||
private final CacheSizeMetrics cacheSizeMetrics;
|
||||
private final AccordExecutor[] executors;
|
||||
private final int mask;
|
||||
|
||||
private long cacheSize, workingSetSize;
|
||||
private int maxQueuedLoads, maxQueuedRangeLoads;
|
||||
|
|
@ -74,6 +76,7 @@ public class AccordCommandStores extends CommandStores implements CacheSize
|
|||
AccordCommandStore.factory(id -> executors[id % executors.length]));
|
||||
this.executors = executors;
|
||||
this.cacheSizeMetrics = new CacheSizeMetrics(ACCORD_STATE_CACHE, this);
|
||||
this.mask = Integer.highestOneBit(executors.length) - 1;
|
||||
cacheSize = DatabaseDescriptor.getAccordCacheSizeInMiB() << 20;
|
||||
workingSetSize = DatabaseDescriptor.getAccordWorkingSetSizeInMiB() << 20;
|
||||
maxQueuedLoads = DatabaseDescriptor.getAccordMaxQueuedLoadCount();
|
||||
|
|
@ -134,6 +137,13 @@ public class AccordCommandStores extends CommandStores implements CacheSize
|
|||
return contains(previous, ((TokenKey) range.start()).table());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SequentialAsyncExecutor someSequentialExecutor()
|
||||
{
|
||||
int idx = ((int) Thread.currentThread().getId()) & mask;
|
||||
return executors[idx].newSequentialExecutor();
|
||||
}
|
||||
|
||||
private static boolean contains(Topology previous, TableId searchTable)
|
||||
{
|
||||
for (Range range : previous.ranges())
|
||||
|
|
|
|||
|
|
@ -103,13 +103,14 @@ public class AccordDataStore implements DataStore
|
|||
List<Future<?>> futures = new ArrayList<>();
|
||||
for (Map.Entry<TableId, SnapshotBounds> e : tables.entrySet())
|
||||
{
|
||||
// TODO (required): is it safe to ignore null tableMetadata (or ColumnFamilyStore below)?
|
||||
TableMetadata tableMetadata = metadata.schema.getTableMetadata(e.getKey());
|
||||
SnapshotBounds bounds = e.getValue();
|
||||
ColumnFamilyStore cfs = Keyspace.openAndGetStoreIfExists(tableMetadata);
|
||||
if (tableMetadata == null) continue;
|
||||
|
||||
// TODO (required): is it safe to ignore null cfs?
|
||||
ColumnFamilyStore cfs = Keyspace.openAndGetStoreIfExists(tableMetadata);
|
||||
if (cfs == null) continue;
|
||||
|
||||
SnapshotBounds bounds = e.getValue();
|
||||
View view = cfs.getTracker().getView();
|
||||
for (Memtable memtable : view.getAllMemtables())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -19,18 +19,26 @@
|
|||
package org.apache.cassandra.service.accord;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.base.Functions;
|
||||
|
||||
import accord.api.Agent;
|
||||
import accord.api.AsyncExecutor;
|
||||
import accord.api.RoutingKey;
|
||||
import accord.local.AgentExecutor;
|
||||
import accord.local.Command;
|
||||
import accord.local.SequentialAsyncExecutor;
|
||||
import accord.local.cfk.CommandsForKey;
|
||||
import accord.primitives.TxnId;
|
||||
import accord.utils.ArrayBuffers.BufferList;
|
||||
|
|
@ -45,12 +53,14 @@ import accord.utils.UnhandledEnum;
|
|||
import accord.utils.async.AsyncChain;
|
||||
import accord.utils.async.AsyncChains;
|
||||
import accord.utils.async.Cancellable;
|
||||
import org.agrona.collections.Object2ObjectHashMap;
|
||||
import org.apache.cassandra.cache.CacheSize;
|
||||
import org.apache.cassandra.concurrent.DebuggableTask;
|
||||
import org.apache.cassandra.concurrent.DebuggableTask.DebuggableTaskRunner;
|
||||
import org.apache.cassandra.concurrent.ExecutorPlus;
|
||||
import org.apache.cassandra.concurrent.ScheduledExecutors;
|
||||
import org.apache.cassandra.concurrent.Shutdownable;
|
||||
import org.apache.cassandra.metrics.AccordCacheMetrics;
|
||||
import org.apache.cassandra.utils.MonotonicClock;
|
||||
import org.apache.cassandra.utils.concurrent.AsyncPromise;
|
||||
import org.apache.cassandra.utils.concurrent.Future;
|
||||
|
||||
|
|
@ -62,9 +72,11 @@ import static org.apache.cassandra.service.accord.AccordTask.State.LOADING;
|
|||
import static org.apache.cassandra.service.accord.AccordTask.State.SCANNING_RANGES;
|
||||
import static org.apache.cassandra.service.accord.AccordTask.State.WAITING_TO_LOAD;
|
||||
import static org.apache.cassandra.service.accord.AccordTask.State.WAITING_TO_RUN;
|
||||
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
|
||||
|
||||
public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLoaded, AccordCacheEntry.OnSaved, Shutdownable, AgentExecutor
|
||||
/**
|
||||
* NOTE: We assume that NO BLOCKING TASKS are submitted to this executor AND WAITED ON by another task executing on this executor.
|
||||
*/
|
||||
public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLoaded, AccordCacheEntry.OnSaved, Shutdownable, AsyncExecutor
|
||||
{
|
||||
public interface AccordExecutorFactory
|
||||
{
|
||||
|
|
@ -122,7 +134,6 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
|
||||
private final TaskQueue<AccordTask<?>> waitingToLoad = new TaskQueue<>(WAITING_TO_LOAD);
|
||||
private final TaskQueue<Task> waitingToRun = new TaskQueue<>(WAITING_TO_RUN);
|
||||
private final Object2ObjectHashMap<AccordCommandStore, CommandStoreQueue> commandStoreQueues = new Object2ObjectHashMap<>();
|
||||
|
||||
private final AccordCacheEntry.OnLoaded onRangeLoaded = this::onRangeLoaded;
|
||||
private final ExclusiveGlobalCaches caches;
|
||||
|
|
@ -208,6 +219,11 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
maybeUnpauseLoading();
|
||||
}
|
||||
|
||||
public Stream<? extends DebuggableTaskRunner> active()
|
||||
{
|
||||
return Stream.of();
|
||||
}
|
||||
|
||||
void maybeUnpauseLoading()
|
||||
{
|
||||
if (!hasPausedLoading)
|
||||
|
|
@ -318,16 +334,29 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Agent agent()
|
||||
{
|
||||
return agent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> AsyncChain<T> build(Callable<T> task)
|
||||
{
|
||||
return AsyncChains.ofCallable(this, task);
|
||||
return new AsyncChains.Head<>()
|
||||
{
|
||||
@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)
|
||||
{
|
||||
return new AsyncChains.Head<>()
|
||||
{
|
||||
@Override
|
||||
protected Cancellable start(BiConsumer<? super T, Throwable> callback)
|
||||
{
|
||||
return submit(new DebuggableChain<>(task, callback, null, describe));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void parkRangeLoad(AccordTask<?> task)
|
||||
|
|
@ -343,10 +372,8 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
{
|
||||
try
|
||||
{
|
||||
if (object instanceof AccordTask<?>)
|
||||
loadExclusive((AccordTask<?>) object);
|
||||
else
|
||||
((SubmitAsync) object).acceptExclusive(this);
|
||||
if (object instanceof SubmittableTask) ((SubmittableTask) object).submitExclusive(this);
|
||||
else ((SubmitAsync) object).submitExclusive(this);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
|
|
@ -371,31 +398,26 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
task.addToQueue(loading);
|
||||
break;
|
||||
case WAITING_TO_RUN:
|
||||
task.runQueuedAt = nanoTime();
|
||||
commandStoreQueues.computeIfAbsent(task.commandStore, CommandStoreQueue::new)
|
||||
.appendOrSetNext(task);
|
||||
waitingToRun(task);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void waitingToRun(Task task)
|
||||
|
||||
private void waitingToRun(AccordTask task)
|
||||
{
|
||||
if (task.commandStore == null)
|
||||
{
|
||||
waitingToRun.append(task);
|
||||
}
|
||||
else
|
||||
{
|
||||
commandStoreQueues.computeIfAbsent(task.commandStore, CommandStoreQueue::new)
|
||||
.appendOrSetNext(task);
|
||||
}
|
||||
task.addToQueue(task.commandStore.exclusiveExecutor);
|
||||
}
|
||||
|
||||
private void waitingToRun(SubmittableTask task, @Nullable SequentialExecutor queue)
|
||||
{
|
||||
task.addToQueue(queue == null ? waitingToRun : queue);
|
||||
}
|
||||
|
||||
private Cancellable submitIOExclusive(Task parent, Runnable run)
|
||||
{
|
||||
Invariants.require(isOwningThread());
|
||||
++tasks;
|
||||
PlainRunnable task = new PlainRunnable(null, run, null);
|
||||
IORunnable task = new IORunnable(run);
|
||||
// TODO (expected): adopt queue position of the submitting task
|
||||
if (parent == null) assignNewQueuePosition(task);
|
||||
else assignQueueSubPosition(parent, task);
|
||||
|
|
@ -413,14 +435,19 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
task.queuePosition = parent.queuePosition | (++nextPosition & 0x7fffffff);
|
||||
}
|
||||
|
||||
public Executor executor(AccordCommandStore commandStore)
|
||||
public SequentialExecutor executor()
|
||||
{
|
||||
return task -> AccordExecutor.this.submit(task, commandStore);
|
||||
return new SequentialExecutor();
|
||||
}
|
||||
|
||||
public SequentialAsyncExecutor newSequentialExecutor()
|
||||
{
|
||||
return new SequentialExecutor();
|
||||
}
|
||||
|
||||
public <R> void submit(AccordTask<R> operation)
|
||||
{
|
||||
submit(AccordExecutor::loadExclusive, Function.identity(), operation);
|
||||
submit(AccordExecutor::submitExclusive, Function.identity(), operation);
|
||||
}
|
||||
|
||||
public <R> void cancel(AccordTask<R> task)
|
||||
|
|
@ -474,22 +501,14 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
|
||||
abstract <P1s, P1a, P2, P3, P4> void submit(QuintConsumer<AccordExecutor, P1s, P2, P3, P4> sync, QuadFunction<P1a, P2, P3, P4, Object> async, P1s p1s, P1a p1a, P2 p2, P3 p3, P4 p4);
|
||||
|
||||
private void submitExclusive(AsyncPromise<Void> result, Runnable run, AccordCommandStore commandStore)
|
||||
{
|
||||
++tasks;
|
||||
PlainRunnable task = new PlainRunnable(result, run, commandStore);
|
||||
task.queuePosition = ++nextPosition;
|
||||
waitingToRun(task);
|
||||
}
|
||||
|
||||
private void submitExclusive(AsyncPromise<Void> result, PlainRunnable task)
|
||||
private void submitExclusive(Plain task)
|
||||
{
|
||||
++tasks;
|
||||
task.queuePosition = ++nextPosition;
|
||||
waitingToRun(task);
|
||||
waitingToRun(task, task.executor());
|
||||
}
|
||||
|
||||
private void loadExclusive(AccordTask<?> task)
|
||||
void submitExclusive(AccordTask<?> task)
|
||||
{
|
||||
++tasks;
|
||||
assignNewQueuePosition(task);
|
||||
|
|
@ -559,7 +578,7 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
finally
|
||||
{
|
||||
task.unqueueIfQueued();
|
||||
task.cleanupExclusive();
|
||||
task.cleanupExclusive(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -609,17 +628,22 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
}
|
||||
|
||||
// TODO (expected): offer queue jumping/priorities
|
||||
public Future<?> submit(Runnable run, AccordCommandStore commandStore)
|
||||
private Future<?> submit(Runnable run, @Nullable SequentialExecutor executor)
|
||||
{
|
||||
PlainRunnable task = new PlainRunnable(new AsyncPromise<>(), run, commandStore);
|
||||
AsyncPromise<Void> result = new AsyncPromise<>();
|
||||
submit(AccordExecutor::submitExclusive, SubmitPlainRunnable::new, result, run, commandStore);
|
||||
return result;
|
||||
PlainRunnable task = new PlainRunnable(new AsyncPromise<>(), run, executor);
|
||||
submit(task);
|
||||
return task.result;
|
||||
}
|
||||
|
||||
private Cancellable submit(Plain task)
|
||||
{
|
||||
submit(AccordExecutor::submitExclusive, Functions.identity(), task);
|
||||
return task;
|
||||
}
|
||||
|
||||
public void execute(Runnable command)
|
||||
{
|
||||
submit(command);
|
||||
submit(new PlainRunnable(null, command, null));
|
||||
}
|
||||
|
||||
public void executeDirectlyWithLock(Runnable command)
|
||||
|
|
@ -637,7 +661,7 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
|
||||
public void execute(Runnable command, AccordCommandStore commandStore)
|
||||
{
|
||||
submit(command, commandStore);
|
||||
submit(new PlainRunnable(null, command, commandStore.exclusiveExecutor));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -682,20 +706,43 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
return cache.weightedSize();
|
||||
}
|
||||
|
||||
protected static abstract class TaskRunner implements DebuggableTaskRunner
|
||||
{
|
||||
private volatile Task running;
|
||||
private static final AtomicReferenceFieldUpdater<TaskRunner, Task> runningUpdater = AtomicReferenceFieldUpdater.newUpdater(TaskRunner.class, Task.class, "running");
|
||||
|
||||
@Override
|
||||
public DebuggableTask running()
|
||||
{
|
||||
Task running = this.running;
|
||||
return running == null ? null : running.debuggable();
|
||||
}
|
||||
|
||||
void setDebuggable(Task debuggable)
|
||||
{
|
||||
runningUpdater.lazySet(this, debuggable);
|
||||
}
|
||||
|
||||
void clearDebuggable()
|
||||
{
|
||||
runningUpdater.lazySet(this, null);
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class Task extends IntrusivePriorityHeap.Node
|
||||
{
|
||||
final AccordCommandStore commandStore;
|
||||
long queuePosition;
|
||||
|
||||
protected Task(AccordCommandStore commandStore)
|
||||
protected Task()
|
||||
{
|
||||
this.commandStore = commandStore;
|
||||
}
|
||||
|
||||
public DebuggableTask debuggable() { return null; }
|
||||
|
||||
/**
|
||||
* Prepare to run while holding the state cache lock
|
||||
*/
|
||||
abstract protected void preRunExclusive();
|
||||
abstract protected void preRunExclusive(@Nullable TaskRunner runner);
|
||||
|
||||
/**
|
||||
* Run the command; the state cache lock may or may not be held depending on the executor implementation
|
||||
|
|
@ -709,66 +756,48 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
/**
|
||||
* Cleanup the command while holding the state cache lock
|
||||
*/
|
||||
abstract protected void cleanupExclusive();
|
||||
abstract protected void cleanupExclusive(@Nullable TaskRunner runner);
|
||||
|
||||
abstract protected void addToQueue(TaskQueue queue);
|
||||
}
|
||||
|
||||
static class CommandStoreQueueTask extends Task
|
||||
static abstract class SubmittableTask extends Task
|
||||
{
|
||||
private final CommandStoreQueue queue;
|
||||
private Task task;
|
||||
abstract void submitExclusive(AccordExecutor owner);
|
||||
}
|
||||
|
||||
CommandStoreQueueTask(CommandStoreQueue queue, AccordCommandStore commandStore)
|
||||
static class SequentialQueueTask extends Task
|
||||
{
|
||||
private final SequentialExecutor queue;
|
||||
|
||||
SequentialQueueTask(SequentialExecutor queue)
|
||||
{
|
||||
super(commandStore);
|
||||
super();
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
public boolean isSet()
|
||||
{
|
||||
return this.task != null;
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
queuePosition = -1;
|
||||
this.task = null;
|
||||
}
|
||||
|
||||
public void setNext(Task task)
|
||||
{
|
||||
queuePosition = task.queuePosition;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preRunExclusive()
|
||||
protected void preRunExclusive(@Nullable TaskRunner runner)
|
||||
{
|
||||
Invariants.require(task != null);
|
||||
Thread self = Thread.currentThread();
|
||||
commandStore.setOwner(self, self);
|
||||
task.preRunExclusive();
|
||||
queue.preRunTask(runner);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run()
|
||||
{
|
||||
task.run();
|
||||
queue.runTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fail(Throwable t)
|
||||
{
|
||||
task.fail(t);
|
||||
queue.failTask(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanupExclusive()
|
||||
protected void cleanupExclusive(@Nullable TaskRunner runner)
|
||||
{
|
||||
task.cleanupExclusive();
|
||||
commandStore.setOwner(null, Thread.currentThread());
|
||||
queue.updateNext();
|
||||
queue.cleanupTask(runner);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -778,75 +807,157 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
}
|
||||
}
|
||||
|
||||
class CommandStoreQueue extends TaskQueue<Task>
|
||||
private static final AtomicReferenceFieldUpdater<SequentialExecutor, Thread> ownerUpdater = AtomicReferenceFieldUpdater.newUpdater(SequentialExecutor.class, Thread.class, "owner");
|
||||
public class SequentialExecutor extends TaskQueue<Task> implements SequentialAsyncExecutor
|
||||
{
|
||||
final CommandStoreQueueTask next;
|
||||
final SequentialQueueTask selfTask;
|
||||
private Task task;
|
||||
private volatile Thread owner, waiting;
|
||||
|
||||
CommandStoreQueue(AccordCommandStore commandStore)
|
||||
SequentialExecutor()
|
||||
{
|
||||
super(WAITING_TO_RUN);
|
||||
this.next = new CommandStoreQueueTask(this, commandStore);
|
||||
this.selfTask = new SequentialQueueTask(this);
|
||||
}
|
||||
|
||||
void updateNext()
|
||||
void preRunTask(@Nullable TaskRunner runner)
|
||||
{
|
||||
updateNext(super.poll());
|
||||
Invariants.require(task != null);
|
||||
task.preRunExclusive(runner);
|
||||
}
|
||||
|
||||
void updateNext(Task task)
|
||||
void runTask()
|
||||
{
|
||||
next.reset();
|
||||
if (task != null)
|
||||
task.addToQueue(this);
|
||||
}
|
||||
|
||||
public void appendOrSetNext(Task task)
|
||||
{
|
||||
if (!next.isSet())
|
||||
task.addToQueue(this);
|
||||
else
|
||||
super.append(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(Task task)
|
||||
{
|
||||
Invariants.require(!next.isSet());
|
||||
// TODO (expected): if the new task is higher priority, replace next
|
||||
next.setNext(task);
|
||||
waitingToRun.append(next);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Task remove)
|
||||
{
|
||||
if (next.isSet() && next.task == remove)
|
||||
Thread self = Thread.currentThread();
|
||||
if (!ownerUpdater.compareAndSet(this, null, self))
|
||||
{
|
||||
next.reset();
|
||||
waitingToRun.remove(next);
|
||||
return;
|
||||
waiting = self;
|
||||
while (owner != null)
|
||||
LockSupport.park();
|
||||
waiting = null;
|
||||
}
|
||||
task.run();
|
||||
}
|
||||
|
||||
super.remove(remove);
|
||||
void failTask(Throwable t)
|
||||
{
|
||||
task.fail(t);
|
||||
}
|
||||
|
||||
void cleanupTask(@Nullable TaskRunner runner)
|
||||
{
|
||||
task.cleanupExclusive(runner);
|
||||
owner = null;
|
||||
task = super.poll();
|
||||
if (task != null)
|
||||
{
|
||||
selfTask.queuePosition = task.queuePosition;
|
||||
waitingToRun.append(selfTask);
|
||||
}
|
||||
}
|
||||
|
||||
// invoked by removeAndUpdateNext; expect to already be next
|
||||
@Override
|
||||
protected void append(Task newTask)
|
||||
{
|
||||
if (task != null)
|
||||
{
|
||||
super.append(newTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
task = newTask;
|
||||
selfTask.queuePosition = newTask.queuePosition;
|
||||
waitingToRun.append(selfTask);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task poll()
|
||||
protected void remove(Task remove)
|
||||
{
|
||||
if (remove != task)
|
||||
{
|
||||
super.remove(remove);
|
||||
}
|
||||
else
|
||||
{
|
||||
Invariants.require(waitingToRun.contains(selfTask));
|
||||
task = super.poll();
|
||||
if (task == null) waitingToRun.remove(selfTask);
|
||||
else
|
||||
{
|
||||
selfTask.queuePosition = task.queuePosition;
|
||||
waitingToRun.update(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean inExecutor()
|
||||
{
|
||||
return owner == Thread.currentThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Task poll()
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task peek()
|
||||
protected Task peek()
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Task contains)
|
||||
protected boolean contains(Task contains)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> AsyncChain<T> build(Callable<T> task)
|
||||
{
|
||||
return new AsyncChains.Head<>()
|
||||
{
|
||||
@Override
|
||||
protected Cancellable start(BiConsumer<? super T, Throwable> callback)
|
||||
{
|
||||
return submit(new PlainChain<>(task, callback, SequentialExecutor.this));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable run)
|
||||
{
|
||||
submit(new PlainRunnable(null, run, this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeExecuteImmediately(Runnable run)
|
||||
{
|
||||
Thread self = Thread.currentThread();
|
||||
Thread owner = this.owner;
|
||||
if (owner == self || (owner == null && ownerUpdater.compareAndSet(this, null, self)))
|
||||
{
|
||||
try { run.run(); }
|
||||
catch (Throwable t) { agent.onUncaughtException(t); }
|
||||
finally
|
||||
{
|
||||
if (owner == null)
|
||||
{
|
||||
this.owner = null;
|
||||
if (waiting != null)
|
||||
LockSupport.unpark(waiting);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
execute(run);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class TaskQueue<T extends Task> extends IntrusivePriorityHeap<T>
|
||||
|
|
@ -863,31 +974,35 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
{
|
||||
return Long.compare(o1.queuePosition, o2.queuePosition);
|
||||
}
|
||||
public void append(T task)
|
||||
|
||||
protected void append(T task)
|
||||
{
|
||||
super.append(task);
|
||||
}
|
||||
|
||||
public T poll()
|
||||
protected void update(T task)
|
||||
{
|
||||
super.update(task);
|
||||
}
|
||||
|
||||
protected T poll()
|
||||
{
|
||||
ensureHeapified();
|
||||
return pollNode();
|
||||
}
|
||||
|
||||
public T peek()
|
||||
protected T peek()
|
||||
{
|
||||
ensureHeapified();
|
||||
return peekNode();
|
||||
}
|
||||
|
||||
public void remove(T remove)
|
||||
protected void remove(T remove)
|
||||
{
|
||||
Invariants.require(super.contains(remove));
|
||||
super.remove(remove);
|
||||
Invariants.require(!super.contains(remove));
|
||||
}
|
||||
|
||||
public boolean contains(T contains)
|
||||
protected boolean contains(T contains)
|
||||
{
|
||||
return super.contains(contains);
|
||||
}
|
||||
|
|
@ -895,27 +1010,7 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
|
||||
private abstract static class SubmitAsync
|
||||
{
|
||||
abstract void acceptExclusive(AccordExecutor executor);
|
||||
}
|
||||
|
||||
private static class SubmitPlainRunnable extends SubmitAsync
|
||||
{
|
||||
final AsyncPromise<Void> result;
|
||||
final Runnable run;
|
||||
final AccordCommandStore commandStore;
|
||||
|
||||
private SubmitPlainRunnable(AsyncPromise<Void> result, Runnable run, AccordCommandStore commandStore)
|
||||
{
|
||||
this.result = result;
|
||||
this.run = run;
|
||||
this.commandStore = commandStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
void acceptExclusive(AccordExecutor executor)
|
||||
{
|
||||
executor.submitExclusive(result, run, commandStore);
|
||||
}
|
||||
abstract void submitExclusive(AccordExecutor executor);
|
||||
}
|
||||
|
||||
private static class OnLoaded<K, V> extends SubmitAsync
|
||||
|
|
@ -958,7 +1053,7 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
}
|
||||
|
||||
@Override
|
||||
void acceptExclusive(AccordExecutor executor)
|
||||
void submitExclusive(AccordExecutor executor)
|
||||
{
|
||||
executor.onLoadedExclusive(loaded, success(), fail(), isForRange());
|
||||
}
|
||||
|
|
@ -976,7 +1071,7 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
}
|
||||
|
||||
@Override
|
||||
void acceptExclusive(AccordExecutor executor)
|
||||
void submitExclusive(AccordExecutor executor)
|
||||
{
|
||||
executor.onScannedRangesExclusive(scanned, fail);
|
||||
}
|
||||
|
|
@ -996,7 +1091,7 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
}
|
||||
|
||||
@Override
|
||||
void acceptExclusive(AccordExecutor executor)
|
||||
void submitExclusive(AccordExecutor executor)
|
||||
{
|
||||
executor.onSavedExclusive(state, identity, fail);
|
||||
}
|
||||
|
|
@ -1012,7 +1107,7 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
}
|
||||
|
||||
@Override
|
||||
void acceptExclusive(AccordExecutor executor)
|
||||
void submitExclusive(AccordExecutor executor)
|
||||
{
|
||||
executor.cancelExclusive(cancel);
|
||||
}
|
||||
|
|
@ -1053,20 +1148,56 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
return r -> f.apply(null, r);
|
||||
}
|
||||
|
||||
class PlainRunnable extends Task implements Cancellable
|
||||
abstract class Plain extends SubmittableTask implements Cancellable
|
||||
{
|
||||
final AsyncPromise<Void> result;
|
||||
final Runnable run;
|
||||
abstract SequentialExecutor executor();
|
||||
|
||||
PlainRunnable(AsyncPromise<Void> result, Runnable run, AccordCommandStore commandStore)
|
||||
@Override
|
||||
protected void preRunExclusive(@Nullable TaskRunner runner) {}
|
||||
|
||||
@Override
|
||||
protected void cleanupExclusive(@Nullable TaskRunner runner) {}
|
||||
|
||||
@Override
|
||||
protected final void addToQueue(TaskQueue queue)
|
||||
{
|
||||
super(commandStore);
|
||||
this.result = result;
|
||||
this.run = run;
|
||||
Invariants.require(queue.kind == WAITING_TO_RUN);
|
||||
queue.append(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preRunExclusive() {}
|
||||
public final void cancel()
|
||||
{
|
||||
executeDirectlyWithLock(() -> {
|
||||
SequentialExecutor executor = executor();
|
||||
TaskQueue queue = executor == null ? waitingToRun : executor;
|
||||
if (queue.contains(this))
|
||||
{
|
||||
queue.remove(this);
|
||||
fail(new CancellationException());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
final void submitExclusive(AccordExecutor owner)
|
||||
{
|
||||
owner.submitExclusive(this);
|
||||
}
|
||||
}
|
||||
|
||||
class PlainRunnable extends Plain implements Cancellable
|
||||
{
|
||||
final @Nullable AsyncPromise<Void> result;
|
||||
final Runnable run;
|
||||
final @Nullable SequentialExecutor executor;
|
||||
|
||||
PlainRunnable(AsyncPromise<Void> result, Runnable run, @Nullable SequentialExecutor executor)
|
||||
{
|
||||
this.result = result;
|
||||
this.run = run;
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run()
|
||||
|
|
@ -1085,27 +1216,185 @@ public abstract class AccordExecutor implements CacheSize, AccordCacheEntry.OnLo
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void cleanupExclusive() {}
|
||||
|
||||
@Override
|
||||
protected void addToQueue(TaskQueue queue)
|
||||
SequentialExecutor executor()
|
||||
{
|
||||
Invariants.require(queue.kind == WAITING_TO_RUN);
|
||||
queue.append(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel()
|
||||
{
|
||||
executeDirectlyWithLock(() -> {
|
||||
if (isInHeap())
|
||||
{
|
||||
waitingToRun.remove(this);
|
||||
if (result != null)
|
||||
result.cancel(false);
|
||||
}
|
||||
});
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
|
||||
class IORunnable extends Plain implements Cancellable, DebuggableTask
|
||||
{
|
||||
// expected to implement toString
|
||||
final Runnable run;
|
||||
final long createdAtNanos = MonotonicClock.Global.approxTime.now();
|
||||
long startedAtNanos;
|
||||
|
||||
IORunnable(Runnable run)
|
||||
{
|
||||
this.run = run;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run()
|
||||
{
|
||||
run.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preRunExclusive(@Nullable TaskRunner runner)
|
||||
{
|
||||
if (runner != null)
|
||||
{
|
||||
startedAtNanos = MonotonicClock.Global.approxTime.now();
|
||||
runner.setDebuggable(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanupExclusive(@Nullable TaskRunner runner)
|
||||
{
|
||||
if (runner != null)
|
||||
runner.clearDebuggable();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fail(Throwable t)
|
||||
{
|
||||
agent.onUncaughtException(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
SequentialExecutor executor()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long creationTimeNanos()
|
||||
{
|
||||
return createdAtNanos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long startTimeNanos()
|
||||
{
|
||||
return startedAtNanos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description()
|
||||
{
|
||||
return run.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class PlainChain<T> extends Plain
|
||||
{
|
||||
final Callable<T> call;
|
||||
final BiConsumer<? super T, Throwable> callback;
|
||||
final @Nullable SequentialExecutor executor;
|
||||
|
||||
PlainChain(Callable<T> call, BiConsumer<? super T, Throwable> callback, @Nullable SequentialExecutor executor)
|
||||
{
|
||||
this.call = call;
|
||||
this.callback = callback;
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
SequentialExecutor executor()
|
||||
{
|
||||
return executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run()
|
||||
{
|
||||
T success;
|
||||
try
|
||||
{
|
||||
success = call.call();
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
fail(t);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
callback.accept(success, null);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
agent.onUncaughtException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fail(Throwable fail)
|
||||
{
|
||||
try
|
||||
{
|
||||
callback.accept(null, fail);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
fail.addSuppressed(t);
|
||||
agent.onUncaughtException(fail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DebuggableChain<T> extends PlainChain<T> implements DebuggableTask
|
||||
{
|
||||
final long createdAtNanos;
|
||||
long startedAtNanos;
|
||||
final Object describe;
|
||||
|
||||
DebuggableChain(Callable<T> call, BiConsumer<? super T, Throwable> callback, @Nullable SequentialExecutor executor, Object describe)
|
||||
{
|
||||
super(call, callback, executor);
|
||||
this.createdAtNanos = MonotonicClock.Global.approxTime.now();
|
||||
this.describe = Invariants.nonNull(describe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long creationTimeNanos()
|
||||
{
|
||||
return createdAtNanos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long startTimeNanos()
|
||||
{
|
||||
return startedAtNanos;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preRunExclusive(@Nullable TaskRunner runner)
|
||||
{
|
||||
startedAtNanos = MonotonicClock.Global.approxTime.now();
|
||||
if (runner != null)
|
||||
runner.setDebuggable(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanupExclusive(@Nullable TaskRunner runner)
|
||||
{
|
||||
if (runner != null)
|
||||
runner.clearDebuggable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description()
|
||||
{
|
||||
return describe.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggableTask debuggable()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,14 @@
|
|||
package org.apache.cassandra.service.accord;
|
||||
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import accord.api.Agent;
|
||||
import accord.utils.QuadFunction;
|
||||
import accord.utils.QuintConsumer;
|
||||
import org.apache.cassandra.concurrent.DebuggableTask.DebuggableTaskRunner;
|
||||
import org.apache.cassandra.metrics.AccordCacheMetrics;
|
||||
import org.apache.cassandra.service.accord.AccordExecutorLoops.LoopTask;
|
||||
import org.apache.cassandra.utils.concurrent.ConcurrentLinkedStack;
|
||||
|
||||
import static org.apache.cassandra.service.accord.AccordExecutor.Mode.RUN_WITH_LOCK;
|
||||
|
|
@ -42,12 +45,14 @@ abstract class AccordExecutorAbstractLockLoop extends AccordExecutor
|
|||
abstract void notifyWork();
|
||||
abstract void notifyWorkExclusive();
|
||||
abstract void awaitExclusive() throws InterruptedException;
|
||||
abstract AccordExecutorLoops loops();
|
||||
abstract boolean isInLoop();
|
||||
abstract <P1s, P1a, P2, P3, P4> void submitExternal(QuintConsumer<AccordExecutor, P1s, P2, P3, P4> sync, QuadFunction<P1a, P2, P3, P4, Object> async, P1s p1s, P1a p1a, P2 p2, P3 p3, P4 p4);
|
||||
|
||||
<P1s, P1a, P2, P3, P4> void submit(QuintConsumer<AccordExecutor, P1s, P2, P3, P4> sync, QuadFunction<P1a, P2, P3, P4, Object> async, P1s p1s, P1a p1a, P2 p2, P3 p3, P4 p4)
|
||||
{
|
||||
// if we're a loop thread, we will poll the waitingToRun queue when we come around
|
||||
// NOTE: this assumes no synchronous blocking tasks are submitted to this executor
|
||||
if (isInLoop()) submitted.push(async.apply(p1a, p2, p3, p4));
|
||||
else submitExternal(sync, async, p1s, p1a, p2, p3, p4);
|
||||
}
|
||||
|
|
@ -129,150 +134,171 @@ abstract class AccordExecutorAbstractLockLoop extends AccordExecutor
|
|||
++running;
|
||||
}
|
||||
|
||||
Runnable task(Mode mode)
|
||||
LoopTask task(String name, Mode mode)
|
||||
{
|
||||
return mode == RUN_WITH_LOCK ? this::runWithLock : this::runWithoutLock;
|
||||
return mode == RUN_WITH_LOCK ? runWithLock(name) : runWithoutLock(name);
|
||||
}
|
||||
|
||||
protected void runWithLock()
|
||||
protected LoopTask runWithLock(String name)
|
||||
{
|
||||
while (true)
|
||||
return new LoopTask(name)
|
||||
{
|
||||
lock.lock();
|
||||
try
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
resumeExclusive();
|
||||
enterLockExclusive();
|
||||
Task task;
|
||||
while (true)
|
||||
{
|
||||
Task task = pollWaitingToRunExclusive();
|
||||
|
||||
if (task != null)
|
||||
{
|
||||
--tasks;
|
||||
try
|
||||
{
|
||||
task.preRunExclusive();
|
||||
task.run();
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
task.fail(t);
|
||||
}
|
||||
finally
|
||||
{
|
||||
task.cleanupExclusive();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shutdown)
|
||||
{
|
||||
pauseExclusive();
|
||||
exitLockExclusive();
|
||||
notifyWorkExclusive(); // always notify on shutdown
|
||||
return;
|
||||
}
|
||||
|
||||
pauseExclusive();
|
||||
awaitExclusive();
|
||||
resumeExclusive();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
pauseExclusive();
|
||||
exitLockExclusive();
|
||||
|
||||
try { agent.onUncaughtException(t); }
|
||||
catch (Throwable t2) { }
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void runWithoutLock()
|
||||
{
|
||||
Task task = null;
|
||||
while (true)
|
||||
{
|
||||
lock.lock();
|
||||
try
|
||||
{
|
||||
if (task != null) task.cleanupExclusive();
|
||||
else resumeExclusive();
|
||||
enterLockExclusive();
|
||||
|
||||
while (true)
|
||||
{
|
||||
task = pollWaitingToRunExclusive();
|
||||
if (task != null)
|
||||
{
|
||||
exitLockExclusive();
|
||||
break;
|
||||
}
|
||||
|
||||
if (shutdown)
|
||||
{
|
||||
exitLockExclusive();
|
||||
notifyWorkExclusive();
|
||||
return;
|
||||
}
|
||||
|
||||
pauseExclusive();
|
||||
awaitExclusive();
|
||||
resumeExclusive();
|
||||
}
|
||||
--tasks;
|
||||
task.preRunExclusive();
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
if (task != null)
|
||||
{
|
||||
try { task.fail(t); }
|
||||
catch (Throwable t2) { t.addSuppressed(t2); }
|
||||
try { task.cleanupExclusive(); }
|
||||
catch (Throwable t2) { t.addSuppressed(t2); }
|
||||
try { agent.onUncaughtException(t); }
|
||||
catch (Throwable t2) { /* nothing we can sensibly do after already reporting */ }
|
||||
task = null;
|
||||
}
|
||||
if (isHeldByExecutor)
|
||||
pauseExclusive();
|
||||
exitLockExclusive();
|
||||
continue;
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
task.run();
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
try { task.fail(t); }
|
||||
catch (Throwable t2)
|
||||
{
|
||||
lock.lock();
|
||||
try
|
||||
{
|
||||
t2.addSuppressed(t);
|
||||
agent.onUncaughtException(t2);
|
||||
resumeExclusive();
|
||||
enterLockExclusive();
|
||||
while (true)
|
||||
{
|
||||
task = pollWaitingToRunExclusive();
|
||||
|
||||
if (task != null)
|
||||
{
|
||||
--tasks;
|
||||
try
|
||||
{
|
||||
task.preRunExclusive(this);
|
||||
task.run();
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
task.fail(t);
|
||||
}
|
||||
finally
|
||||
{
|
||||
task.cleanupExclusive(this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shutdown)
|
||||
{
|
||||
pauseExclusive();
|
||||
exitLockExclusive();
|
||||
notifyWorkExclusive(); // always notify on shutdown
|
||||
return;
|
||||
}
|
||||
|
||||
pauseExclusive();
|
||||
awaitExclusive();
|
||||
resumeExclusive();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable t3)
|
||||
catch (Throwable t)
|
||||
{
|
||||
// empty to ensure we definitely loop so we cleanup the task
|
||||
pauseExclusive();
|
||||
exitLockExclusive();
|
||||
|
||||
try { agent.onUncaughtException(t); }
|
||||
catch (Throwable t2) { }
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected LoopTask runWithoutLock(String name)
|
||||
{
|
||||
return new LoopTask(name)
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
Task task = null;
|
||||
while (true)
|
||||
{
|
||||
lock.lock();
|
||||
try
|
||||
{
|
||||
if (task != null) task.cleanupExclusive(this);
|
||||
else resumeExclusive();
|
||||
enterLockExclusive();
|
||||
|
||||
while (true)
|
||||
{
|
||||
task = pollWaitingToRunExclusive();
|
||||
if (task != null)
|
||||
{
|
||||
exitLockExclusive();
|
||||
break;
|
||||
}
|
||||
|
||||
if (shutdown)
|
||||
{
|
||||
exitLockExclusive();
|
||||
notifyWorkExclusive();
|
||||
return;
|
||||
}
|
||||
|
||||
pauseExclusive();
|
||||
awaitExclusive();
|
||||
resumeExclusive();
|
||||
}
|
||||
--tasks;
|
||||
task.preRunExclusive(this);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
if (task != null)
|
||||
{
|
||||
try { task.fail(t); }
|
||||
catch (Throwable t2) { t.addSuppressed(t2); }
|
||||
try { task.cleanupExclusive(this); }
|
||||
catch (Throwable t2) { t.addSuppressed(t2); }
|
||||
try { agent.onUncaughtException(t); }
|
||||
catch (Throwable t2) { /* nothing we can sensibly do after already reporting */ }
|
||||
task = null;
|
||||
}
|
||||
if (isHeldByExecutor)
|
||||
pauseExclusive();
|
||||
exitLockExclusive();
|
||||
continue;
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
task.run();
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
try { task.fail(t); }
|
||||
catch (Throwable t2)
|
||||
{
|
||||
try
|
||||
{
|
||||
t2.addSuppressed(t);
|
||||
agent.onUncaughtException(t2);
|
||||
}
|
||||
catch (Throwable t3)
|
||||
{
|
||||
// empty to ensure we definitely loop so we cleanup the task
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends DebuggableTaskRunner> active()
|
||||
{
|
||||
return loops().active();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -51,6 +51,12 @@ class AccordExecutorAsyncSubmit extends AccordExecutorAbstractSemiSyncSubmit
|
|||
lock.await();
|
||||
}
|
||||
|
||||
@Override
|
||||
AccordExecutorLoops loops()
|
||||
{
|
||||
return loops;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isInLoop()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,12 +20,15 @@ package org.apache.cassandra.service.accord;
|
|||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import accord.utils.Invariants;
|
||||
import org.agrona.collections.Long2ObjectHashMap;
|
||||
import org.apache.cassandra.concurrent.DebuggableTask.DebuggableTaskRunner;
|
||||
import org.apache.cassandra.service.accord.AccordExecutor.Mode;
|
||||
import org.apache.cassandra.service.accord.AccordExecutor.TaskRunner;
|
||||
import org.apache.cassandra.utils.concurrent.Condition;
|
||||
|
||||
import static org.apache.cassandra.concurrent.ExecutorFactory.Global.executorFactory;
|
||||
|
|
@ -36,21 +39,33 @@ import static org.apache.cassandra.utils.Clock.Global.nanoTime;
|
|||
|
||||
class AccordExecutorLoops
|
||||
{
|
||||
static abstract class LoopTask extends TaskRunner implements Runnable
|
||||
{
|
||||
final String id;
|
||||
LoopTask(String id) { this.id = id; }
|
||||
@Override public String id() { return id; }
|
||||
}
|
||||
|
||||
private final Long2ObjectHashMap<Thread> loops;
|
||||
private final Long2ObjectHashMap<LoopTask> tasks;
|
||||
|
||||
private final AtomicInteger running = new AtomicInteger();
|
||||
private final Condition terminated = Condition.newOneTimeCondition();
|
||||
|
||||
public AccordExecutorLoops(Mode mode, int threads, IntFunction<String> name, Function<Mode, Runnable> loopFactory)
|
||||
public AccordExecutorLoops(Mode mode, int threads, IntFunction<String> loopName, BiFunction<String, Mode, LoopTask> loopFactory)
|
||||
{
|
||||
Invariants.require(mode == RUN_WITH_LOCK ? threads == 1 : threads >= 1);
|
||||
running.addAndGet(threads);
|
||||
loops = new Long2ObjectHashMap<>(threads, 0.65f);
|
||||
tasks = new Long2ObjectHashMap<>(threads, 0.65f);
|
||||
for (int i = 0; i < threads; ++i)
|
||||
{
|
||||
Thread thread = executorFactory().startThread(name.apply(i), wrap(loopFactory.apply(mode)), NON_DAEMON, INFINITE_LOOP);
|
||||
String name = loopName.apply(i);
|
||||
LoopTask task = loopFactory.apply(name, mode);
|
||||
Thread thread = executorFactory().startThread(name, wrap(task), NON_DAEMON, INFINITE_LOOP);
|
||||
Thread conflict = loops.putIfAbsent(thread.getId(), thread);
|
||||
Invariants.require(conflict == null || !conflict.isAlive(), "Allocated two threads with the same threadId!");
|
||||
tasks.put(thread.getId(), task);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -70,6 +85,11 @@ class AccordExecutorLoops
|
|||
};
|
||||
}
|
||||
|
||||
public Stream<? extends DebuggableTaskRunner> active()
|
||||
{
|
||||
return tasks.values().stream();
|
||||
}
|
||||
|
||||
public boolean isInLoop()
|
||||
{
|
||||
Thread thread = Thread.currentThread();
|
||||
|
|
|
|||
|
|
@ -53,6 +53,12 @@ class AccordExecutorSemiSyncSubmit extends AccordExecutorAbstractSemiSyncSubmit
|
|||
hasWork.await();
|
||||
}
|
||||
|
||||
@Override
|
||||
AccordExecutorLoops loops()
|
||||
{
|
||||
return loops;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isInLoop()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -92,9 +92,9 @@ class AccordExecutorSimple extends AccordExecutor
|
|||
return;
|
||||
|
||||
--tasks;
|
||||
try { task.preRunExclusive(); task.run(); }
|
||||
try { task.preRunExclusive(null); task.run(); }
|
||||
catch (Throwable t) { task.fail(t); }
|
||||
finally { task.cleanupExclusive(); }
|
||||
finally { task.cleanupExclusive(null); }
|
||||
}
|
||||
}
|
||||
catch (Throwable t)
|
||||
|
|
|
|||
|
|
@ -64,6 +64,12 @@ class AccordExecutorSyncSubmit extends AccordExecutorAbstractLockLoop
|
|||
hasWork.await();
|
||||
}
|
||||
|
||||
@Override
|
||||
AccordExecutorLoops loops()
|
||||
{
|
||||
return loops;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isInLoop()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -164,6 +164,26 @@ public class AccordFetchCoordinator extends AbstractFetchCoordinator implements
|
|||
builder.putAll(that.streams);
|
||||
return new StreamData(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Data without(Ranges ranges)
|
||||
{
|
||||
ImmutableMap.Builder<TokenRange, SessionInfo> copy = ImmutableMap.builder();
|
||||
for (Map.Entry<TokenRange, SessionInfo> e : streams.entrySet())
|
||||
{
|
||||
if (!ranges.intersects(e.getKey()))
|
||||
{
|
||||
copy.put(e.getKey(), e.getValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
Ranges remainder = Ranges.of(e.getKey()).without(ranges);
|
||||
for (Range range : remainder)
|
||||
copy.put((TokenRange) range, e.getValue());
|
||||
}
|
||||
}
|
||||
return new StreamData(copy.build());
|
||||
}
|
||||
}
|
||||
|
||||
// needs to be externally synchronized
|
||||
|
|
@ -265,7 +285,7 @@ public class AccordFetchCoordinator extends AbstractFetchCoordinator implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public AsyncChain<Data> read(Seekable key, SafeCommandStore commandStore, Timestamp executeAt, DataStore store)
|
||||
public AsyncChain<Data> read(SafeCommandStore commandStore, Seekable key, Timestamp executeAt)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -359,7 +379,7 @@ public class AccordFetchCoordinator extends AbstractFetchCoordinator implements
|
|||
|
||||
public AccordFetchCoordinator(Node node, Ranges ranges, SyncPoint syncPoint, DataStore.FetchRanges fetchRanges, CommandStore commandStore)
|
||||
{
|
||||
super(node, ranges, syncPoint, fetchRanges, commandStore);
|
||||
super(node, node.someSequentialExecutor(), ranges, syncPoint, fetchRanges, commandStore);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@ import com.google.common.collect.ImmutableMap;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import accord.api.AsyncExecutor;
|
||||
import accord.api.MessageSink;
|
||||
import accord.impl.RequestCallbacks;
|
||||
import accord.local.AgentExecutor;
|
||||
import accord.local.Node;
|
||||
import accord.messages.Callback;
|
||||
import accord.messages.MessageType;
|
||||
|
|
@ -180,7 +180,7 @@ public class AccordMessageSink implements MessageSink
|
|||
|
||||
// TODO (expected): permit bulk send to save esp. on callback registration (and combine records)
|
||||
@Override
|
||||
public void send(Node.Id to, Request request, int attempt, AgentExecutor executor, Callback callback)
|
||||
public void send(Node.Id to, Request request, int attempt, AsyncExecutor executor, Callback callback)
|
||||
{
|
||||
Verb verb = VerbMapping.getVerb(request);
|
||||
Preconditions.checkNotNull(verb, "Verb is null for type %s", request.type());
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ import accord.primitives.TxnId;
|
|||
import accord.primitives.Unseekables;
|
||||
import accord.primitives.Writes;
|
||||
import accord.utils.ImmutableBitSet;
|
||||
import accord.utils.UnhandledEnum;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.dht.Murmur3Partitioner;
|
||||
import org.apache.cassandra.schema.TableId;
|
||||
|
|
@ -353,6 +354,7 @@ public class AccordObjectSizes
|
|||
case AcceptedMedium:
|
||||
case AcceptedMediumWithDefinition:
|
||||
case AcceptedSlow:
|
||||
case AcceptedSlowWithDefAndVote:
|
||||
case AcceptedSlowWithDefinition:
|
||||
case PreCommitted:
|
||||
case PreCommittedWithDeps:
|
||||
|
|
@ -378,7 +380,7 @@ public class AccordObjectSizes
|
|||
case Invalidated:
|
||||
return INVALIDATED;
|
||||
default:
|
||||
throw new IllegalStateException("Unhandled status " + command.status());
|
||||
throw new UnhandledEnum(command.saveStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import accord.api.Journal;
|
||||
import accord.api.ProtocolModifiers;
|
||||
import accord.coordinate.CoordinateMaxConflict;
|
||||
import accord.coordinate.CoordinateTransaction;
|
||||
import accord.coordinate.KeyBarriers;
|
||||
|
|
@ -131,12 +132,11 @@ import org.apache.cassandra.utils.concurrent.AsyncPromise;
|
|||
import org.apache.cassandra.utils.concurrent.Future;
|
||||
import org.apache.cassandra.utils.concurrent.UncheckedInterruptedException;
|
||||
|
||||
import static accord.api.ProtocolModifiers.Toggles.FastExec.MAY_BYPASS_SAFESTORE;
|
||||
import static accord.local.durability.DurabilityService.SyncLocal.Self;
|
||||
import static accord.local.durability.DurabilityService.SyncRemote.All;
|
||||
import static accord.messages.SimpleReply.Ok;
|
||||
import static accord.primitives.Routable.Domain.Key;
|
||||
import static accord.primitives.Txn.Kind.Write;
|
||||
import static accord.primitives.TxnId.Cardinality.cardinality;
|
||||
import static accord.topology.TopologyManager.TopologyRange;
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
|
@ -154,8 +154,17 @@ import static org.apache.cassandra.utils.Clock.Global.nanoTime;
|
|||
public class AccordService implements IAccordService, Shutdownable
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(AccordService.class);
|
||||
static
|
||||
{
|
||||
ProtocolModifiers.Toggles.setPermitLocalExecution(true);
|
||||
ProtocolModifiers.Toggles.setRequiresUniqueHlcs(true);
|
||||
ProtocolModifiers.Toggles.setFastReadExecMayResendTxn(true);
|
||||
ProtocolModifiers.Toggles.setFastReadExec(MAY_BYPASS_SAFESTORE);
|
||||
ProtocolModifiers.Toggles.setFastWriteExec(MAY_BYPASS_SAFESTORE);
|
||||
ProtocolModifiers.Toggles.setDataStoreDetectsFutureReads(true);
|
||||
}
|
||||
|
||||
private enum State {INIT, STARTED, SHUTTING_DOWN, SHUTDOWN}
|
||||
private enum State { INIT, STARTED, SHUTTING_DOWN, SHUTDOWN }
|
||||
|
||||
private final Node node;
|
||||
private final Shutdownable nodeShutdown;
|
||||
|
|
@ -320,7 +329,7 @@ public class AccordService implements IAccordService, Shutdownable
|
|||
DefaultProgressLogs::new,
|
||||
DefaultLocalListeners.Factory::new,
|
||||
AccordCommandStores.factory(),
|
||||
new AccordInteropFactory(agent, configService),
|
||||
new AccordInteropFactory(configService),
|
||||
journal.durableBeforePersister(),
|
||||
journal);
|
||||
this.nodeShutdown = toShutdownable(node);
|
||||
|
|
@ -515,7 +524,7 @@ public class AccordService implements IAccordService, Shutdownable
|
|||
return syncInternal(minBound, keys, syncLocal, syncRemote);
|
||||
|
||||
return KeyBarriers.find(node, minBound, keys.get(0).toUnseekable(), syncLocal, syncRemote)
|
||||
.flatMap(found -> KeyBarriers.await(node, found, syncLocal, syncRemote))
|
||||
.flatMap(found -> KeyBarriers.await(node, node.someSequentialExecutor(), found, syncLocal, syncRemote))
|
||||
.flatMap(success -> {
|
||||
if (success)
|
||||
return null;
|
||||
|
|
@ -525,7 +534,7 @@ public class AccordService implements IAccordService, Shutdownable
|
|||
|
||||
private AsyncChain<Void> syncInternal(Timestamp minBound, Keys keys, DurabilityService.SyncLocal syncLocal, DurabilityService.SyncRemote syncRemote)
|
||||
{
|
||||
TxnId txnId = node.nextTxnId(minBound, Write, Key, cardinality(keys));
|
||||
TxnId txnId = node.nextTxnId(minBound, keys, Write);
|
||||
FullRoute<?> route = node.computeRoute(txnId, keys);
|
||||
Txn txn = new Txn.InMemory(Write, keys, TxnRead.createNoOpRead(keys), TxnQuery.NONE, TxnUpdate.empty(), new TableMetadatasAndKeys(TableMetadatas.none(), keys));
|
||||
return CoordinateTransaction.coordinate(node, route, txnId, txn)
|
||||
|
|
@ -535,7 +544,7 @@ public class AccordService implements IAccordService, Shutdownable
|
|||
@Override
|
||||
public AsyncChain<Timestamp> maxConflict(Ranges ranges)
|
||||
{
|
||||
return node.commandStores().any().build(() -> CoordinateMaxConflict.maxConflict(node, ranges)).flatMap(i -> i);
|
||||
return CoordinateMaxConflict.maxConflict(node, ranges);
|
||||
}
|
||||
|
||||
public static <V> V getBlocking(AsyncChain<V> async, Seekables<?, ?> keysOrRanges, RequestBookkeeping bookkeeping, long startedAt, long deadline, boolean isTxnRequest)
|
||||
|
|
@ -631,10 +640,16 @@ public class AccordService implements IAccordService, Shutdownable
|
|||
return coordinateAsync(minEpoch, txn, consistencyLevel, requestTime).awaitAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccordExecutor> executors()
|
||||
{
|
||||
return ((AccordCommandStores)node.commandStores()).executors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nonnull IAccordResult<TxnResult> coordinateAsync(long minEpoch, @Nonnull Txn txn, @Nonnull ConsistencyLevel consistencyLevel, @Nonnull Dispatcher.RequestTime requestTime)
|
||||
{
|
||||
TxnId txnId = node.nextTxnId(txn.kind(), txn.keys().domain(), cardinality(txn.keys()));
|
||||
TxnId txnId = node.nextTxnId(txn);
|
||||
long timeout = txnId.isWrite() ? DatabaseDescriptor.getWriteRpcTimeout(NANOSECONDS) : DatabaseDescriptor.getReadRpcTimeout(NANOSECONDS);
|
||||
ClientRequestBookkeeping bookkeeping = txn.isWrite() ? accordWriteBookkeeping : accordReadBookkeeping;
|
||||
bookkeeping.metrics.keySize.update(txn.keys().size());
|
||||
|
|
|
|||
|
|
@ -27,15 +27,16 @@ import java.util.concurrent.CancellationException;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
import accord.api.Journal;
|
||||
import accord.api.RoutingKey;
|
||||
|
|
@ -56,8 +57,10 @@ import accord.utils.async.AsyncChains;
|
|||
import accord.utils.async.Cancellable;
|
||||
import org.agrona.collections.Object2ObjectHashMap;
|
||||
import org.agrona.collections.ObjectHashSet;
|
||||
import org.apache.cassandra.concurrent.DebuggableTask;
|
||||
import org.apache.cassandra.service.accord.AccordCacheEntry.Status;
|
||||
import org.apache.cassandra.service.accord.AccordCommandStore.Caches;
|
||||
import org.apache.cassandra.service.accord.AccordExecutor.SubmittableTask;
|
||||
import org.apache.cassandra.service.accord.AccordExecutor.Task;
|
||||
import org.apache.cassandra.service.accord.AccordExecutor.TaskQueue;
|
||||
import org.apache.cassandra.service.accord.AccordKeyspace.CommandsForKeyAccessor;
|
||||
|
|
@ -85,18 +88,12 @@ import static org.apache.cassandra.service.accord.AccordTask.State.WAITING_TO_RU
|
|||
import static org.apache.cassandra.service.accord.AccordTask.State.WAITING_TO_SCAN_RANGES;
|
||||
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
|
||||
|
||||
public abstract class AccordTask<R> extends Task implements Runnable, Function<SafeCommandStore, R>, Cancellable
|
||||
public abstract class AccordTask<R> extends SubmittableTask implements Runnable, Function<SafeCommandStore, R>, Cancellable, DebuggableTask
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(AccordTask.class);
|
||||
private static final NoSpamLogger noSpamLogger = NoSpamLogger.getLogger(logger, 1, TimeUnit.MINUTES);
|
||||
private static final boolean SANITY_CHECK = DTEST_ACCORD_JOURNAL_SANITY_CHECK_ENABLED.getBoolean();
|
||||
|
||||
private static class LoggingProps
|
||||
{
|
||||
private static final String COMMAND_STORE = "command_store";
|
||||
private static final String ACCORD_TASK = "accord_task";
|
||||
}
|
||||
|
||||
static class ForFunction<R> extends AccordTask<R>
|
||||
{
|
||||
private final Function<? super SafeCommandStore, R> function;
|
||||
|
|
@ -190,9 +187,11 @@ public abstract class AccordTask<R> extends Task implements Runnable, Function<S
|
|||
}
|
||||
|
||||
private State state = INITIALIZED;
|
||||
final AccordCommandStore commandStore;
|
||||
private final PreLoadContext preLoadContext;
|
||||
private final String loggingId;
|
||||
private volatile String loggingId;
|
||||
private static final AtomicLong nextLoggingId = new AtomicLong(Clock.Global.currentTimeMillis());
|
||||
private static final AtomicReferenceFieldUpdater<AccordTask, String> loggingIdUpdater = AtomicReferenceFieldUpdater.newUpdater(AccordTask.class, String.class, "loggingId");
|
||||
|
||||
// TODO (desired): merge all of these maps into one
|
||||
@Nullable Object2ObjectHashMap<TxnId, AccordSafeCommand> commands;
|
||||
|
|
@ -207,43 +206,40 @@ public abstract class AccordTask<R> extends Task implements Runnable, Function<S
|
|||
|
||||
private BiConsumer<? super R, Throwable> callback;
|
||||
private List<Command> sanityCheck;
|
||||
public long createdAt = nanoTime(), loadedAt, runQueuedAt, runAt, completedAt;
|
||||
public long createdAt = nanoTime(), waitingToRunAt, runningAt, completedAt;
|
||||
|
||||
private void setLoggingIds()
|
||||
public AccordTask(@Nonnull AccordCommandStore commandStore, PreLoadContext preLoadContext)
|
||||
{
|
||||
MDC.put(LoggingProps.COMMAND_STORE, commandStore.loggingId);
|
||||
MDC.put(LoggingProps.ACCORD_TASK, loggingId);
|
||||
}
|
||||
|
||||
private void clearLoggingIds()
|
||||
{
|
||||
MDC.remove(LoggingProps.COMMAND_STORE);
|
||||
MDC.remove(LoggingProps.ACCORD_TASK);
|
||||
}
|
||||
|
||||
public AccordTask(AccordCommandStore commandStore, PreLoadContext preLoadContext)
|
||||
{
|
||||
super(commandStore);
|
||||
this.loggingId = "0x" + Long.toHexString(nextLoggingId.incrementAndGet());
|
||||
this.commandStore = commandStore;
|
||||
this.preLoadContext = preLoadContext;
|
||||
this.loggingId = "0x" + Long.toHexString(nextLoggingId.incrementAndGet());
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
setLoggingIds();
|
||||
logger.trace("Created {} on {}", this, commandStore);
|
||||
clearLoggingIds();
|
||||
}
|
||||
|
||||
private String loggingId()
|
||||
{
|
||||
String id = loggingId;
|
||||
if (id == null)
|
||||
{
|
||||
TxnId primaryTxnId = preLoadContext.primaryTxnId();
|
||||
id = "0x" + Long.toHexString(nextLoggingId.incrementAndGet()) + (primaryTxnId != null ? '/' + primaryTxnId.toString() : "");
|
||||
if (!loggingIdUpdater.compareAndSet(this, null, id))
|
||||
id = loggingId;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "AccordTask{" + state + "}-" + loggingId;
|
||||
return "AccordTask{" + state + "}-" + loggingId();
|
||||
}
|
||||
|
||||
public String toDescription()
|
||||
{
|
||||
return "AccordTask{" + state + "}-" + loggingId + ": "
|
||||
return "AccordTask{" + state + "}-" + loggingId() + ": "
|
||||
+ (queued == null ? "unqueued" : queued.kind)
|
||||
+ ", primaryTxnId: " + preLoadContext.primaryTxnId()
|
||||
+ ", waitingToLoad: " + summarise(waitingToLoad)
|
||||
|
|
@ -298,11 +294,11 @@ public abstract class AccordTask<R> extends Task implements Runnable, Function<S
|
|||
{
|
||||
Invariants.require(rangeScanner == null || rangeScanner.scanned);
|
||||
Invariants.require(loading == null && waitingToLoad == null, "WAITING_TO_RUN => no loading or waiting; found %s", this, AccordTask::toDescription);
|
||||
loadedAt = nanoTime();
|
||||
waitingToRunAt = nanoTime();
|
||||
}
|
||||
else if (state == RUNNING)
|
||||
{
|
||||
runAt = nanoTime();
|
||||
runningAt = nanoTime();
|
||||
}
|
||||
else if (state.isExecuted())
|
||||
{
|
||||
|
|
@ -315,6 +311,8 @@ public abstract class AccordTask<R> extends Task implements Runnable, Function<S
|
|||
return preLoadContext.keys();
|
||||
}
|
||||
|
||||
// TODO (expected): try to execute immediately BUT consider ordering requirements
|
||||
// esp. with deferred actions on e.g. CommandsForKey (not yet supported but also important for performance)
|
||||
public AsyncChain<R> chain()
|
||||
{
|
||||
return new AsyncChains.Head<>()
|
||||
|
|
@ -360,6 +358,12 @@ public abstract class AccordTask<R> extends Task implements Runnable, Function<S
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void submitExclusive(AccordExecutor owner)
|
||||
{
|
||||
owner.submitExclusive(this);
|
||||
}
|
||||
|
||||
public void setupExclusive()
|
||||
{
|
||||
setupInternal(commandStore.cachesExclusive());
|
||||
|
|
@ -634,9 +638,12 @@ public abstract class AccordTask<R> extends Task implements Runnable, Function<S
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void preRunExclusive()
|
||||
protected void preRunExclusive(@Nullable AccordExecutor.TaskRunner runner)
|
||||
{
|
||||
state(RUNNING);
|
||||
queued = null;
|
||||
if (runner != null)
|
||||
runner.setDebuggable(this);
|
||||
if (rangeScanner != null)
|
||||
{
|
||||
commandsForRanges = rangeScanner.finish(commandStore.cachesExclusive());
|
||||
|
|
@ -651,7 +658,6 @@ public abstract class AccordTask<R> extends Task implements Runnable, Function<S
|
|||
@Override
|
||||
public void run()
|
||||
{
|
||||
setLoggingIds();
|
||||
logger.trace("Running {} with state {}", this, state);
|
||||
AccordSafeCommandStore safeStore = null;
|
||||
try
|
||||
|
|
@ -708,7 +714,6 @@ public abstract class AccordTask<R> extends Task implements Runnable, Function<S
|
|||
finally
|
||||
{
|
||||
logger.trace("Exiting {}", this);
|
||||
clearLoggingIds();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -750,8 +755,10 @@ public abstract class AccordTask<R> extends Task implements Runnable, Function<S
|
|||
}
|
||||
}
|
||||
|
||||
protected void cleanupExclusive()
|
||||
protected void cleanupExclusive(@Nullable AccordExecutor.TaskRunner runner)
|
||||
{
|
||||
if (runner != null)
|
||||
runner.clearDebuggable();
|
||||
releaseResources(commandStore.cachesExclusive());
|
||||
if (state == FAILING)
|
||||
state(FAILED);
|
||||
|
|
@ -1084,7 +1091,7 @@ public abstract class AccordTask<R> extends Task implements Runnable, Function<S
|
|||
|
||||
void startInternal(Caches caches)
|
||||
{
|
||||
summaryLoader = commandStore.diskCommandsForRanges().loader(preLoadContext.primaryTxnId(), preLoadContext.keyHistory(), keysOrRanges);
|
||||
summaryLoader = commandStore.commandsForRanges().loader(preLoadContext.primaryTxnId(), preLoadContext.keyHistory(), keysOrRanges);
|
||||
summaryLoader.forEachInCache(summary -> summaries.put(summary.txnId, summary), caches);
|
||||
caches.commands().register(commandWatcher);
|
||||
}
|
||||
|
|
@ -1113,6 +1120,35 @@ public abstract class AccordTask<R> extends Task implements Runnable, Function<S
|
|||
caches.commands().unregister(commandWatcher);
|
||||
return new CommandsForRanges(summaries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Scanning range intersections for " + AccordTask.this;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggableTask debuggable()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long creationTimeNanos()
|
||||
{
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long startTimeNanos()
|
||||
{
|
||||
return runningAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description()
|
||||
{
|
||||
return toString();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ public class AccordVerbHandler<T extends Request> implements IVerbHandler<T>
|
|||
}
|
||||
else
|
||||
{
|
||||
node.withEpochAtLeast(waitForEpoch, (ignored, withEpochFailure) -> {
|
||||
node.withEpochAtLeast(waitForEpoch, null, (ignored, withEpochFailure) -> {
|
||||
if (withEpochFailure != null)
|
||||
throw new RuntimeException("Timed out waiting for epoch when processing message from " + fromNodeId + " to " + node + " message " + message, withEpochFailure);
|
||||
request.process(node, fromNodeId, message.header);
|
||||
|
|
|
|||
|
|
@ -85,6 +85,8 @@ public interface IAccordService
|
|||
@Nonnull IAccordResult<TxnResult> coordinateAsync(long minEpoch, @Nonnull Txn txn, @Nonnull ConsistencyLevel consistencyLevel, RequestTime requestTime);
|
||||
@Nonnull TxnResult coordinate(long minEpoch, @Nonnull Txn txn, @Nonnull ConsistencyLevel consistencyLevel, RequestTime requestTime) throws RequestExecutionException;
|
||||
|
||||
List<AccordExecutor> executors();
|
||||
|
||||
interface IAccordResult<V>
|
||||
{
|
||||
V success();
|
||||
|
|
@ -214,6 +216,12 @@ public interface IAccordService
|
|||
throw new UnsupportedOperationException("No accord transaction should be executed when accord.enabled = false in cassandra.yaml");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccordExecutor> executors()
|
||||
{
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nonnull IAccordResult<TxnResult> coordinateAsync(long minEpoch, @Nonnull Txn txn, @Nonnull ConsistencyLevel consistencyLevel, RequestTime requestTime)
|
||||
{
|
||||
|
|
@ -383,6 +391,12 @@ public interface IAccordService
|
|||
return delegate.coordinate(minEpoch, txn, consistencyLevel, requestTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccordExecutor> executors()
|
||||
{
|
||||
return delegate.executors();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public IAccordResult<TxnResult> coordinateAsync(long minEpoch, @Nonnull Txn txn, @Nonnull ConsistencyLevel consistencyLevel, RequestTime requestTime)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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.Callable;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import accord.api.AsyncExecutor;
|
||||
import accord.utils.async.AsyncChain;
|
||||
import accord.utils.async.AsyncChains;
|
||||
import org.apache.cassandra.service.accord.api.AccordAgent;
|
||||
|
||||
public class ImmediateAsyncExecutor implements AsyncExecutor, BiConsumer<Object, Throwable>
|
||||
{
|
||||
public static final ImmediateAsyncExecutor INSTANCE = new ImmediateAsyncExecutor();
|
||||
|
||||
@Override
|
||||
public <T> AsyncChain<T> build(Callable<T> task)
|
||||
{
|
||||
try
|
||||
{
|
||||
return AsyncChains.success(task.call());
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
AccordAgent.handleUncaughtException(t);
|
||||
return AsyncChains.failure(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@Nonnull Runnable command)
|
||||
{
|
||||
try
|
||||
{
|
||||
command.run();
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
AccordAgent.handleUncaughtException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Object o, Throwable throwable)
|
||||
{
|
||||
if (throwable != null)
|
||||
AccordAgent.handleUncaughtException(throwable);
|
||||
}
|
||||
}
|
||||
|
|
@ -22,8 +22,6 @@ import java.util.concurrent.CancellationException;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
|
@ -40,7 +38,6 @@ import accord.local.SafeCommandStore;
|
|||
import accord.local.TimeService;
|
||||
import accord.messages.ReplyContext;
|
||||
import accord.primitives.Keys;
|
||||
import accord.primitives.Participants;
|
||||
import accord.primitives.Ranges;
|
||||
import accord.primitives.Routable;
|
||||
import accord.primitives.Status;
|
||||
|
|
@ -143,10 +140,14 @@ public class AccordAgent implements Agent
|
|||
|
||||
@Override
|
||||
public void onUncaughtException(Throwable t)
|
||||
{
|
||||
handleUncaughtException(t);
|
||||
}
|
||||
|
||||
public static void handleUncaughtException(Throwable t)
|
||||
{
|
||||
if (t instanceof RequestTimeoutException || t instanceof CancellationException)
|
||||
return;
|
||||
logger.error("Uncaught accord exception", t);
|
||||
JVMStabilityInspector.uncaughtException(Thread.currentThread(), t);
|
||||
}
|
||||
|
||||
|
|
@ -154,7 +155,7 @@ public class AccordAgent implements Agent
|
|||
public void onCaughtException(Throwable t, String context)
|
||||
{
|
||||
logger.warn(context, t);
|
||||
JVMStabilityInspector.uncaughtException(Thread.currentThread(), t);
|
||||
JVMStabilityInspector.inspectThrowable(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -351,10 +352,4 @@ public class AccordAgent implements Agent
|
|||
{
|
||||
return node.now() - (100 + getAccordScheduleDurabilityTxnIdLag(MICROSECONDS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViolation(String message, Participants<?> participants, @Nullable TxnId notWitnessed, @Nullable Timestamp notWitnessedExecuteAt, @Nullable TxnId by, @Nullable Timestamp byEexecuteAt)
|
||||
{
|
||||
logger.error(message);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.api;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import accord.api.ViolationHandler;
|
||||
import accord.primitives.Participants;
|
||||
import accord.primitives.Timestamp;
|
||||
import accord.primitives.TxnId;
|
||||
|
||||
public class AccordViolationHandler implements ViolationHandler
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(AccordViolationHandler.class);
|
||||
|
||||
static
|
||||
{
|
||||
ViolationHandlerHolder.set(AccordViolationHandler::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViolation(String message, Participants<?> participants, @Nullable TxnId notWitnessed, @Nullable Timestamp notWitnessedExecuteAt, @Nullable TxnId by, @Nullable Timestamp byEexecuteAt)
|
||||
{
|
||||
logger.error(message);
|
||||
}
|
||||
}
|
||||
|
|
@ -27,9 +27,10 @@ import accord.api.Result;
|
|||
import accord.api.Update;
|
||||
import accord.coordinate.CoordinationAdapter;
|
||||
import accord.coordinate.CoordinationAdapter.Adapters.TxnAdapter;
|
||||
import accord.coordinate.ExecuteFlag.ExecuteFlags;
|
||||
import accord.coordinate.ExecuteFlag.CoordinationFlags;
|
||||
import accord.coordinate.ExecutePath;
|
||||
import accord.local.Node;
|
||||
import accord.local.SequentialAsyncExecutor;
|
||||
import accord.messages.Apply;
|
||||
import accord.primitives.Ballot;
|
||||
import accord.primitives.Deps;
|
||||
|
|
@ -43,8 +44,6 @@ import accord.topology.Topologies;
|
|||
import accord.topology.Topologies.SelectNodeOwnership;
|
||||
import org.apache.cassandra.db.ConsistencyLevel;
|
||||
import org.apache.cassandra.service.accord.AccordEndpointMapper;
|
||||
import org.apache.cassandra.service.accord.api.AccordAgent;
|
||||
import org.apache.cassandra.service.accord.interop.AccordInteropExecution.InteropExecutor;
|
||||
import org.apache.cassandra.service.accord.txn.AccordUpdate;
|
||||
import org.apache.cassandra.service.accord.txn.TxnRead;
|
||||
|
||||
|
|
@ -58,11 +57,10 @@ public class AccordInteropAdapter extends TxnAdapter
|
|||
{
|
||||
final AccordInteropAdapter standard, recovery;
|
||||
|
||||
public AccordInteropFactory(AccordAgent agent, AccordEndpointMapper endpointMapper)
|
||||
public AccordInteropFactory(AccordEndpointMapper endpointMapper)
|
||||
{
|
||||
final InteropExecutor executor = new InteropExecutor(agent);
|
||||
standard = new AccordInteropAdapter(executor, endpointMapper, Minimal);
|
||||
recovery = new AccordInteropAdapter(executor, endpointMapper, Maximal);
|
||||
standard = new AccordInteropAdapter(endpointMapper, Minimal);
|
||||
recovery = new AccordInteropAdapter(endpointMapper, Maximal);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -74,36 +72,33 @@ public class AccordInteropAdapter extends TxnAdapter
|
|||
}
|
||||
};
|
||||
|
||||
private final InteropExecutor executor;
|
||||
private final AccordEndpointMapper endpointMapper;
|
||||
private final Apply.Kind applyKind;
|
||||
|
||||
private AccordInteropAdapter(InteropExecutor executor, AccordEndpointMapper endpointMapper, Apply.Kind applyKind)
|
||||
private AccordInteropAdapter(AccordEndpointMapper endpointMapper, Apply.Kind applyKind)
|
||||
{
|
||||
super(Minimal);
|
||||
this.executor = executor;
|
||||
this.endpointMapper = endpointMapper;
|
||||
this.applyKind = applyKind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Node node, Topologies any, FullRoute<?> route, Ballot ballot, ExecutePath path, ExecuteFlags executeFlags, TxnId txnId, Txn txn, Timestamp executeAt, Deps stableDeps, Deps sendDeps, BiConsumer<? super Result, Throwable> callback)
|
||||
public void execute(Node node, SequentialAsyncExecutor executor, Topologies any, FullRoute<?> route, Ballot ballot, ExecutePath path, CoordinationFlags flags, TxnId txnId, Txn txn, Timestamp executeAt, Deps stableDeps, Deps sendDeps, BiConsumer<? super Result, Throwable> callback)
|
||||
{
|
||||
if (!doInteropExecute(node, route, ballot, txnId, txn, executeAt, stableDeps, callback))
|
||||
super.execute(node, any, route, ballot, path, executeFlags, txnId, txn, executeAt, stableDeps, sendDeps, callback);
|
||||
if (!doInteropExecute(node, executor, route, ballot, txnId, txn, executeAt, stableDeps, callback))
|
||||
super.execute(node, executor, any, route, ballot, path, flags, txnId, txn, executeAt, stableDeps, sendDeps, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void persist(Node node, Topologies any, Route<?> require, Route<?> sendTo, SelectNodeOwnership selectSendTo, FullRoute<?> route, Ballot ballot, TxnId txnId, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, boolean informDurableOnDone, BiConsumer<? super Result, Throwable> callback)
|
||||
public void persist(Node node, SequentialAsyncExecutor executor, Topologies any, Route<?> require, Route<?> sendTo, SelectNodeOwnership selectSendTo, FullRoute<?> route, Ballot ballot, CoordinationFlags flags, TxnId txnId, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, boolean informDurableOnDone, BiConsumer<? super Result, Throwable> callback)
|
||||
{
|
||||
if (applyKind == Minimal && doInteropPersist(node, any, require, sendTo, selectSendTo, ballot, txnId, txn, executeAt, deps, writes, result, route, informDurableOnDone, callback))
|
||||
if (applyKind == Minimal && doInteropPersist(node, executor, any, require, sendTo, selectSendTo, ballot, txnId, txn, executeAt, deps, writes, result, route, informDurableOnDone, callback))
|
||||
return;
|
||||
|
||||
super.persist(node, any, require, sendTo, selectSendTo, route, ballot, txnId, txn, executeAt, deps, writes, result, informDurableOnDone, callback);
|
||||
super.persist(node, executor, any, require, sendTo, selectSendTo, route, ballot, flags, txnId, txn, executeAt, deps, writes, result, informDurableOnDone, callback);
|
||||
}
|
||||
|
||||
|
||||
private boolean doInteropExecute(Node node, FullRoute<?> route, Ballot ballot, TxnId txnId, Txn txn, Timestamp executeAt, Deps deps, BiConsumer<? super Result, Throwable> callback)
|
||||
private boolean doInteropExecute(Node node, SequentialAsyncExecutor executor, FullRoute<?> route, Ballot ballot, TxnId txnId, Txn txn, Timestamp executeAt, Deps deps, BiConsumer<? super Result, Throwable> callback)
|
||||
{
|
||||
// Unrecoverable repair always needs to be run by AccordInteropExecution
|
||||
AccordUpdate.Kind updateKind = AccordUpdate.kind(txn.update());
|
||||
|
|
@ -116,7 +111,7 @@ public class AccordInteropAdapter extends TxnAdapter
|
|||
return true;
|
||||
}
|
||||
|
||||
private boolean doInteropPersist(Node node, Topologies any, Route<?> require, Route<?> sendTo, SelectNodeOwnership selectSendTo, Ballot ballot, TxnId txnId, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, FullRoute<?> fullRoute, boolean informDurableOnDone, BiConsumer<? super Result, Throwable> callback)
|
||||
private boolean doInteropPersist(Node node, SequentialAsyncExecutor executor, Topologies any, Route<?> require, Route<?> sendTo, SelectNodeOwnership selectSendTo, Ballot ballot, TxnId txnId, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, FullRoute<?> fullRoute, boolean informDurableOnDone, BiConsumer<? super Result, Throwable> callback)
|
||||
{
|
||||
Update update = txn.update();
|
||||
ConsistencyLevel consistencyLevel = update instanceof AccordUpdate ? ((AccordUpdate) update).cassandraCommitCL() : null;
|
||||
|
|
@ -124,7 +119,7 @@ public class AccordInteropAdapter extends TxnAdapter
|
|||
return false;
|
||||
|
||||
Topologies all = execution(node, any, sendTo, selectSendTo, fullRoute, txnId, executeAt);
|
||||
new AccordInteropPersist(node, all, txnId, require, ballot, txn, executeAt, deps, writes, result, fullRoute, consistencyLevel, informDurableOnDone, callback)
|
||||
new AccordInteropPersist(node, executor, all, txnId, require, ballot, txn, executeAt, deps, writes, result, fullRoute, consistencyLevel, CoordinationFlags.none(), informDurableOnDone, callback)
|
||||
.start(Minimal, any, writes, result);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import javax.annotation.Nullable;
|
|||
|
||||
import accord.api.LocalListeners;
|
||||
import accord.api.Result;
|
||||
import accord.coordinate.ExecuteFlag.ExecuteFlags;
|
||||
import accord.local.Command;
|
||||
import accord.local.Node.Id;
|
||||
import accord.local.SafeCommand;
|
||||
|
|
@ -66,23 +67,23 @@ public class AccordInteropApply extends Apply implements LocalListeners.ComplexL
|
|||
public static final Apply.Factory FACTORY = new Apply.Factory()
|
||||
{
|
||||
@Override
|
||||
public Apply create(Kind kind, Id to, Topologies participates, TxnId txnId, Ballot ballot, Route<?> route, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, FullRoute<?> fullRoute)
|
||||
public Apply create(Kind kind, Id to, Topologies participates, TxnId txnId, Ballot ballot, Route<?> route, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, FullRoute<?> fullRoute, ExecuteFlags flags)
|
||||
{
|
||||
checkArgument(kind != Kind.Maximal, "Shouldn't need to send a maximal commit with interop support");
|
||||
ConsistencyLevel commitCL = txn.update() instanceof AccordUpdate ? ((AccordUpdate) txn.update()).cassandraCommitCL() : null;
|
||||
// Any asynchronous apply option should use the regular Apply that doesn't wait for writes to complete
|
||||
if (commitCL == null || commitCL == ConsistencyLevel.ANY)
|
||||
return Apply.FACTORY.create(kind, to, participates, txnId, ballot, route, txn, executeAt, deps, writes, result, fullRoute);
|
||||
return new AccordInteropApply(kind, to, participates, txnId, ballot, route, txn, executeAt, deps, writes, result, fullRoute);
|
||||
return Apply.FACTORY.create(kind, to, participates, txnId, ballot, route, txn, executeAt, deps, writes, result, fullRoute, flags);
|
||||
return new AccordInteropApply(kind, to, participates, txnId, ballot, route, txn, executeAt, deps, writes, result, fullRoute, flags);
|
||||
}
|
||||
};
|
||||
|
||||
public static final IVersionedSerializer<AccordInteropApply> serializer = new ApplySerializer<AccordInteropApply>()
|
||||
{
|
||||
@Override
|
||||
protected AccordInteropApply deserializeApply(TxnId txnId, Ballot ballot, Route<?> scope, long minEpoch, long waitForEpoch, long maxEpoch, Apply.Kind kind, Timestamp executeAt, PartialDeps deps, PartialTxn txn, @Nullable FullRoute<?> fullRoute, Writes writes, Result result)
|
||||
protected AccordInteropApply deserializeApply(TxnId txnId, Ballot ballot, Route<?> scope, long minEpoch, long waitForEpoch, long maxEpoch, Apply.Kind kind, Timestamp executeAt, PartialDeps deps, PartialTxn txn, @Nullable FullRoute<?> fullRoute, Writes writes, Result result, ExecuteFlags flags)
|
||||
{
|
||||
return new AccordInteropApply(kind, txnId, ballot, scope, minEpoch, waitForEpoch, maxEpoch, executeAt, deps, txn, fullRoute, writes, result);
|
||||
return new AccordInteropApply(kind, txnId, ballot, scope, minEpoch, waitForEpoch, maxEpoch, executeAt, deps, txn, fullRoute, writes, result, flags);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -90,14 +91,14 @@ public class AccordInteropApply extends Apply implements LocalListeners.ComplexL
|
|||
transient Int2ObjectHashMap<LocalListeners.Registered> listeners;
|
||||
boolean failed;
|
||||
|
||||
private AccordInteropApply(Kind kind, TxnId txnId, Ballot ballot, Route<?> route, long minEpoch, long waitForEpoch, long maxEpoch, Timestamp executeAt, PartialDeps deps, @Nullable PartialTxn txn, @Nullable FullRoute<?> fullRoute, Writes writes, Result result)
|
||||
private AccordInteropApply(Kind kind, TxnId txnId, Ballot ballot, Route<?> route, long minEpoch, long waitForEpoch, long maxEpoch, Timestamp executeAt, PartialDeps deps, @Nullable PartialTxn txn, @Nullable FullRoute<?> fullRoute, Writes writes, Result result, ExecuteFlags flags)
|
||||
{
|
||||
super(kind, txnId, ballot, route, minEpoch, waitForEpoch, maxEpoch, executeAt, deps, txn, fullRoute, writes, result);
|
||||
super(kind, txnId, ballot, route, minEpoch, waitForEpoch, maxEpoch, executeAt, deps, txn, fullRoute, writes, result, flags);
|
||||
}
|
||||
|
||||
private AccordInteropApply(Kind kind, Id to, Topologies participates, TxnId txnId, Ballot ballot, Route<?> route, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, FullRoute<?> fullRoute)
|
||||
private AccordInteropApply(Kind kind, Id to, Topologies participates, TxnId txnId, Ballot ballot, Route<?> route, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, FullRoute<?> fullRoute, ExecuteFlags flags)
|
||||
{
|
||||
super(kind, to, participates, txnId, ballot, route, txn, executeAt, deps, writes, result, fullRoute);
|
||||
super(kind, to, participates, txnId, ballot, route, txn, executeAt, deps, writes, result, fullRoute, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -22,19 +22,17 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import accord.api.Agent;
|
||||
import accord.api.Data;
|
||||
import accord.api.Result;
|
||||
import accord.coordinate.CoordinationAdapter;
|
||||
import accord.local.AgentExecutor;
|
||||
import accord.local.CommandStore;
|
||||
import accord.coordinate.ExecuteFlag.CoordinationFlags;
|
||||
import accord.local.Node;
|
||||
import accord.local.Node.Id;
|
||||
import accord.local.SequentialAsyncExecutor;
|
||||
import accord.messages.Commit;
|
||||
import accord.messages.Commit.Kind;
|
||||
import accord.primitives.AbstractRanges;
|
||||
|
|
@ -76,7 +74,6 @@ import org.apache.cassandra.schema.TableId;
|
|||
import org.apache.cassandra.service.StorageProxy;
|
||||
import org.apache.cassandra.service.accord.AccordEndpointMapper;
|
||||
import org.apache.cassandra.service.accord.TokenRange;
|
||||
import org.apache.cassandra.service.accord.api.AccordAgent;
|
||||
import org.apache.cassandra.service.accord.api.TokenKey;
|
||||
import org.apache.cassandra.service.accord.interop.AccordInteropReadCallback.MaximalCommitSender;
|
||||
import org.apache.cassandra.service.accord.serializers.TableMetadatasAndKeys;
|
||||
|
|
@ -111,35 +108,6 @@ import static org.apache.cassandra.metrics.ClientRequestsMetricsHolder.accordWri
|
|||
*/
|
||||
public class AccordInteropExecution implements ReadCoordinator, MaximalCommitSender
|
||||
{
|
||||
static class InteropExecutor implements AgentExecutor
|
||||
{
|
||||
private final AccordAgent agent;
|
||||
|
||||
public InteropExecutor(AccordAgent agent)
|
||||
{
|
||||
this.agent = agent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Agent agent()
|
||||
{
|
||||
return agent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> AsyncChain<T> build(Callable<T> task)
|
||||
{
|
||||
try
|
||||
{
|
||||
return AsyncChains.success(task.call());
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
return AsyncChains.failure(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Node node;
|
||||
private final TxnId txnId;
|
||||
private final Txn txn;
|
||||
|
|
@ -148,7 +116,7 @@ public class AccordInteropExecution implements ReadCoordinator, MaximalCommitSen
|
|||
private final Timestamp executeAt;
|
||||
private final Deps deps;
|
||||
private final BiConsumer<? super Result, Throwable> callback;
|
||||
private final AgentExecutor executor;
|
||||
private final SequentialAsyncExecutor executor;
|
||||
private final ConsistencyLevel consistencyLevel;
|
||||
private final AccordEndpointMapper endpointMapper;
|
||||
|
||||
|
|
@ -163,7 +131,7 @@ public class AccordInteropExecution implements ReadCoordinator, MaximalCommitSen
|
|||
private final AccordUpdate.Kind updateKind;
|
||||
|
||||
public AccordInteropExecution(Node node, TxnId txnId, Txn txn, AccordUpdate.Kind updateKind, FullRoute<?> route, Ballot ballot, Timestamp executeAt, Deps deps, BiConsumer<? super Result, Throwable> callback,
|
||||
AgentExecutor executor, ConsistencyLevel consistencyLevel, AccordEndpointMapper endpointMapper)
|
||||
SequentialAsyncExecutor executor, ConsistencyLevel consistencyLevel, AccordEndpointMapper endpointMapper)
|
||||
{
|
||||
requireArgument(!txn.read().keys().isEmpty() || updateKind == AccordUpdate.Kind.UNRECOVERABLE_REPAIR);
|
||||
this.node = node;
|
||||
|
|
@ -401,10 +369,9 @@ public class AccordInteropExecution implements ReadCoordinator, MaximalCommitSen
|
|||
else
|
||||
result = readChains();
|
||||
|
||||
CommandStore cs = node.commandStores().select(route.homeKey());
|
||||
result.beginAsResult().withExecutor(cs).begin((data, failure) -> {
|
||||
result.begin((data, failure) -> {
|
||||
if (failure == null)
|
||||
((CoordinationAdapter)node.coordinationAdapter(txnId, Standard)).persist(node, executes, route, ballot, txnId, txn, executeAt, deps, txnId.is(Write) ? txn.execute(txnId, executeAt, data) : null, txn.result(txnId, executeAt, data), callback);
|
||||
((CoordinationAdapter)node.coordinationAdapter(txnId, Standard)).persist(node, executor, executes, route, ballot, CoordinationFlags.none(), txnId, txn, executeAt, deps, txnId.is(Write) ? txn.execute(txnId, executeAt, data) : null, txn.result(txnId, executeAt, data), callback);
|
||||
else
|
||||
callback.accept(null, failure);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -21,12 +21,14 @@ package org.apache.cassandra.service.accord.interop;
|
|||
import java.util.function.BiConsumer;
|
||||
|
||||
import accord.api.Result;
|
||||
import accord.coordinate.ExecuteFlag;
|
||||
import accord.coordinate.Persist;
|
||||
import accord.coordinate.tracking.AllTracker;
|
||||
import accord.coordinate.tracking.QuorumTracker;
|
||||
import accord.coordinate.tracking.RequestStatus;
|
||||
import accord.coordinate.tracking.ResponseTracker;
|
||||
import accord.local.Node;
|
||||
import accord.local.SequentialAsyncExecutor;
|
||||
import accord.messages.Apply;
|
||||
import accord.primitives.Ballot;
|
||||
import accord.primitives.Deps;
|
||||
|
|
@ -109,9 +111,9 @@ public class AccordInteropPersist extends Persist
|
|||
private final ConsistencyLevel consistencyLevel;
|
||||
private CallbackHolder callback;
|
||||
|
||||
public AccordInteropPersist(Node node, Topologies topologies, TxnId txnId, Route<?> sendTo, Ballot ballot, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, FullRoute<?> fullRoute, ConsistencyLevel consistencyLevel, boolean informDurableOnDone, BiConsumer<? super Result, Throwable> clientCallback)
|
||||
public AccordInteropPersist(Node node, SequentialAsyncExecutor executor, Topologies topologies, TxnId txnId, Route<?> sendTo, Ballot ballot, Txn txn, Timestamp executeAt, Deps deps, Writes writes, Result result, FullRoute<?> fullRoute, ConsistencyLevel consistencyLevel, ExecuteFlag.CoordinationFlags flags, boolean informDurableOnDone, BiConsumer<? super Result, Throwable> clientCallback)
|
||||
{
|
||||
super(node, topologies, txnId, ballot, sendTo, txn, executeAt, deps, writes, result, fullRoute, informDurableOnDone, AccordInteropApply.FACTORY);
|
||||
super(node, executor, topologies, txnId, ballot, sendTo, txn, executeAt, deps, writes, result, fullRoute, flags, informDurableOnDone, AccordInteropApply.FACTORY);
|
||||
Invariants.requireArgument(consistencyLevel == ConsistencyLevel.QUORUM || consistencyLevel == ConsistencyLevel.ALL || consistencyLevel == ConsistencyLevel.SERIAL || consistencyLevel == ConsistencyLevel.ONE);
|
||||
this.consistencyLevel = consistencyLevel;
|
||||
registerClientCallback(result, clientCallback);
|
||||
|
|
|
|||
|
|
@ -62,8 +62,7 @@ import org.apache.cassandra.service.accord.api.TokenKey;
|
|||
import org.apache.cassandra.service.accord.serializers.CommandSerializers;
|
||||
import org.apache.cassandra.service.accord.serializers.IVersionedSerializer;
|
||||
import org.apache.cassandra.service.accord.serializers.KeySerializers;
|
||||
import org.apache.cassandra.service.accord.serializers.ReadDataSerializers;
|
||||
import org.apache.cassandra.service.accord.serializers.ReadDataSerializers.ReadDataSerializer;
|
||||
import org.apache.cassandra.service.accord.serializers.ReadDataSerializer;
|
||||
import org.apache.cassandra.service.accord.serializers.Version;
|
||||
import org.apache.cassandra.service.accord.txn.TxnNamedRead;
|
||||
import org.apache.cassandra.service.accord.txn.TxnRead;
|
||||
|
|
@ -77,7 +76,7 @@ import static com.google.common.base.Preconditions.checkState;
|
|||
public class AccordInteropRead extends ReadData
|
||||
{
|
||||
|
||||
public static final IVersionedSerializer<AccordInteropRead> requestSerializer = new ReadDataSerializer<AccordInteropRead>()
|
||||
public static final IVersionedSerializer<AccordInteropRead> requestSerializer = new IVersionedSerializer<>()
|
||||
{
|
||||
@Override
|
||||
public void serialize(AccordInteropRead read, DataOutputPlus out, Version version) throws IOException
|
||||
|
|
@ -108,7 +107,7 @@ public class AccordInteropRead extends ReadData
|
|||
}
|
||||
};
|
||||
|
||||
public static final IVersionedSerializer<ReadReply> replySerializer = new ReadDataSerializers.ReplySerializer<>(LocalReadData.serializer);
|
||||
public static final IVersionedSerializer<ReadReply> replySerializer = new ReadDataSerializer.ReplySerializer<>(LocalReadData.serializer);
|
||||
|
||||
private static class LocalReadData implements Data
|
||||
{
|
||||
|
|
@ -153,6 +152,14 @@ public class AccordInteropRead extends ReadData
|
|||
this.remoteResponse = null;
|
||||
}
|
||||
|
||||
private LocalReadData(@Nonnull List<Pair<TokenKey, ReadResponse>> responses, @Nonnull ReadCommand readCommand)
|
||||
{
|
||||
checkNotNull(readCommand, "readCommand is null");
|
||||
this.localResponses = responses;
|
||||
this.readCommand = readCommand;
|
||||
this.remoteResponse = null;
|
||||
}
|
||||
|
||||
public LocalReadData(@Nonnull ReadResponse remoteResponse)
|
||||
{
|
||||
checkNotNull(remoteResponse);
|
||||
|
|
@ -172,9 +179,15 @@ public class AccordInteropRead extends ReadData
|
|||
@Override
|
||||
public Data merge(Data data)
|
||||
{
|
||||
if (localResponses.isEmpty())
|
||||
return data;
|
||||
|
||||
LocalReadData other = (LocalReadData)data;
|
||||
if (other.localResponses.isEmpty())
|
||||
return this;
|
||||
|
||||
checkState(remoteResponse == null, "Already serialized");
|
||||
checkState(readCommand.isRangeRequest(), "Should only ever be a single partition");
|
||||
LocalReadData other = (LocalReadData)data;
|
||||
checkState(readCommand == other.readCommand, "Should share the same ReadCommand");
|
||||
if (localResponses.size() == 1)
|
||||
{
|
||||
|
|
@ -182,10 +195,25 @@ public class AccordInteropRead extends ReadData
|
|||
merged.add(localResponses.get(0));
|
||||
localResponses = merged;
|
||||
}
|
||||
// TODO (required): this may be safe, but it is surprising and we should avoid it (e.g. by using immutable structure like btree)
|
||||
localResponses.addAll(other.localResponses);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Data without(Ranges ranges)
|
||||
{
|
||||
List<Pair<TokenKey, ReadResponse>> copy = new ArrayList<>(localResponses.size());
|
||||
for (Pair<TokenKey, ReadResponse> r : localResponses)
|
||||
{
|
||||
if (!ranges.contains(r.left))
|
||||
copy.add(r);
|
||||
}
|
||||
if (copy.size() == localResponses.size())
|
||||
return this;
|
||||
return new LocalReadData(localResponses, readCommand);
|
||||
}
|
||||
|
||||
private void ensureRemoteResponse()
|
||||
{
|
||||
if (remoteResponse != null)
|
||||
|
|
@ -225,13 +253,13 @@ public class AccordInteropRead extends ReadData
|
|||
|
||||
public AccordInteropRead(Node.Id to, Topologies topologies, TxnId txnId, Participants<?> scope, long executeAtEpoch, ReadCommand command)
|
||||
{
|
||||
super(to, topologies, txnId, scope, executeAtEpoch);
|
||||
super(to, topologies, txnId, scope, null, null, executeAtEpoch);
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public AccordInteropRead(TxnId txnId, Participants<?> scope, long executeAtEpoch, ReadCommand command)
|
||||
{
|
||||
super(txnId, scope, executeAtEpoch);
|
||||
super(txnId, scope, null, null, executeAtEpoch);
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
|
|
@ -251,7 +279,7 @@ public class AccordInteropRead extends ReadData
|
|||
SinglePartitionReadCommand readCommand = ((SinglePartitionReadCommand)command);
|
||||
TokenKey key = new TokenKey(readCommand.metadata().id, readCommand.partitionKey().getToken());
|
||||
if (!execute.contains(key))
|
||||
return AsyncChains.success(null);
|
||||
return AsyncChains.success(new LocalReadData(new ArrayList<>(), readCommand));
|
||||
|
||||
ReadCommand submit = readCommand.withTransactionalSettings(TxnNamedRead.readsWithoutReconciliation(txnRead.cassandraConsistencyLevel()), nowInSeconds);
|
||||
return AsyncChains.ofCallable(Stage.READ.executor(), () -> new LocalReadData(key, ReadCommandVerbHandler.instance.doRead(submit, false), command));
|
||||
|
|
@ -273,7 +301,7 @@ public class AccordInteropRead extends ReadData
|
|||
}
|
||||
|
||||
if (chains.isEmpty())
|
||||
return AsyncChains.success(null);
|
||||
return AsyncChains.success(new LocalReadData(new ArrayList<>(), command));
|
||||
|
||||
return AsyncChains.reduce(chains, Data::merge);
|
||||
}
|
||||
|
|
@ -285,9 +313,9 @@ public class AccordInteropRead extends ReadData
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ReadOk constructReadOk(Ranges unavailable, Data data, long uniqueHlc)
|
||||
protected void reply(Ranges unavailable, Data data, long uniqueHlc)
|
||||
{
|
||||
return new InteropReadOk(unavailable, data, uniqueHlc);
|
||||
reply(new InteropReadOk(unavailable, data, uniqueHlc), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -49,8 +49,7 @@ import org.apache.cassandra.service.accord.AccordMessageSink.AccordMessageType;
|
|||
import org.apache.cassandra.service.accord.serializers.CommandSerializers;
|
||||
import org.apache.cassandra.service.accord.serializers.IVersionedSerializer;
|
||||
import org.apache.cassandra.service.accord.serializers.KeySerializers;
|
||||
import org.apache.cassandra.service.accord.serializers.ReadDataSerializers;
|
||||
import org.apache.cassandra.service.accord.serializers.ReadDataSerializers.ReadDataSerializer;
|
||||
import org.apache.cassandra.service.accord.serializers.ReadDataSerializer;
|
||||
import org.apache.cassandra.service.accord.serializers.Version;
|
||||
|
||||
/**
|
||||
|
|
@ -60,7 +59,7 @@ import org.apache.cassandra.service.accord.serializers.Version;
|
|||
*/
|
||||
public class AccordInteropReadRepair extends ReadData
|
||||
{
|
||||
public static final IVersionedSerializer<AccordInteropReadRepair> requestSerializer = new ReadDataSerializer<AccordInteropReadRepair>()
|
||||
public static final IVersionedSerializer<AccordInteropReadRepair> requestSerializer = new IVersionedSerializer<>()
|
||||
{
|
||||
@Override
|
||||
public void serialize(AccordInteropReadRepair repair, DataOutputPlus out, Version version) throws IOException
|
||||
|
|
@ -119,17 +118,17 @@ public class AccordInteropReadRepair extends ReadData
|
|||
public long serializedSize(Data t, Version version) { return 0; }
|
||||
};
|
||||
|
||||
public static final IVersionedSerializer<ReadReply> replySerializer = new ReadDataSerializers.ReplySerializer<>(noop_data_serializer);
|
||||
public static final IVersionedSerializer<ReadReply> replySerializer = new ReadDataSerializer.ReplySerializer<>(noop_data_serializer);
|
||||
|
||||
public AccordInteropReadRepair(Node.Id to, Topologies topologies, TxnId txnId, Participants<?> scope, long executeAtEpoch, Mutation mutation)
|
||||
{
|
||||
super(to, topologies, txnId, scope, executeAtEpoch);
|
||||
super(to, topologies, txnId, scope, null, null, executeAtEpoch);
|
||||
this.mutation = mutation;
|
||||
}
|
||||
|
||||
public AccordInteropReadRepair(TxnId txnId, Participants<?> scope, long executeAtEpoch, Mutation mutation)
|
||||
{
|
||||
super(txnId, scope, executeAtEpoch);
|
||||
super(txnId, scope, null, null, executeAtEpoch);
|
||||
this.mutation = mutation;
|
||||
}
|
||||
|
||||
|
|
@ -156,9 +155,9 @@ public class AccordInteropReadRepair extends ReadData
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ReadOk constructReadOk(Ranges unavailable, Data data, long uniqueHlc)
|
||||
protected void reply(Ranges unavailable, Data data, long uniqueHlc)
|
||||
{
|
||||
return new InteropReadRepairOk(unavailable, data, uniqueHlc);
|
||||
reply(new InteropReadRepairOk(unavailable, data, uniqueHlc), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ import org.apache.cassandra.service.accord.serializers.CommitSerializers;
|
|||
import org.apache.cassandra.service.accord.serializers.DepsSerializers;
|
||||
import org.apache.cassandra.service.accord.serializers.IVersionedSerializer;
|
||||
import org.apache.cassandra.service.accord.serializers.KeySerializers;
|
||||
import org.apache.cassandra.service.accord.serializers.ReadDataSerializers.ReadDataSerializer;
|
||||
import org.apache.cassandra.service.accord.serializers.Version;
|
||||
|
||||
import static accord.messages.Commit.WithDeps.HasDeps;
|
||||
|
|
@ -63,7 +62,7 @@ import static accord.primitives.SaveStatus.ReadyToExecute;
|
|||
public class AccordInteropStableThenRead extends AccordInteropRead
|
||||
{
|
||||
// TODO (desired): duplicates a lot of StableThenReadSerializer
|
||||
public static final IVersionedSerializer<AccordInteropStableThenRead> requestSerializer = new ReadDataSerializer<>()
|
||||
public static final IVersionedSerializer<AccordInteropStableThenRead> requestSerializer = new IVersionedSerializer<>()
|
||||
{
|
||||
@Override
|
||||
public void serialize(AccordInteropStableThenRead read, DataOutputPlus out, Version version) throws IOException
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ package org.apache.cassandra.service.accord.serializers;
|
|||
import java.io.IOException;
|
||||
|
||||
import accord.api.Result;
|
||||
import accord.coordinate.ExecuteFlag.ExecuteFlags;
|
||||
import accord.messages.Apply;
|
||||
import accord.primitives.Ballot;
|
||||
import accord.primitives.FullRoute;
|
||||
|
|
@ -31,6 +32,7 @@ import accord.primitives.Timestamp;
|
|||
import accord.primitives.TxnId;
|
||||
import accord.primitives.Writes;
|
||||
import accord.utils.Invariants;
|
||||
import accord.utils.VIntCoding;
|
||||
import org.apache.cassandra.db.TypeSizes;
|
||||
import org.apache.cassandra.io.UnversionedSerializer;
|
||||
import org.apache.cassandra.io.util.DataInputPlus;
|
||||
|
|
@ -76,10 +78,11 @@ public class ApplySerializers
|
|||
KeySerializers.nullableFullRoute.serialize(apply.fullRoute, out);
|
||||
if (apply.txnId.is(Write))
|
||||
CommandSerializers.writes.serialize(apply.writes, out, version);
|
||||
out.writeUnsignedVInt32(apply.flags.bits());
|
||||
}
|
||||
|
||||
protected abstract A deserializeApply(TxnId txnId, Ballot ballot, Route<?> scope, long minEpoch, long waitForEpoch, long maxEpoch, Apply.Kind kind,
|
||||
Timestamp executeAt, PartialDeps deps, PartialTxn txn, FullRoute<?> fullRoute, Writes writes, Result result);
|
||||
Timestamp executeAt, PartialDeps deps, PartialTxn txn, FullRoute<?> fullRoute, Writes writes, Result result, ExecuteFlags flags);
|
||||
|
||||
@Override
|
||||
public A deserializeBody(DataInputPlus in, Version version, TxnId txnId, Route<?> scope, long waitForEpoch) throws IOException
|
||||
|
|
@ -94,13 +97,14 @@ public class ApplySerializers
|
|||
CommandSerializers.nullablePartialTxn.deserialize(in, version),
|
||||
KeySerializers.nullableFullRoute.deserialize(in),
|
||||
(txnId.is(Write) ? CommandSerializers.writes.deserialize(in, version) : null),
|
||||
ResultSerializers.APPLIED);
|
||||
ResultSerializers.APPLIED,
|
||||
ExecuteFlags.get(in.readUnsignedVInt32()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long serializedBodySize(A apply, Version version)
|
||||
{
|
||||
return CommandSerializers.ballot.serializedSize(apply.ballot)
|
||||
return CommandSerializers.ballot.serializedSize(apply.ballot)
|
||||
+ TypeSizes.sizeofVInt(apply.minEpoch - apply.waitForEpoch)
|
||||
+ TypeSizes.sizeofUnsignedVInt(apply.maxEpoch - apply.minEpoch)
|
||||
+ kind.serializedSize(apply.kind)
|
||||
|
|
@ -108,7 +112,9 @@ public class ApplySerializers
|
|||
+ DepsSerializers.partialDeps.serializedSize(apply.deps)
|
||||
+ CommandSerializers.nullablePartialTxn.serializedSize(apply.txn, version)
|
||||
+ KeySerializers.nullableFullRoute.serializedSize(apply.fullRoute)
|
||||
+ (apply.txnId.is(Write) ? CommandSerializers.writes.serializedSize(apply.writes, version) : 0);
|
||||
+ (apply.txnId.is(Write) ? CommandSerializers.writes.serializedSize(apply.writes, version) : 0)
|
||||
+ VIntCoding.sizeOfUnsignedVInt(apply.flags.bits())
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -116,9 +122,9 @@ public class ApplySerializers
|
|||
{
|
||||
@Override
|
||||
protected Apply deserializeApply(TxnId txnId, Ballot ballot, Route<?> scope, long minEpoch, long waitForEpoch, long maxEpoch, Apply.Kind kind,
|
||||
Timestamp executeAt, PartialDeps deps, PartialTxn txn, FullRoute<?> fullRoute, Writes writes, Result result)
|
||||
Timestamp executeAt, PartialDeps deps, PartialTxn txn, FullRoute<?> fullRoute, Writes writes, Result result, ExecuteFlags flags)
|
||||
{
|
||||
return Apply.SerializationSupport.create(txnId, ballot, scope, minEpoch, waitForEpoch, maxEpoch, kind, executeAt, deps, txn, fullRoute, writes, result);
|
||||
return Apply.SerializationSupport.create(txnId, ballot, scope, minEpoch, waitForEpoch, maxEpoch, kind, executeAt, deps, txn, fullRoute, writes, result, flags);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,360 @@
|
|||
/*
|
||||
* 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.serializers;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import accord.api.Data;
|
||||
import accord.coordinate.ExecuteFlag.ExecuteFlags;
|
||||
import accord.messages.ApplyThenWaitUntilApplied;
|
||||
import accord.messages.Commit;
|
||||
import accord.messages.ReadData;
|
||||
import accord.messages.ReadData.CommitOrReadNack;
|
||||
import accord.messages.ReadData.ReadOk;
|
||||
import accord.messages.ReadData.ReadOkWithFutureEpoch;
|
||||
import accord.messages.ReadData.ReadReply;
|
||||
import accord.messages.ReadData.ReadType;
|
||||
import accord.messages.ReadEphemeralTxnData;
|
||||
import accord.messages.ReadTxnData;
|
||||
import accord.messages.StableThenRead;
|
||||
import accord.messages.WaitUntilApplied;
|
||||
import accord.primitives.FullRoute;
|
||||
import accord.primitives.PartialDeps;
|
||||
import accord.primitives.PartialTxn;
|
||||
import accord.primitives.Participants;
|
||||
import accord.primitives.Ranges;
|
||||
import accord.primitives.Timestamp;
|
||||
import accord.primitives.TxnId;
|
||||
import accord.primitives.Writes;
|
||||
import accord.utils.UnhandledEnum;
|
||||
import org.apache.cassandra.db.TypeSizes;
|
||||
import org.apache.cassandra.io.VersionedSerializer;
|
||||
import org.apache.cassandra.io.util.DataInputPlus;
|
||||
import org.apache.cassandra.io.util.DataOutputPlus;
|
||||
import org.apache.cassandra.service.accord.serializers.CommandSerializers.ExecuteAtSerializer;
|
||||
import org.apache.cassandra.service.accord.txn.TxnData;
|
||||
import org.apache.cassandra.utils.vint.VIntCoding;
|
||||
|
||||
import static org.apache.cassandra.utils.NullableSerializer.deserializeNullable;
|
||||
import static org.apache.cassandra.utils.NullableSerializer.serializeNullable;
|
||||
import static org.apache.cassandra.utils.NullableSerializer.serializedNullableSize;
|
||||
|
||||
public class ReadDataSerializer implements IVersionedSerializer<ReadData>
|
||||
{
|
||||
public static final ReadDataSerializer request = new ReadDataSerializer();
|
||||
|
||||
private static final Commit.Kind[] KINDS = Commit.Kind.values();
|
||||
private static final ReadType[] TYPES = ReadType.values();
|
||||
private static final int HAS_TXN = 0x20;
|
||||
private static final int HAS_EXECUTE_AT = 0x40;
|
||||
private static final int HAS_EXECUTE_AT_EPOCH = 0x80;
|
||||
|
||||
// ATWUA = ApplyThenWaitUntilApplied
|
||||
private static final int ATWUA_HAS_MIN_EPOCH = 0x1;
|
||||
private static final int ATWUA_HAS_WRITES = 0x2;
|
||||
|
||||
// STR = StableThenRead
|
||||
private static final int STR_HAS_DEPS = 0x1;
|
||||
private static final int STR_HAS_FULL_ROUTE = 0x2;
|
||||
|
||||
@Override
|
||||
public void serialize(ReadData read, DataOutputPlus out, Version version) throws IOException
|
||||
{
|
||||
ReadType type = read.kind();
|
||||
boolean hasTxn = read.partialTxn() != null;
|
||||
boolean hasExecuteAt = read.executeAt() != null;
|
||||
boolean hasExecuteAtEpoch = (read.executeAt() != null ? read.executeAt() : read.txnId).epoch() != read.executeAtEpoch;
|
||||
|
||||
out.writeUnsignedVInt32(type.ordinal()
|
||||
| (read.flags.bits() << 3)
|
||||
| (hasTxn ? HAS_TXN : 0)
|
||||
| (hasExecuteAt ? HAS_EXECUTE_AT : 0)
|
||||
| (hasExecuteAtEpoch ? HAS_EXECUTE_AT_EPOCH : 0)
|
||||
);
|
||||
CommandSerializers.txnId.serialize(read.txnId, out);
|
||||
KeySerializers.participants.serialize(read.scope, out);
|
||||
if (hasTxn)
|
||||
CommandSerializers.partialTxn.serialize(read.partialTxn(), out, version);
|
||||
if (hasExecuteAt)
|
||||
ExecuteAtSerializer.serialize(read.txnId, read.executeAt(), out);
|
||||
if (hasExecuteAtEpoch)
|
||||
out.writeVInt(read.executeAtEpoch - (read.executeAt() != null ? read.executeAt() : read.txnId).epoch());
|
||||
|
||||
switch (type)
|
||||
{
|
||||
default: throw new UnhandledEnum(type);
|
||||
case readTxnData: break;
|
||||
case waitUntilApplied:
|
||||
{
|
||||
out.writeVInt(read.txnId.epoch() - ((WaitUntilApplied)read).minEpoch());
|
||||
break;
|
||||
}
|
||||
case readEphemeral:
|
||||
{
|
||||
ReadEphemeralTxnData re = (ReadEphemeralTxnData) read;
|
||||
DepsSerializers.partialDeps.serialize(re.partialDeps(), out);
|
||||
KeySerializers.fullRoute.serialize(re.route(), out);
|
||||
break;
|
||||
}
|
||||
case stableThenRead:
|
||||
{
|
||||
StableThenRead str = (StableThenRead) read;
|
||||
out.writeUnsignedVInt32(str.kind.ordinal());
|
||||
out.writeVInt(read.txnId.epoch() - str.minEpoch);
|
||||
boolean hasDeps = str.partialDeps() != null;
|
||||
boolean hasFullRoute = str.route != null;
|
||||
out.writeUnsignedVInt32((hasDeps ? STR_HAS_DEPS : 0)
|
||||
| (hasFullRoute? STR_HAS_FULL_ROUTE : 0));
|
||||
if (hasDeps)
|
||||
DepsSerializers.partialDeps.serialize(str.partialDeps(), out);
|
||||
if (hasFullRoute)
|
||||
KeySerializers.fullRoute.serialize(str.route, out);
|
||||
break;
|
||||
}
|
||||
case applyThenWaitUntilApplied:
|
||||
{
|
||||
ApplyThenWaitUntilApplied msg = (ApplyThenWaitUntilApplied) read;
|
||||
boolean hasMinEpoch = msg.minEpoch() != read.txnId.epoch();
|
||||
boolean hasWrites = msg.writes != null;
|
||||
out.writeUnsignedVInt32((hasMinEpoch ? ATWUA_HAS_MIN_EPOCH : 0)
|
||||
| (hasWrites ? ATWUA_HAS_WRITES : 0));
|
||||
if (hasMinEpoch)
|
||||
out.writeVInt(read.txnId.epoch() - msg.minEpoch());
|
||||
DepsSerializers.partialDeps.serialize(msg.deps, out);
|
||||
KeySerializers.fullRoute.serialize(msg.route, out);
|
||||
if (hasWrites)
|
||||
CommandSerializers.writes.serialize(msg.writes, out, version);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadData deserialize(DataInputPlus in, Version version) throws IOException
|
||||
{
|
||||
ReadType type;
|
||||
ExecuteFlags flags;
|
||||
boolean hasTxn, hasExecuteAt, hasExecuteAtEpoch;
|
||||
{
|
||||
int tmp = in.readUnsignedVInt32();
|
||||
type = TYPES[tmp & 0x7];
|
||||
flags = ExecuteFlags.get((tmp >>> 3) & 0x3);
|
||||
hasTxn = (tmp & HAS_TXN) != 0;
|
||||
hasExecuteAt = (tmp & HAS_EXECUTE_AT) != 0;
|
||||
hasExecuteAtEpoch = (tmp & HAS_EXECUTE_AT_EPOCH) != 0;
|
||||
}
|
||||
|
||||
TxnId txnId = CommandSerializers.txnId.deserialize(in);
|
||||
Participants<?> scope = KeySerializers.participants.deserialize(in);
|
||||
PartialTxn txn = hasTxn ? CommandSerializers.partialTxn.deserialize(in, version) : null;
|
||||
Timestamp executeAt = hasExecuteAt ? ExecuteAtSerializer.deserialize(txnId, in) : null;
|
||||
long executeAtEpoch = (hasExecuteAt ? executeAt : txnId).epoch();
|
||||
if (hasExecuteAtEpoch)
|
||||
executeAtEpoch += in.readVInt();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
default: throw new UnhandledEnum(type);
|
||||
case readTxnData:
|
||||
{
|
||||
return ReadTxnData.SerializerSupport.create(txnId, scope, txn, executeAt, executeAtEpoch, flags);
|
||||
}
|
||||
case waitUntilApplied:
|
||||
{
|
||||
long minEpoch = txnId.epoch() - in.readVInt();
|
||||
return WaitUntilApplied.SerializerSupport.create(txnId, scope, minEpoch, executeAtEpoch);
|
||||
}
|
||||
case readEphemeral:
|
||||
{
|
||||
PartialDeps deps = DepsSerializers.partialDeps.deserialize(in);
|
||||
FullRoute<?> route = KeySerializers.fullRoute.deserialize(in);
|
||||
return ReadEphemeralTxnData.SerializerSupport.create(txnId, scope, txn, deps, route, flags);
|
||||
}
|
||||
case stableThenRead:
|
||||
{
|
||||
Commit.Kind kind = KINDS[in.readUnsignedVInt32()];
|
||||
long minEpoch = txnId.epoch() - in.readVInt();
|
||||
boolean hasDeps, hasFullRoute;
|
||||
{
|
||||
int extraFlags = in.readUnsignedVInt32();
|
||||
hasDeps = (extraFlags & STR_HAS_DEPS) != 0;
|
||||
hasFullRoute = (extraFlags & STR_HAS_FULL_ROUTE) != 0;
|
||||
}
|
||||
PartialDeps deps = hasDeps ? DepsSerializers.partialDeps.deserialize(in) : null;
|
||||
FullRoute<?> route = hasFullRoute ? KeySerializers.fullRoute.deserialize(in) : null;
|
||||
return StableThenRead.SerializerSupport.create(txnId, scope, kind, minEpoch, executeAt, txn, deps, route);
|
||||
}
|
||||
case applyThenWaitUntilApplied:
|
||||
{
|
||||
boolean hasMinEpoch, hasWrites;
|
||||
{
|
||||
int extraFlags = in.readUnsignedVInt32();
|
||||
hasMinEpoch = (extraFlags & ATWUA_HAS_MIN_EPOCH) != 0;
|
||||
hasWrites = (extraFlags & ATWUA_HAS_WRITES) != 0;
|
||||
}
|
||||
long minEpoch = txnId.epoch();
|
||||
if (hasMinEpoch)
|
||||
minEpoch = txnId.epoch() - in.readVInt();
|
||||
PartialDeps deps = DepsSerializers.partialDeps.deserialize(in);
|
||||
FullRoute<?> route = KeySerializers.fullRoute.deserialize(in);
|
||||
Writes writes = hasWrites ? CommandSerializers.writes.deserialize(in, version) : null;
|
||||
return ApplyThenWaitUntilApplied.SerializerSupport.create(txnId, scope, minEpoch, executeAt, route, txn, deps, writes, ResultSerializers.APPLIED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long serializedSize(ReadData read, Version version)
|
||||
{
|
||||
ReadType type = read.kind();
|
||||
boolean hasTxn = read.partialTxn() != null;
|
||||
boolean hasExecuteAt = read.executeAt() != null;
|
||||
boolean hasExecuteAtEpoch = (read.executeAt() != null ? read.executeAt() : read.txnId).epoch() != read.executeAtEpoch;
|
||||
|
||||
long size = VIntCoding.computeUnsignedVIntSize(type.ordinal()
|
||||
| (read.flags.bits() << 3)
|
||||
| (hasTxn ? HAS_TXN : 0)
|
||||
| (hasExecuteAt ? HAS_EXECUTE_AT : 0)
|
||||
| (hasExecuteAtEpoch ? HAS_EXECUTE_AT_EPOCH : 0))
|
||||
+ CommandSerializers.txnId.serializedSize(read.txnId)
|
||||
+ KeySerializers.participants.serializedSize(read.scope);
|
||||
if (hasTxn)
|
||||
size += CommandSerializers.partialTxn.serializedSize(read.partialTxn(), version);
|
||||
if (hasExecuteAt)
|
||||
size += ExecuteAtSerializer.serializedSize(read.txnId, read.executeAt());
|
||||
if (hasExecuteAtEpoch)
|
||||
size += VIntCoding.computeVIntSize(read.executeAtEpoch - (read.executeAt() != null ? read.executeAt() : read.txnId).epoch());
|
||||
|
||||
switch (type)
|
||||
{
|
||||
default: throw new UnhandledEnum(type);
|
||||
case readTxnData: break;
|
||||
case waitUntilApplied:
|
||||
{
|
||||
size += VIntCoding.computeVIntSize(read.txnId.epoch() - ((WaitUntilApplied)read).minEpoch());
|
||||
break;
|
||||
}
|
||||
case readEphemeral:
|
||||
{
|
||||
ReadEphemeralTxnData re = (ReadEphemeralTxnData) read;
|
||||
size += DepsSerializers.partialDeps.serializedSize(re.partialDeps());
|
||||
size += KeySerializers.fullRoute.serializedSize(re.route());
|
||||
break;
|
||||
}
|
||||
case stableThenRead:
|
||||
{
|
||||
StableThenRead str = (StableThenRead) read;
|
||||
size += VIntCoding.computeUnsignedVIntSize(str.kind.ordinal());
|
||||
size += VIntCoding.computeVIntSize(read.txnId.epoch() - str.minEpoch);
|
||||
boolean hasDeps = str.partialDeps() != null;
|
||||
boolean hasFullRoute = str.route != null;
|
||||
size += VIntCoding.computeUnsignedVIntSize((hasDeps ? STR_HAS_DEPS : 0)
|
||||
| (hasFullRoute? STR_HAS_FULL_ROUTE : 0));
|
||||
if (hasDeps)
|
||||
size += DepsSerializers.partialDeps.serializedSize(str.partialDeps());
|
||||
if (hasFullRoute)
|
||||
size += KeySerializers.fullRoute.serializedSize(str.route);
|
||||
break;
|
||||
}
|
||||
case applyThenWaitUntilApplied:
|
||||
{
|
||||
ApplyThenWaitUntilApplied msg = (ApplyThenWaitUntilApplied) read;
|
||||
boolean hasMinEpoch = msg.minEpoch() != read.txnId.epoch();
|
||||
boolean hasWrites = msg.writes != null;
|
||||
size += VIntCoding.computeUnsignedVIntSize((hasMinEpoch ? ATWUA_HAS_MIN_EPOCH : 0)
|
||||
| (hasWrites ? ATWUA_HAS_WRITES : 0));
|
||||
if (hasMinEpoch)
|
||||
size += VIntCoding.computeVIntSize(read.txnId.epoch() - msg.minEpoch());
|
||||
size += DepsSerializers.partialDeps.serializedSize(msg.deps);
|
||||
size += KeySerializers.fullRoute.serializedSize(msg.route);
|
||||
if (hasWrites)
|
||||
size += CommandSerializers.writes.serializedSize(msg.writes, version);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
public static final class ReplySerializer<D extends Data> implements IVersionedSerializer<ReadReply>
|
||||
{
|
||||
final CommitOrReadNack[] nacks = CommitOrReadNack.values();
|
||||
private final VersionedSerializer<D, Version> dataSerializer;
|
||||
|
||||
public ReplySerializer(VersionedSerializer<D, Version> dataSerializer)
|
||||
{
|
||||
this.dataSerializer = dataSerializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(ReadReply reply, DataOutputPlus out, Version version) throws IOException
|
||||
{
|
||||
if (!reply.isOk())
|
||||
{
|
||||
out.writeByte(3 + ((CommitOrReadNack) reply).ordinal());
|
||||
return;
|
||||
}
|
||||
|
||||
ReadOk readOk = (ReadOk) reply;
|
||||
int flags = readOk.getClass() == ReadOkWithFutureEpoch.class ? 2 : readOk.uniqueHlc != 0 ? 1 : 0;
|
||||
out.writeByte(flags);
|
||||
serializeNullable(readOk.unavailable, out, KeySerializers.ranges);
|
||||
dataSerializer.serialize((D) readOk.data, out, version);
|
||||
switch (flags)
|
||||
{
|
||||
case 2: out.writeUnsignedVInt(((ReadOkWithFutureEpoch) reply).futureEpoch); break;
|
||||
case 1: out.writeUnsignedVInt(readOk.uniqueHlc);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadReply deserialize(DataInputPlus in, Version version) throws IOException
|
||||
{
|
||||
int flags = in.readByte();
|
||||
if (flags > 2)
|
||||
return nacks[flags - 3];
|
||||
|
||||
Ranges unavailable = deserializeNullable(in, KeySerializers.ranges);
|
||||
D data = dataSerializer.deserialize(in, version);
|
||||
|
||||
long extraLong = flags == 0 ? 0 : in.readUnsignedVInt();
|
||||
if (flags <= 1)
|
||||
return new ReadOk(unavailable, data, extraLong);
|
||||
return new ReadOkWithFutureEpoch(unavailable, data, extraLong);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long serializedSize(ReadReply reply, Version version)
|
||||
{
|
||||
if (!reply.isOk())
|
||||
return TypeSizes.BYTE_SIZE;
|
||||
|
||||
ReadOk readOk = (ReadOk) reply;
|
||||
long size = TypeSizes.BYTE_SIZE
|
||||
+ serializedNullableSize(readOk.unavailable, KeySerializers.ranges)
|
||||
+ dataSerializer.serializedSize((D) readOk.data, version);
|
||||
if (readOk.uniqueHlc != 0)
|
||||
size += TypeSizes.sizeofUnsignedVInt(readOk.uniqueHlc);
|
||||
else if (readOk instanceof ReadOkWithFutureEpoch)
|
||||
size += TypeSizes.sizeofUnsignedVInt(((ReadOkWithFutureEpoch) readOk).futureEpoch);
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
public static final IVersionedSerializer<ReadReply> reply = new ReplySerializer<>(TxnData.nullableSerializer);
|
||||
}
|
||||
|
|
@ -1,369 +0,0 @@
|
|||
/*
|
||||
* 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.serializers;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import accord.api.Data;
|
||||
import accord.messages.ApplyThenWaitUntilApplied;
|
||||
import accord.messages.Commit;
|
||||
import accord.messages.ReadData;
|
||||
import accord.messages.ReadData.CommitOrReadNack;
|
||||
import accord.messages.ReadData.ReadOk;
|
||||
import accord.messages.ReadData.ReadOkWithFutureEpoch;
|
||||
import accord.messages.ReadData.ReadReply;
|
||||
import accord.messages.ReadData.ReadType;
|
||||
import accord.messages.ReadEphemeralTxnData;
|
||||
import accord.messages.ReadTxnData;
|
||||
import accord.messages.StableThenRead;
|
||||
import accord.messages.WaitUntilApplied;
|
||||
import accord.primitives.FullRoute;
|
||||
import accord.primitives.PartialDeps;
|
||||
import accord.primitives.PartialTxn;
|
||||
import accord.primitives.Participants;
|
||||
import accord.primitives.Ranges;
|
||||
import accord.primitives.Timestamp;
|
||||
import accord.primitives.TxnId;
|
||||
import org.apache.cassandra.db.TypeSizes;
|
||||
import org.apache.cassandra.io.VersionedSerializer;
|
||||
import org.apache.cassandra.io.util.DataInputPlus;
|
||||
import org.apache.cassandra.io.util.DataOutputPlus;
|
||||
import org.apache.cassandra.service.accord.serializers.CommandSerializers.ExecuteAtSerializer;
|
||||
import org.apache.cassandra.service.accord.txn.TxnData;
|
||||
|
||||
import static accord.messages.Commit.WithDeps.HasDeps;
|
||||
import static accord.messages.Commit.WithDeps.NoDeps;
|
||||
import static accord.messages.Commit.WithTxn.HasTxn;
|
||||
import static accord.messages.Commit.WithTxn.NoTxn;
|
||||
import static org.apache.cassandra.db.TypeSizes.sizeof;
|
||||
import static org.apache.cassandra.utils.NullableSerializer.deserializeNullable;
|
||||
import static org.apache.cassandra.utils.NullableSerializer.serializeNullable;
|
||||
import static org.apache.cassandra.utils.NullableSerializer.serializedNullableSize;
|
||||
|
||||
public class ReadDataSerializers
|
||||
{
|
||||
public static final IVersionedSerializer<ReadData> readData = new IVersionedSerializer<ReadData>()
|
||||
{
|
||||
@Override
|
||||
public void serialize(ReadData t, DataOutputPlus out, Version version) throws IOException
|
||||
{
|
||||
out.writeByte(t.kind().val);
|
||||
serializerFor(t).serialize(t, out, version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadData deserialize(DataInputPlus in, Version version) throws IOException
|
||||
{
|
||||
return serializerFor(ReadType.valueOf(in.readByte())).deserialize(in, version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long serializedSize(ReadData t, Version version)
|
||||
{
|
||||
return sizeof(t.kind().val) + serializerFor(t).serializedSize(t, version);
|
||||
}
|
||||
};
|
||||
|
||||
public static final ApplyThenWaitUntilAppliedSerializer applyThenWaitUntilApplied = new ApplyThenWaitUntilAppliedSerializer();
|
||||
|
||||
public static class ApplyThenWaitUntilAppliedSerializer implements ReadDataSerializer<ApplyThenWaitUntilApplied>
|
||||
{
|
||||
@Override
|
||||
public void serialize(ApplyThenWaitUntilApplied msg, DataOutputPlus out, Version version) throws IOException
|
||||
{
|
||||
CommandSerializers.txnId.serialize(msg.txnId, out);
|
||||
KeySerializers.participants.serialize(msg.scope, out);
|
||||
out.writeUnsignedVInt(msg.minEpoch());
|
||||
ExecuteAtSerializer.serialize(msg.txnId, msg.executeAt, out);
|
||||
KeySerializers.fullRoute.serialize(msg.route, out);
|
||||
CommandSerializers.partialTxn.serialize(msg.txn, out, version);
|
||||
DepsSerializers.partialDeps.serialize(msg.deps, out);
|
||||
CommandSerializers.nullableWrites.serialize(msg.writes, out, version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplyThenWaitUntilApplied deserialize(DataInputPlus in, Version version) throws IOException
|
||||
{
|
||||
TxnId txnId = CommandSerializers.txnId.deserialize(in);
|
||||
return ApplyThenWaitUntilApplied.SerializerSupport.create(
|
||||
txnId,
|
||||
KeySerializers.participants.deserialize(in),
|
||||
in.readUnsignedVInt(),
|
||||
ExecuteAtSerializer.deserialize(txnId, in),
|
||||
KeySerializers.fullRoute.deserialize(in),
|
||||
CommandSerializers.partialTxn.deserialize(in, version),
|
||||
DepsSerializers.partialDeps.deserialize(in),
|
||||
CommandSerializers.nullableWrites.deserialize(in, version),
|
||||
ResultSerializers.APPLIED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long serializedSize(ApplyThenWaitUntilApplied msg, Version version)
|
||||
{
|
||||
return CommandSerializers.txnId.serializedSize(msg.txnId)
|
||||
+ KeySerializers.participants.serializedSize(msg.scope)
|
||||
+ TypeSizes.sizeofUnsignedVInt(msg.minEpoch())
|
||||
+ ExecuteAtSerializer.serializedSize(msg.txnId, msg.executeAt)
|
||||
+ KeySerializers.fullRoute.serializedSize(msg.route)
|
||||
+ CommandSerializers.partialTxn.serializedSize(msg.txn, version)
|
||||
+ DepsSerializers.partialDeps.serializedSize(msg.deps)
|
||||
+ CommandSerializers.nullableWrites.serializedSize(msg.writes, version);
|
||||
}
|
||||
}
|
||||
|
||||
private static final ReadDataSerializer<ReadTxnData> readTxnData = new ReadDataSerializer<ReadTxnData>()
|
||||
{
|
||||
@Override
|
||||
public void serialize(ReadTxnData read, DataOutputPlus out, Version version) throws IOException
|
||||
{
|
||||
CommandSerializers.txnId.serialize(read.txnId, out);
|
||||
KeySerializers.participants.serialize(read.scope, out);
|
||||
out.writeUnsignedVInt(read.executeAtEpoch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadTxnData deserialize(DataInputPlus in, Version version) throws IOException
|
||||
{
|
||||
TxnId txnId = CommandSerializers.txnId.deserialize(in);
|
||||
Participants<?> scope = KeySerializers.participants.deserialize(in);
|
||||
long executeAtEpoch = in.readUnsignedVInt();
|
||||
return ReadTxnData.SerializerSupport.create(txnId, scope, executeAtEpoch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long serializedSize(ReadTxnData read, Version version)
|
||||
{
|
||||
return CommandSerializers.txnId.serializedSize(read.txnId)
|
||||
+ KeySerializers.participants.serializedSize(read.scope)
|
||||
+ TypeSizes.sizeofUnsignedVInt(read.executeAtEpoch);
|
||||
}
|
||||
};
|
||||
|
||||
public static final ReadDataSerializer<ReadEphemeralTxnData> readEphemeralTxnData = new ReadDataSerializer<>()
|
||||
{
|
||||
@Override
|
||||
public void serialize(ReadEphemeralTxnData read, DataOutputPlus out, Version version) throws IOException
|
||||
{
|
||||
CommandSerializers.txnId.serialize(read.txnId, out);
|
||||
KeySerializers.participants.serialize(read.scope, out);
|
||||
out.writeUnsignedVInt(read.executeAtEpoch);
|
||||
CommandSerializers.partialTxn.serialize(read.partialTxn(), out, version);
|
||||
DepsSerializers.partialDeps.serialize(read.partialDeps(), out);
|
||||
KeySerializers.fullRoute.serialize(read.route(), out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadEphemeralTxnData deserialize(DataInputPlus in, Version version) throws IOException
|
||||
{
|
||||
TxnId txnId = CommandSerializers.txnId.deserialize(in);
|
||||
Participants<?> scope = KeySerializers.participants.deserialize(in);
|
||||
long executeAtEpoch = in.readUnsignedVInt();
|
||||
PartialTxn partialTxn = CommandSerializers.partialTxn.deserialize(in, version);
|
||||
PartialDeps partialDeps = DepsSerializers.partialDeps.deserialize(in);
|
||||
FullRoute<?> route = KeySerializers.fullRoute.deserialize(in);
|
||||
return ReadEphemeralTxnData.SerializerSupport.create(txnId, scope, executeAtEpoch, partialTxn, partialDeps, route);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long serializedSize(ReadEphemeralTxnData read, Version version)
|
||||
{
|
||||
return CommandSerializers.txnId.serializedSize(read.txnId)
|
||||
+ KeySerializers.participants.serializedSize(read.scope)
|
||||
+ TypeSizes.sizeofUnsignedVInt(read.executeAtEpoch)
|
||||
+ CommandSerializers.partialTxn.serializedSize(read.partialTxn(), version)
|
||||
+ DepsSerializers.partialDeps.serializedSize(read.partialDeps())
|
||||
+ KeySerializers.fullRoute.serializedSize(read.route());
|
||||
}
|
||||
};
|
||||
|
||||
public interface ReadDataSerializer<T extends ReadData> extends IVersionedSerializer<T>
|
||||
{
|
||||
void serialize(T bound, DataOutputPlus out, Version version) throws IOException;
|
||||
T deserialize(DataInputPlus in, Version version) throws IOException;
|
||||
long serializedSize(T condition, Version version);
|
||||
}
|
||||
|
||||
private static ReadDataSerializer serializerFor(ReadData toSerialize)
|
||||
{
|
||||
return serializerFor(toSerialize.kind());
|
||||
}
|
||||
|
||||
private static ReadDataSerializer serializerFor(ReadType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case readTxnData:
|
||||
return readTxnData;
|
||||
case readDataWithoutTimestamp:
|
||||
return readEphemeralTxnData;
|
||||
case applyThenWaitUntilApplied:
|
||||
return applyThenWaitUntilApplied;
|
||||
case waitUntilApplied:
|
||||
return waitUntilApplied;
|
||||
default:
|
||||
throw new IllegalStateException("Unsupported ExecuteType " + type);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ReplySerializer<D extends Data> implements IVersionedSerializer<ReadReply>
|
||||
{
|
||||
final CommitOrReadNack[] nacks = CommitOrReadNack.values();
|
||||
private final VersionedSerializer<D, Version> dataSerializer;
|
||||
|
||||
public ReplySerializer(VersionedSerializer<D, Version> dataSerializer)
|
||||
{
|
||||
this.dataSerializer = dataSerializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(ReadReply reply, DataOutputPlus out, Version version) throws IOException
|
||||
{
|
||||
if (!reply.isOk())
|
||||
{
|
||||
out.writeByte(3 + ((CommitOrReadNack) reply).ordinal());
|
||||
return;
|
||||
}
|
||||
|
||||
ReadOk readOk = (ReadOk) reply;
|
||||
int flags = readOk.getClass() == ReadOkWithFutureEpoch.class ? 2 : readOk.uniqueHlc != 0 ? 1 : 0;
|
||||
out.writeByte(flags);
|
||||
serializeNullable(readOk.unavailable, out, KeySerializers.ranges);
|
||||
dataSerializer.serialize((D) readOk.data, out, version);
|
||||
switch (flags)
|
||||
{
|
||||
case 2: out.writeUnsignedVInt(((ReadOkWithFutureEpoch) reply).futureEpoch); break;
|
||||
case 1: out.writeUnsignedVInt(readOk.uniqueHlc);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadReply deserialize(DataInputPlus in, Version version) throws IOException
|
||||
{
|
||||
int flags = in.readByte();
|
||||
if (flags > 2)
|
||||
return nacks[flags - 3];
|
||||
|
||||
Ranges unavailable = deserializeNullable(in, KeySerializers.ranges);
|
||||
D data = dataSerializer.deserialize(in, version);
|
||||
|
||||
long extraLong = flags == 0 ? 0 : in.readUnsignedVInt();
|
||||
if (flags <= 1)
|
||||
return new ReadOk(unavailable, data, extraLong);
|
||||
return new ReadOkWithFutureEpoch(unavailable, data, extraLong);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long serializedSize(ReadReply reply, Version version)
|
||||
{
|
||||
if (!reply.isOk())
|
||||
return TypeSizes.BYTE_SIZE;
|
||||
|
||||
ReadOk readOk = (ReadOk) reply;
|
||||
long size = TypeSizes.BYTE_SIZE
|
||||
+ serializedNullableSize(readOk.unavailable, KeySerializers.ranges)
|
||||
+ dataSerializer.serializedSize((D) readOk.data, version);
|
||||
if (readOk.uniqueHlc != 0)
|
||||
size += TypeSizes.sizeofUnsignedVInt(readOk.uniqueHlc);
|
||||
else if (readOk instanceof ReadOkWithFutureEpoch)
|
||||
size += TypeSizes.sizeofUnsignedVInt(((ReadOkWithFutureEpoch) readOk).futureEpoch);
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
public static final IVersionedSerializer<ReadReply> reply = new ReplySerializer<>(TxnData.nullableSerializer);
|
||||
|
||||
// TODO (desired): duplicates ReadTxnData ser/de logic; conside deduplicating if another instance of this is added
|
||||
public static final ReadDataSerializer<WaitUntilApplied> waitUntilApplied = new ReadDataSerializer<WaitUntilApplied>()
|
||||
{
|
||||
@Override
|
||||
public void serialize(WaitUntilApplied waitUntilApplied, DataOutputPlus out, Version version) throws IOException
|
||||
{
|
||||
CommandSerializers.txnId.serialize(waitUntilApplied.txnId, out);
|
||||
KeySerializers.participants.serialize(waitUntilApplied.scope, out);
|
||||
out.writeUnsignedVInt(waitUntilApplied.minEpoch());
|
||||
out.writeUnsignedVInt(waitUntilApplied.executeAtEpoch - waitUntilApplied.minEpoch());
|
||||
}
|
||||
|
||||
@Override
|
||||
public WaitUntilApplied deserialize(DataInputPlus in, Version version) throws IOException
|
||||
{
|
||||
TxnId txnId = CommandSerializers.txnId.deserialize(in);
|
||||
Participants<?> scope = KeySerializers.participants.deserialize(in);
|
||||
long minEpoch = in.readUnsignedVInt();
|
||||
long executeAtEpoch = minEpoch + in.readUnsignedVInt();
|
||||
return WaitUntilApplied.SerializerSupport.create(txnId, scope, minEpoch, executeAtEpoch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long serializedSize(WaitUntilApplied waitUntilApplied, Version version)
|
||||
{
|
||||
return CommandSerializers.txnId.serializedSize(waitUntilApplied.txnId)
|
||||
+ KeySerializers.participants.serializedSize(waitUntilApplied.scope)
|
||||
+ TypeSizes.sizeofUnsignedVInt(waitUntilApplied.minEpoch())
|
||||
+ TypeSizes.sizeofUnsignedVInt(waitUntilApplied.executeAtEpoch - waitUntilApplied.minEpoch());
|
||||
}
|
||||
};
|
||||
|
||||
// TODO (desired): duplicates a lot of Commit serializer
|
||||
public static final ReadDataSerializer<StableThenRead> stableThenRead = new ReadDataSerializer<>()
|
||||
{
|
||||
@Override
|
||||
public void serialize(StableThenRead read, DataOutputPlus out, Version version) throws IOException
|
||||
{
|
||||
CommandSerializers.txnId.serialize(read.txnId, out);
|
||||
KeySerializers.participants.serialize(read.scope, out);
|
||||
CommitSerializers.kind.serialize(read.kind, out);
|
||||
out.writeUnsignedVInt(read.minEpoch);
|
||||
ExecuteAtSerializer.serialize(read.txnId, read.executeAt, out);
|
||||
if (read.kind.withTxn != NoTxn)
|
||||
CommandSerializers.nullablePartialTxn.serialize(read.partialTxn, out, version);
|
||||
if (read.kind.withDeps == HasDeps)
|
||||
DepsSerializers.partialDeps.serialize(read.partialDeps, out);
|
||||
if (read.kind.withTxn == HasTxn)
|
||||
KeySerializers.fullRoute.serialize(read.route, out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StableThenRead deserialize(DataInputPlus in, Version version) throws IOException
|
||||
{
|
||||
TxnId txnId = CommandSerializers.txnId.deserialize(in);
|
||||
Participants<?> scope = KeySerializers.participants.deserialize(in);
|
||||
Commit.Kind kind = CommitSerializers.kind.deserialize(in);
|
||||
long minEpoch = in.readUnsignedVInt();
|
||||
Timestamp executeAt = ExecuteAtSerializer.deserialize(txnId, in);
|
||||
PartialTxn partialTxn = kind.withTxn == NoTxn ? null : CommandSerializers.nullablePartialTxn.deserialize(in, version);
|
||||
PartialDeps partialDeps = kind.withDeps == NoDeps ? null : DepsSerializers.partialDeps.deserialize(in);
|
||||
FullRoute < ?> route = kind.withTxn == HasTxn ? KeySerializers.fullRoute.deserialize(in) : null;
|
||||
return StableThenRead.SerializerSupport.create(txnId, scope, kind, minEpoch, executeAt, partialTxn, partialDeps, route);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long serializedSize(StableThenRead read, Version version)
|
||||
{
|
||||
return CommandSerializers.txnId.serializedSize(read.txnId)
|
||||
+ KeySerializers.participants.serializedSize(read.scope)
|
||||
+ CommitSerializers.kind.serializedSize(read.kind)
|
||||
+ TypeSizes.sizeofUnsignedVInt(read.minEpoch)
|
||||
+ ExecuteAtSerializer.serializedSize(read.txnId, read.executeAt)
|
||||
+ (read.kind.withTxn == NoTxn ? 0 : CommandSerializers.nullablePartialTxn.serializedSize(read.partialTxn, version))
|
||||
+ (read.kind.withDeps != HasDeps ? 0 : DepsSerializers.partialDeps.serializedSize(read.partialDeps))
|
||||
+ (read.kind.withTxn != HasTxn ? 0 : KeySerializers.fullRoute.serializedSize(read.route));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -22,6 +22,9 @@ import java.io.IOException;
|
|||
import java.util.Map;
|
||||
|
||||
import accord.api.Data;
|
||||
import accord.primitives.Ranges;
|
||||
import accord.primitives.Timestamp;
|
||||
import accord.primitives.TxnId;
|
||||
import org.agrona.collections.Int2ObjectHashMap;
|
||||
import org.apache.cassandra.db.EmptyIterators;
|
||||
import org.apache.cassandra.db.SinglePartitionReadCommand;
|
||||
|
|
@ -131,14 +134,74 @@ public class TxnData extends Int2ObjectHashMap<TxnDataValue> implements TxnResul
|
|||
@Override
|
||||
public TxnData merge(Data data)
|
||||
{
|
||||
TxnData that = (TxnData) data;
|
||||
TxnData merged = new TxnData();
|
||||
this.forEach(merged::put);
|
||||
for (Map.Entry<Integer, TxnDataValue> e : that.entrySet())
|
||||
merged.merge(e.getKey(), e.getValue(), TxnDataValue::merge);
|
||||
return merge(this, (TxnData) data);
|
||||
}
|
||||
|
||||
private static TxnData merge(TxnData a, TxnData b)
|
||||
{
|
||||
if (a.size() < b.size()) { TxnData tmp = a; a = b; b = tmp; }
|
||||
|
||||
TxnData merged = null;
|
||||
int matches = 0;
|
||||
for (Map.Entry<Integer, TxnDataValue> e : a.entrySet())
|
||||
{
|
||||
Integer key = e.getKey();
|
||||
TxnDataValue av = e.getValue();
|
||||
TxnDataValue bv = b.get(key);
|
||||
if (bv != null || merged != null)
|
||||
{
|
||||
if (bv == null) merged.put(key, av);
|
||||
else
|
||||
{
|
||||
++matches;
|
||||
TxnDataValue upd = e.getValue().merge(bv);
|
||||
if (merged != null || upd != av)
|
||||
{
|
||||
if (merged == null) merged = new TxnData();
|
||||
merged.put(key, upd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matches == b.size())
|
||||
return merged == null ? a : merged;
|
||||
|
||||
if (merged == null)
|
||||
{
|
||||
merged = new TxnData();
|
||||
a.forEach(merged::put);
|
||||
}
|
||||
|
||||
b.forEach(merged::putIfAbsent);
|
||||
return merged;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Data without(Ranges ranges)
|
||||
{
|
||||
TxnData result = null;
|
||||
for (Map.Entry<Integer, TxnDataValue> e : entrySet())
|
||||
{
|
||||
TxnDataValue oldValue = e.getValue();
|
||||
TxnDataValue newValue = oldValue.without(ranges);
|
||||
if (oldValue == newValue)
|
||||
{
|
||||
if (result != null)
|
||||
result.put(e.getKey(), oldValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result == null)
|
||||
result = new TxnData();
|
||||
if (newValue != null)
|
||||
result.put(e.getKey(), newValue);
|
||||
}
|
||||
}
|
||||
|
||||
return result != null ? result : this;
|
||||
}
|
||||
|
||||
public static Data merge(Data left, Data right)
|
||||
{
|
||||
if (left == null)
|
||||
|
|
@ -149,6 +212,20 @@ public class TxnData extends Int2ObjectHashMap<TxnDataValue> implements TxnResul
|
|||
return left.merge(right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateReply(TxnId txnId, Timestamp executeAt, boolean futureReadPossible)
|
||||
{
|
||||
if (futureReadPossible)
|
||||
{
|
||||
for (TxnDataValue value : values())
|
||||
{
|
||||
if (value.maxTimestamp() >= executeAt.hlc())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long estimatedSizeOnHeap()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ package org.apache.cassandra.service.accord.txn;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import accord.primitives.Ranges;
|
||||
import accord.utils.Invariants;
|
||||
import org.apache.cassandra.db.partitions.FilteredPartition;
|
||||
import org.apache.cassandra.db.rows.Row;
|
||||
|
|
@ -32,6 +33,7 @@ import org.apache.cassandra.io.util.DataOutputPlus;
|
|||
import org.apache.cassandra.schema.Schema;
|
||||
import org.apache.cassandra.schema.TableId;
|
||||
import org.apache.cassandra.schema.TableMetadata;
|
||||
import org.apache.cassandra.service.accord.api.TokenKey;
|
||||
import org.apache.cassandra.service.accord.serializers.Version;
|
||||
|
||||
import static org.apache.cassandra.db.SerializationHeader.StableHeaderSerializer.STABLE;
|
||||
|
|
@ -57,6 +59,12 @@ public class TxnDataKeyValue extends FilteredPartition implements TxnDataValue
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TxnDataValue without(Ranges ranges)
|
||||
{
|
||||
return ranges.contains(new TokenKey(metadata().id, partitionKey().getToken())) ? this : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long estimatedSizeOnHeap()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import javax.annotation.Nullable;
|
|||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import accord.primitives.Ranges;
|
||||
import org.apache.cassandra.db.TypeSizes;
|
||||
import org.apache.cassandra.db.partitions.FilteredPartition;
|
||||
import org.apache.cassandra.db.partitions.PartitionIterator;
|
||||
|
|
@ -40,6 +41,8 @@ import org.apache.cassandra.io.util.DataOutputPlus;
|
|||
import org.apache.cassandra.schema.Schema;
|
||||
import org.apache.cassandra.schema.TableId;
|
||||
import org.apache.cassandra.schema.TableMetadata;
|
||||
import org.apache.cassandra.service.accord.TokenRange;
|
||||
import org.apache.cassandra.service.accord.api.TokenKey;
|
||||
import org.apache.cassandra.service.accord.serializers.Version;
|
||||
import org.apache.cassandra.utils.ObjectSizes;
|
||||
|
||||
|
|
@ -83,6 +86,34 @@ public class TxnDataRangeValue extends ArrayList<FilteredPartition> implements T
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TxnDataValue without(Ranges ranges)
|
||||
{
|
||||
TokenRange range = range();
|
||||
if (range == null || !ranges.intersects(range))
|
||||
return this;
|
||||
|
||||
if (ranges.contains(range))
|
||||
return null;
|
||||
|
||||
TxnDataRangeValue result = new TxnDataRangeValue();
|
||||
for (FilteredPartition p : this)
|
||||
{
|
||||
if (!ranges.contains(new TokenKey(p.metadata().id, p.partitionKey().getToken())))
|
||||
result.add(p);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long maxTimestamp()
|
||||
{
|
||||
long max = 0;
|
||||
for (FilteredPartition partition : this)
|
||||
max = Math.max(max, partition.maxTimestamp());
|
||||
return max;
|
||||
}
|
||||
|
||||
Supplier<PartitionIterator> toPartitionIterator(boolean reversed)
|
||||
{
|
||||
// Sorting isn't preserved when merging TxnDataRangeValues together so sort here
|
||||
|
|
@ -95,13 +126,25 @@ public class TxnDataRangeValue extends ArrayList<FilteredPartition> implements T
|
|||
sort(Comparator.comparing(FilteredPartition::partitionKey));
|
||||
}
|
||||
|
||||
private @Nullable TableId tableId()
|
||||
public @Nullable TableId tableId()
|
||||
{
|
||||
if (isEmpty())
|
||||
return null;
|
||||
return get(0).metadata().id;
|
||||
}
|
||||
|
||||
public @Nullable TokenRange range()
|
||||
{
|
||||
if (isEmpty())
|
||||
return null;
|
||||
FilteredPartition first = get(0);
|
||||
FilteredPartition last = get(size() - 1);
|
||||
return TokenRange.create(new TokenKey(first.metadata().id, first.partitionKey().getToken()),
|
||||
new TokenKey(last.metadata().id, last.partitionKey().getToken()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public long estimatedSizeOnHeap()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ package org.apache.cassandra.service.accord.txn;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import accord.primitives.Ranges;
|
||||
import org.apache.cassandra.io.util.DataInputPlus;
|
||||
import org.apache.cassandra.io.util.DataOutputPlus;
|
||||
import org.apache.cassandra.service.accord.serializers.IVersionedSerializer;
|
||||
|
|
@ -63,6 +66,8 @@ public interface TxnDataValue
|
|||
TxnDataValue.Kind kind();
|
||||
|
||||
TxnDataValue merge(TxnDataValue other);
|
||||
@Nullable TxnDataValue without(Ranges ranges);
|
||||
long maxTimestamp();
|
||||
|
||||
long estimatedSizeOnHeap();
|
||||
|
||||
|
|
|
|||
|
|
@ -35,10 +35,7 @@ import accord.primitives.Seekable;
|
|||
import accord.primitives.Timestamp;
|
||||
import accord.utils.Invariants;
|
||||
import accord.utils.async.AsyncChain;
|
||||
import accord.utils.async.AsyncChains;
|
||||
import accord.utils.async.AsyncResults;
|
||||
import org.apache.cassandra.concurrent.DebuggableTask;
|
||||
import org.apache.cassandra.concurrent.Stage;
|
||||
import org.apache.cassandra.db.ConsistencyLevel;
|
||||
import org.apache.cassandra.db.PartitionPosition;
|
||||
import org.apache.cassandra.db.PartitionRangeReadCommand;
|
||||
|
|
@ -60,6 +57,7 @@ import org.apache.cassandra.io.util.DataInputPlus;
|
|||
import org.apache.cassandra.io.util.DataOutputBuffer;
|
||||
import org.apache.cassandra.io.util.DataOutputPlus;
|
||||
import org.apache.cassandra.schema.TableId;
|
||||
import org.apache.cassandra.service.accord.AccordExecutor;
|
||||
import org.apache.cassandra.service.accord.TokenRange;
|
||||
import org.apache.cassandra.service.accord.api.TokenKey;
|
||||
import org.apache.cassandra.service.accord.api.PartitionKey;
|
||||
|
|
@ -69,7 +67,6 @@ import org.apache.cassandra.service.accord.serializers.Version;
|
|||
import org.apache.cassandra.service.accord.txn.TxnData.TxnDataNameKind;
|
||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
import org.apache.cassandra.utils.Comparables;
|
||||
import org.apache.cassandra.utils.MonotonicClock;
|
||||
import org.apache.cassandra.utils.ObjectSizes;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
|
@ -229,7 +226,7 @@ public class TxnNamedRead extends AbstractParameterisedVersionedSerialized<ReadC
|
|||
return TimeUnit.MICROSECONDS.toSeconds(executeAt.hlc());
|
||||
}
|
||||
|
||||
public AsyncChain<Data> read(TableMetadatas tables, ConsistencyLevel consistencyLevel, Seekable key, Timestamp executeAt)
|
||||
public AsyncChain<Data> read(AccordExecutor executor, TableMetadatas tables, ConsistencyLevel consistencyLevel, Seekable key, Timestamp executeAt)
|
||||
{
|
||||
ReadCommand command = deserialize(tables);
|
||||
if (command == null)
|
||||
|
|
@ -244,9 +241,9 @@ public class TxnNamedRead extends AbstractParameterisedVersionedSerialized<ReadC
|
|||
switch (key.domain())
|
||||
{
|
||||
case Key:
|
||||
return performLocalKeyRead(((SinglePartitionReadCommand) command).withTransactionalSettings(withoutReconciliation, nowInSeconds));
|
||||
return performLocalKeyRead(executor, ((SinglePartitionReadCommand) command).withTransactionalSettings(withoutReconciliation, nowInSeconds));
|
||||
case Range:
|
||||
return performLocalRangeRead(((PartitionRangeReadCommand) command), key.asRange(), consistencyLevel, nowInSeconds);
|
||||
return performLocalRangeRead(executor, ((PartitionRangeReadCommand) command), key.asRange(), consistencyLevel, nowInSeconds);
|
||||
default:
|
||||
throw new IllegalStateException("Unhandled domain " + key.domain());
|
||||
}
|
||||
|
|
@ -289,66 +286,35 @@ public class TxnNamedRead extends AbstractParameterisedVersionedSerialized<ReadC
|
|||
return deserialize(tables);
|
||||
}
|
||||
|
||||
private AsyncChain<Data> performLocalKeyRead(SinglePartitionReadCommand read)
|
||||
private AsyncChain<Data> performLocalKeyRead(AccordExecutor executor, SinglePartitionReadCommand command)
|
||||
{
|
||||
Callable<Data> readCallable = () ->
|
||||
Callable<Data> callable = new Callable<>()
|
||||
{
|
||||
try (ReadExecutionController controller = read.executionController();
|
||||
PartitionIterator iterator = UnfilteredPartitionIterators.filter(read.executeLocally(controller), read.nowInSec()))
|
||||
@Override
|
||||
public Data call()
|
||||
{
|
||||
TxnData result = new TxnData();
|
||||
if (iterator.hasNext())
|
||||
try (ReadExecutionController controller = command.executionController();
|
||||
PartitionIterator iterator = UnfilteredPartitionIterators.filter(command.executeLocally(controller), command.nowInSec()))
|
||||
{
|
||||
TxnDataKeyValue value = new TxnDataKeyValue(iterator.next());
|
||||
if (value.hasRows() || read.selectsFullPartition())
|
||||
result.put(name, value);
|
||||
TxnData result = new TxnData();
|
||||
if (iterator.hasNext())
|
||||
{
|
||||
TxnDataKeyValue value = new TxnDataKeyValue(iterator.next());
|
||||
if (value.hasRows() || command.selectsFullPartition())
|
||||
result.put(name, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return command.toCQLString();
|
||||
}
|
||||
};
|
||||
|
||||
return AsyncChains.ofCallable(Stage.READ.executor(), readCallable, (callable, receiver) ->
|
||||
new DebuggableTask.RunnableDebuggableTask()
|
||||
{
|
||||
private final long approxCreationTimeNanos = MonotonicClock.Global.approxTime.now();
|
||||
private volatile long approxStartTimeNanos;
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
approxStartTimeNanos = MonotonicClock.Global.approxTime.now();
|
||||
|
||||
try
|
||||
{
|
||||
Data call = callable.call();
|
||||
receiver.accept(call, null);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
logger.debug("AsyncChain Callable threw an Exception", t);
|
||||
receiver.accept(null, t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long creationTimeNanos()
|
||||
{
|
||||
return approxCreationTimeNanos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long startTimeNanos()
|
||||
{
|
||||
return approxStartTimeNanos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description()
|
||||
{
|
||||
return read.toCQLString();
|
||||
}
|
||||
}
|
||||
);
|
||||
return submit(executor, callable, callable);
|
||||
}
|
||||
|
||||
public static PartitionRangeReadCommand commandForSubrange(PartitionRangeReadCommand command, Range r, ConsistencyLevel consistencyLevel, long nowInSeconds)
|
||||
|
|
@ -392,75 +358,48 @@ public class TxnNamedRead extends AbstractParameterisedVersionedSerialized<ReadC
|
|||
return command.withTransactionalSettings(nowInSeconds, subRange, isRangeContinuation, readsWithoutReconciliation(consistencyLevel));
|
||||
}
|
||||
|
||||
private AsyncChain<Data> performLocalRangeRead(PartitionRangeReadCommand command, Range r, ConsistencyLevel consistencyLevel, long nowInSeconds)
|
||||
private AsyncChain<Data> performLocalRangeRead(AccordExecutor executor, PartitionRangeReadCommand command, Range r, ConsistencyLevel consistencyLevel, long nowInSeconds)
|
||||
{
|
||||
PartitionRangeReadCommand read = commandForSubrange(command, r, consistencyLevel, nowInSeconds);
|
||||
Callable<Data> readCallable = () ->
|
||||
Callable<Data> callable = new Callable<>()
|
||||
{
|
||||
try (ReadExecutionController controller = read.executionController();
|
||||
UnfilteredPartitionIterator partition = read.executeLocally(controller);
|
||||
PartitionIterator iterator = UnfilteredPartitionIterators.filter(partition, read.nowInSec()))
|
||||
@Override
|
||||
public Data call()
|
||||
{
|
||||
TxnData result = new TxnData();
|
||||
TxnDataRangeValue value = new TxnDataRangeValue();
|
||||
while (iterator.hasNext())
|
||||
try (ReadExecutionController controller = read.executionController();
|
||||
UnfilteredPartitionIterator partition = read.executeLocally(controller);
|
||||
PartitionIterator iterator = UnfilteredPartitionIterators.filter(partition, read.nowInSec()))
|
||||
{
|
||||
try (RowIterator rows = iterator.next())
|
||||
TxnData result = new TxnData();
|
||||
TxnDataRangeValue value = new TxnDataRangeValue();
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
FilteredPartition filtered = FilteredPartition.create(rows);
|
||||
if (filtered.hasRows() || read.selectsFullPartition())
|
||||
try (RowIterator rows = iterator.next())
|
||||
{
|
||||
value.add(filtered);
|
||||
FilteredPartition filtered = FilteredPartition.create(rows);
|
||||
if (filtered.hasRows() || read.selectsFullPartition())
|
||||
{
|
||||
value.add(filtered);
|
||||
}
|
||||
}
|
||||
}
|
||||
result.put(TxnData.txnDataName(TxnDataNameKind.USER), value);
|
||||
return result;
|
||||
}
|
||||
result.put(TxnData.txnDataName(TxnDataNameKind.USER), value);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return command.toCQLString();
|
||||
}
|
||||
};
|
||||
return submit(executor, callable, callable);
|
||||
}
|
||||
|
||||
return AsyncChains.ofCallable(Stage.READ.executor(), readCallable, (callable, receiver) ->
|
||||
new DebuggableTask.RunnableDebuggableTask()
|
||||
{
|
||||
private final long approxCreationTimeNanos = MonotonicClock.Global.approxTime.now();
|
||||
private volatile long approxStartTimeNanos;
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
approxStartTimeNanos = MonotonicClock.Global.approxTime.now();
|
||||
|
||||
try
|
||||
{
|
||||
Data call = callable.call();
|
||||
receiver.accept(call, null);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
logger.debug("AsyncChain Callable threw an Exception", t);
|
||||
receiver.accept(null, t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long creationTimeNanos()
|
||||
{
|
||||
return approxCreationTimeNanos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long startTimeNanos()
|
||||
{
|
||||
return approxStartTimeNanos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description()
|
||||
{
|
||||
return command.toCQLString();
|
||||
}
|
||||
}
|
||||
);
|
||||
private AsyncChain<Data> submit(AccordExecutor executor, Callable<Data> readCallable, Object describe)
|
||||
{
|
||||
return executor.buildDebuggable(readCallable, describe);
|
||||
}
|
||||
|
||||
static final ParameterisedVersionedSerializer<TxnNamedRead, TableMetadatasAndKeys, Version> serializer = new ParameterisedVersionedSerializer<>()
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@ import javax.annotation.Nullable;
|
|||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import accord.api.Data;
|
||||
import accord.api.DataStore;
|
||||
import accord.api.Key;
|
||||
import accord.api.Read;
|
||||
import accord.local.CommandStore;
|
||||
import accord.local.SafeCommandStore;
|
||||
import accord.primitives.Keys;
|
||||
import accord.primitives.Participants;
|
||||
|
|
@ -40,7 +40,6 @@ import accord.primitives.Routable.Domain;
|
|||
import accord.primitives.Seekable;
|
||||
import accord.primitives.Seekables;
|
||||
import accord.primitives.Timestamp;
|
||||
import accord.utils.Invariants;
|
||||
import accord.utils.UnhandledEnum;
|
||||
import accord.utils.async.AsyncChain;
|
||||
import accord.utils.async.AsyncChains;
|
||||
|
|
@ -53,16 +52,17 @@ import org.apache.cassandra.dht.AbstractBounds;
|
|||
import org.apache.cassandra.io.ParameterisedVersionedSerializer;
|
||||
import org.apache.cassandra.io.util.DataInputPlus;
|
||||
import org.apache.cassandra.io.util.DataOutputPlus;
|
||||
import org.apache.cassandra.service.accord.AccordCommandStore;
|
||||
import org.apache.cassandra.service.accord.AccordExecutor;
|
||||
import org.apache.cassandra.service.accord.api.PartitionKey;
|
||||
import org.apache.cassandra.service.accord.serializers.TableMetadatas;
|
||||
import org.apache.cassandra.service.accord.serializers.TableMetadatasAndKeys;
|
||||
import org.apache.cassandra.service.accord.serializers.Version;
|
||||
import org.apache.cassandra.tcm.ClusterMetadata;
|
||||
import org.apache.cassandra.utils.ObjectSizes;
|
||||
|
||||
import static accord.primitives.Routables.Slice.Minimal;
|
||||
import static accord.utils.Invariants.require;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static org.apache.cassandra.service.accord.AccordSerializers.consistencyLevelSerializer;
|
||||
import static org.apache.cassandra.service.accord.IAccordService.SUPPORTED_READ_CONSISTENCY_LEVELS;
|
||||
import static org.apache.cassandra.service.accord.txn.TxnData.TxnDataNameKind.CAS_READ;
|
||||
|
|
@ -124,7 +124,7 @@ public class TxnRead extends AbstractKeySorted<TxnNamedRead> implements Read
|
|||
// TODO (expected): relax this condition, require only that it holds for each equal byte[]
|
||||
// right now this means we don't permit two different range queries in the same transaction touching adjacent ranges
|
||||
// this is a pretty weak restriction and doesn't interfere with current CQL capabilities, but should be addressed eventually
|
||||
Invariants.require(domain == Domain.Key || ((Ranges)keys()).mergeTouching() == keys());
|
||||
require(domain == Domain.Key || ((Ranges)keys()).mergeTouching() == keys());
|
||||
}
|
||||
|
||||
private TxnRead(TableMetadatas tables, @Nonnull List<TxnNamedRead> items, @Nullable ConsistencyLevel cassandraConsistencyLevel)
|
||||
|
|
@ -134,14 +134,13 @@ public class TxnRead extends AbstractKeySorted<TxnNamedRead> implements Read
|
|||
checkArgument(cassandraConsistencyLevel == null || SUPPORTED_READ_CONSISTENCY_LEVELS.contains(cassandraConsistencyLevel), "Unsupported consistency level for read: %s", cassandraConsistencyLevel);
|
||||
this.cassandraConsistencyLevel = cassandraConsistencyLevel;
|
||||
this.domain = items.get(0).key().domain();
|
||||
Invariants.require(domain == Domain.Key || ((Ranges)keys()).mergeTouching() == keys());
|
||||
require(domain == Domain.Key || ((Ranges)keys()).mergeTouching() == keys());
|
||||
}
|
||||
|
||||
private static void sortReads(List<TxnNamedRead> reads)
|
||||
{
|
||||
if (reads.size() == 0)
|
||||
return;
|
||||
reads.sort(TXN_NAMED_READ_KEY_COMPARATOR);
|
||||
if (reads.size() > 1)
|
||||
reads.sort(TXN_NAMED_READ_KEY_COMPARATOR);
|
||||
}
|
||||
|
||||
public static TxnRead createTxnRead(TableMetadatas tables, @Nonnull List<TxnNamedRead> items, @Nullable ConsistencyLevel consistencyLevel, Domain domain)
|
||||
|
|
@ -360,7 +359,7 @@ public class TxnRead extends AbstractKeySorted<TxnNamedRead> implements Read
|
|||
}
|
||||
else
|
||||
{
|
||||
Invariants.require(c == 0);
|
||||
require(c == 0);
|
||||
pending = pending.merge(add);
|
||||
}
|
||||
}
|
||||
|
|
@ -380,22 +379,26 @@ public class TxnRead extends AbstractKeySorted<TxnNamedRead> implements Read
|
|||
}
|
||||
|
||||
@Override
|
||||
public AsyncChain<Data> read(Seekable key, SafeCommandStore safeStore, Timestamp executeAt, DataStore store)
|
||||
public AsyncChain<Data> read(SafeCommandStore safeStore, Seekable key, Timestamp executeAt)
|
||||
{
|
||||
// Set to null since we don't need it and interop can pass in null
|
||||
safeStore = null;
|
||||
ClusterMetadata cm = ClusterMetadata.current();
|
||||
checkState(cm.epoch.getEpoch() >= executeAt.epoch(), "TCM epoch %d is < executeAt epoch %d", cm.epoch.getEpoch(), executeAt.epoch());
|
||||
return readDirect(safeStore.commandStore(), key, executeAt);
|
||||
}
|
||||
|
||||
List<AsyncChain<Data>> results = new ArrayList<>();
|
||||
forEachWithKey(key, read -> results.add(read.read(tables, cassandraConsistencyLevel, key, executeAt)));
|
||||
|
||||
if (results.isEmpty())
|
||||
// Result type must match everywhere
|
||||
@Override
|
||||
public AsyncChain<Data> readDirect(CommandStore commandStore, Seekable key, Timestamp executeAt)
|
||||
{
|
||||
if (key == null)
|
||||
return AsyncChains.success(new TxnData());
|
||||
|
||||
if (results.size() == 1)
|
||||
return results.get(0);
|
||||
AccordExecutor executor = ((AccordCommandStore)commandStore).executor();
|
||||
if (items.length == 1 && key.equals(items[0].key()))
|
||||
return items[0].read(executor, tables, cassandraConsistencyLevel, key, executeAt);
|
||||
|
||||
List<AsyncChain<Data>> results = new ArrayList<>();
|
||||
forEachWithKey(key, read -> results.add(read.read(executor, tables, cassandraConsistencyLevel, key, executeAt)));
|
||||
|
||||
if (results.isEmpty())
|
||||
return AsyncChains.success(new TxnData());
|
||||
|
||||
return AsyncChains.reduce(results, Data::merge);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,13 +27,14 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import accord.api.DataStore;
|
||||
import accord.api.Write;
|
||||
import accord.local.CommandStore;
|
||||
import accord.local.SafeCommandStore;
|
||||
import accord.primitives.PartialTxn;
|
||||
import accord.primitives.Routable.Domain;
|
||||
|
|
@ -45,7 +46,6 @@ import accord.primitives.TxnId;
|
|||
import accord.primitives.Writes;
|
||||
import accord.utils.async.AsyncChain;
|
||||
import accord.utils.async.AsyncChains;
|
||||
import org.apache.cassandra.concurrent.Stage;
|
||||
import org.apache.cassandra.cql3.UpdateParameters;
|
||||
import org.apache.cassandra.db.Clustering;
|
||||
import org.apache.cassandra.db.Columns;
|
||||
|
|
@ -62,6 +62,8 @@ import org.apache.cassandra.io.util.DataInputPlus;
|
|||
import org.apache.cassandra.io.util.DataOutputBuffer;
|
||||
import org.apache.cassandra.io.util.DataOutputPlus;
|
||||
import org.apache.cassandra.schema.ColumnMetadata;
|
||||
import org.apache.cassandra.service.accord.AccordCommandStore;
|
||||
import org.apache.cassandra.service.accord.AccordExecutor;
|
||||
import org.apache.cassandra.service.accord.api.PartitionKey;
|
||||
import org.apache.cassandra.service.accord.serializers.TableMetadatas;
|
||||
import org.apache.cassandra.service.accord.serializers.TableMetadatasAndKeys;
|
||||
|
|
@ -135,13 +137,13 @@ public class TxnWrite extends AbstractKeySorted<TxnWrite.Update> implements Writ
|
|||
'}';
|
||||
}
|
||||
|
||||
public AsyncChain<Void> write(TableMetadatas tables, boolean preserveTimestamps, long timestamp)
|
||||
public AsyncChain<Void> write(Executor executor, TableMetadatas tables, boolean preserveTimestamps, long timestamp)
|
||||
{
|
||||
PartitionUpdate update = deserialize(tables);
|
||||
if (!preserveTimestamps)
|
||||
update = new PartitionUpdate.Builder(update, 0).updateAllTimestamp(timestamp).build();
|
||||
Mutation mutation = new Mutation(update, PotentialTxnConflicts.ALLOW);
|
||||
return AsyncChains.ofRunnable(Stage.MUTATION.executor(), mutation::applyUnsafe);
|
||||
return AsyncChains.ofRunnable(executor, mutation::applyUnsafe);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -424,7 +426,13 @@ public class TxnWrite extends AbstractKeySorted<TxnWrite.Update> implements Writ
|
|||
}
|
||||
|
||||
@Override
|
||||
public AsyncChain<Void> apply(Seekable key, SafeCommandStore safeStore, TxnId txnId, Timestamp executeAt, DataStore store, PartialTxn txn)
|
||||
public AsyncChain<Void> apply(SafeCommandStore safeStore, Seekable key, TxnId txnId, Timestamp executeAt, PartialTxn txn)
|
||||
{
|
||||
return applyDirect(safeStore.commandStore(), key, txnId, executeAt, txn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncChain<Void> applyDirect(CommandStore commandStore, Seekable key, TxnId txnId, Timestamp executeAt, PartialTxn txn)
|
||||
{
|
||||
// UnrecoverableRepairUpdate will deserialize as null at other nodes
|
||||
// Accord should skip the Update for a read transaction, but handle it here anyways
|
||||
|
|
@ -438,15 +446,16 @@ public class TxnWrite extends AbstractKeySorted<TxnWrite.Update> implements Writ
|
|||
List<AsyncChain<Void>> results = new ArrayList<>();
|
||||
if (isConditionMet)
|
||||
{
|
||||
AccordExecutor executor = ((AccordCommandStore) commandStore).executor();
|
||||
boolean preserveTimestamps = txnUpdate.preserveTimestamps();
|
||||
// Apply updates not specified fully by the client but built from fragments completed by data from reads.
|
||||
// This occurs, for example, when an UPDATE statement uses a value assigned by a LET statement.
|
||||
forEachWithKey(key, write -> results.add(write.write(tables, preserveTimestamps, timestamp)));
|
||||
forEachWithKey(key, write -> results.add(write.write(executor, tables, preserveTimestamps, timestamp)));
|
||||
// Apply updates that are fully specified by the client and not reliant on data from reads.
|
||||
// ex. INSERT INTO tbl (a, b, c) VALUES (1, 2, 3)
|
||||
// These updates are persisted only in TxnUpdate and not in TxnWrite to avoid duplication.
|
||||
List<Update> updates = txnUpdate.completeUpdatesForKey((RoutableKey) key);
|
||||
updates.forEach(write -> results.add(write.write(tables, preserveTimestamps, timestamp)));
|
||||
updates.forEach(write -> results.add(write.write(executor, tables, preserveTimestamps, timestamp)));
|
||||
}
|
||||
|
||||
if (results.isEmpty())
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import org.junit.AfterClass;
|
|||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import accord.impl.progresslog.DefaultProgressLogs;
|
||||
import com.datastax.driver.core.ConsistencyLevel;
|
||||
import com.datastax.driver.core.Session;
|
||||
import com.datastax.driver.core.SimpleStatement;
|
||||
|
|
@ -42,7 +41,6 @@ import org.apache.cassandra.db.ReadExecutionController;
|
|||
import org.apache.cassandra.db.partitions.UnfilteredPartitionIterator;
|
||||
import org.apache.cassandra.distributed.Cluster;
|
||||
import org.apache.cassandra.distributed.api.Feature;
|
||||
import org.apache.cassandra.distributed.api.IInvokableInstance;
|
||||
import org.apache.cassandra.distributed.api.Row;
|
||||
import org.apache.cassandra.distributed.api.SimpleQueryResult;
|
||||
import org.apache.cassandra.service.consensus.TransactionalMode;
|
||||
|
|
@ -67,7 +65,6 @@ public class QueriesTableTest extends TestBaseImpl
|
|||
SHARED_CLUSTER = init(Cluster.build(1).withInstanceInitializer(QueryDelayHelper::install)
|
||||
.withConfig(c -> c.with(Feature.NATIVE_PROTOCOL, Feature.GOSSIP)
|
||||
.set("write_request_timeout", "10s")).start());
|
||||
|
||||
DRIVER_CLUSTER = JavaDriverUtils.create(SHARED_CLUSTER);
|
||||
SESSION = DRIVER_CLUSTER.connect();
|
||||
}
|
||||
|
|
@ -178,10 +175,6 @@ public class QueriesTableTest extends TestBaseImpl
|
|||
{
|
||||
SHARED_CLUSTER.schemaChange("CREATE TABLE " + KEYSPACE + ".accord_tbl (k int primary key, v int) WITH " + TransactionalMode.mixed_reads.asCqlParam());
|
||||
|
||||
// Disable recovery to make sure only one local read occurs:
|
||||
for (IInvokableInstance instance : SHARED_CLUSTER)
|
||||
instance.runOnInstance(() -> DefaultProgressLogs.unsafePauseForTesting(true));
|
||||
|
||||
String update = "BEGIN TRANSACTION\n" +
|
||||
" LET row1 = (SELECT * FROM " + KEYSPACE + ".accord_tbl WHERE k = 0);\n" +
|
||||
" SELECT row1.k, row1.v;\n" +
|
||||
|
|
@ -220,7 +213,7 @@ public class QueriesTableTest extends TestBaseImpl
|
|||
String threadId = row.get("thread_id").toString();
|
||||
String task = row.get("task").toString();
|
||||
|
||||
readVisible |= threadId.contains("Read") && task.contains("SELECT");
|
||||
readVisible |= (threadId.contains("AccordExecutor") || threadId.contains("Read")) && task.contains("SELECT");
|
||||
coordinatorTxnVisible |= threadId.contains("Native-Transport-Requests") && task.contains("BEGIN TRANSACTION");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -166,10 +166,18 @@ public class AccordLoadTest extends AccordTestBase
|
|||
}
|
||||
catch (RejectedExecutionException e)
|
||||
{
|
||||
int index = 1 + random.nextInt(cluster.size());
|
||||
logger.info("Picking new coordinator ... {}", index);
|
||||
coordinator = cluster.coordinator(index);
|
||||
inFlight.release();
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
int index = 1 + random.nextInt(cluster.size());
|
||||
logger.info("Picking new coordinator ... {}", index);
|
||||
coordinator = cluster.coordinator(index);
|
||||
break;
|
||||
}
|
||||
catch (Throwable t) { logger.info("Failed to select coordinator", t); }
|
||||
}
|
||||
}
|
||||
batchSize++;
|
||||
if (System.nanoTime() >= batchEnd)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ import org.junit.Test;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import accord.api.ProtocolModifiers;
|
||||
import accord.primitives.TxnId.FastPath;
|
||||
import accord.primitives.TxnId.FastPaths;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.db.virtual.AccordDebugKeyspace;
|
||||
import org.apache.cassandra.distributed.api.ConsistencyLevel;
|
||||
|
|
@ -72,7 +75,10 @@ public class AccordMetricsTest extends AccordTestBase
|
|||
public static void setupClass() throws IOException
|
||||
{
|
||||
AccordTestBase.setupCluster(Function.identity(), 2);
|
||||
SHARED_CLUSTER.forEach(node -> node.runOnInstance(() -> AccordService.instance().setCacheSize(0)));
|
||||
SHARED_CLUSTER.forEach(node -> node.runOnInstance(() -> {
|
||||
AccordService.instance().setCacheSize(0);
|
||||
ProtocolModifiers.Toggles.setPermittedFastPaths(new FastPaths(FastPath.Unoptimised));
|
||||
}));
|
||||
for (int i = 0; i < SHARED_CLUSTER.size(); i++) // initialize metrics
|
||||
logger.trace(SHARED_CLUSTER.get(i + 1).callOnInstance(() -> AccordMetrics.readMetrics.toString() + AccordMetrics.writeMetrics.toString()));
|
||||
}
|
||||
|
|
@ -120,8 +126,8 @@ public class AccordMetricsTest extends AccordTestBase
|
|||
SHARED_CLUSTER.coordinator(1).executeWithResult(readCql(), ConsistencyLevel.ALL, 0, 0, 1, 1);
|
||||
assertCoordinatorMetrics(0, "ro", 1, 0, 0, 0, 0);
|
||||
assertCoordinatorMetrics(1, "ro", 0, 0, 0, 0, 0);
|
||||
assertReplicaMetrics(0, "ro", 1, 1, 0);
|
||||
assertReplicaMetrics(1, "ro", 1, 1, 0);
|
||||
assertReplicaMetrics(0, "ro", 0, 1, 1);
|
||||
assertReplicaMetrics(1, "ro", 0, 1, 1);
|
||||
assertZeroMetrics("rw");
|
||||
}
|
||||
|
||||
|
|
@ -285,9 +291,9 @@ public class AccordMetricsTest extends AccordTestBase
|
|||
Map<String, Long> metrics = diff(countingMetrics0).get(node);
|
||||
Function<String, Long> metric = n -> metrics.get(nameFactory.createMetricName(n).getMetricName());
|
||||
assertThat(metric.apply(AccordMetrics.STABLE_LATENCY)).isLessThanOrEqualTo(stable);
|
||||
assertThat(metric.apply(AccordMetrics.EXECUTE_LATENCY)).isEqualTo(executions);
|
||||
assertThat(metric.apply(AccordMetrics.PREAPPLY_LATENCY)).isEqualTo(executions);
|
||||
assertThat(metric.apply(AccordMetrics.APPLY_LATENCY)).isEqualTo(applications);
|
||||
assertThat(metric.apply(AccordMetrics.APPLY_DURATION)).isEqualTo(applications);
|
||||
assertThat(metric.apply(AccordMetrics.APPLY_DURATION)).isEqualTo(scope.equals("rw") ? applications : 0);
|
||||
assertThat(metric.apply(AccordMetrics.PARTIAL_DEPENDENCIES)).isEqualTo(executions);
|
||||
|
||||
// Verify that replica metrics are published to the appropriate virtual table:
|
||||
|
|
|
|||
|
|
@ -172,8 +172,7 @@ public class AccordReadInteroperabilityTest extends AccordTestBase
|
|||
assertEquals(0, messageCount(Verb.ACCORD_INTEROP_STABLE_THEN_READ_REQ));
|
||||
assertEquals(0, messageCount(Verb.ACCORD_INTEROP_READ_REQ));
|
||||
assertEquals(0, messageCount(Verb.ACCORD_INTEROP_READ_RSP));
|
||||
// Durability scheduling creates a lot of background commits that generate read responses
|
||||
assertTrue(messageCount(Verb.ACCORD_READ_RSP) > 0);
|
||||
assertTrue(messageCount(Verb.ACCORD_GET_EPHMRL_READ_DEPS_REQ) > 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import accord.api.Agent;
|
||||
import accord.api.Key;
|
||||
import accord.api.Result;
|
||||
import accord.local.CheckedCommands;
|
||||
import accord.local.Command;
|
||||
import accord.local.CommandStore;
|
||||
|
|
@ -60,7 +59,6 @@ import accord.primitives.Status;
|
|||
import accord.primitives.Txn;
|
||||
import accord.primitives.Txn.Kind;
|
||||
import accord.primitives.TxnId;
|
||||
import accord.primitives.Writes;
|
||||
import org.apache.cassandra.SchemaLoader;
|
||||
import org.apache.cassandra.config.CassandraRelevantProperties;
|
||||
import org.apache.cassandra.cql3.QueryProcessor;
|
||||
|
|
@ -88,7 +86,6 @@ import org.apache.cassandra.service.accord.AccordTestUtils;
|
|||
import org.apache.cassandra.service.accord.IAccordService;
|
||||
import org.apache.cassandra.service.accord.api.TokenKey;
|
||||
import org.apache.cassandra.utils.FBUtilities;
|
||||
import org.apache.cassandra.utils.Pair;
|
||||
|
||||
import static accord.local.KeyHistory.SYNC;
|
||||
import static accord.local.PreLoadContext.contextFor;
|
||||
|
|
@ -281,15 +278,12 @@ public class CompactionAccordIteratorsTest
|
|||
|
||||
private static void flush(AccordCommandStore commandStore)
|
||||
{
|
||||
commandStore.executeBlocking(() -> {
|
||||
// clear cache and wait for post-eviction writes to complete
|
||||
try (AccordExecutor.ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();)
|
||||
{
|
||||
long cacheSize = cache.global.capacity();
|
||||
cache.global.setCapacity(0);
|
||||
cache.global.setCapacity(cacheSize);
|
||||
}
|
||||
});
|
||||
try (AccordExecutor.ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();)
|
||||
{
|
||||
long cacheSize = cache.global.capacity();
|
||||
cache.global.setCapacity(0);
|
||||
cache.global.setCapacity(cacheSize);
|
||||
}
|
||||
commandsForKey.forceBlockingFlush(FlushReason.UNIT_TESTS);
|
||||
while (commandStore.executor().hasTasks())
|
||||
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
|
||||
|
|
@ -331,10 +325,11 @@ public class CompactionAccordIteratorsTest
|
|||
CheckedCommands.commit(safe, SaveStatus.Stable, Ballot.ZERO, txnId, route, partialTxn, txnId, partialDeps, (a, b) -> {});
|
||||
}).beginAsResult());
|
||||
flush(commandStore);
|
||||
getUninterruptibly(commandStore.execute(contextFor(txnId, route, SYNC), safe -> {
|
||||
Pair<Writes, Result> result = AccordTestUtils.processTxnResultDirect(safe, txnId, partialTxn, txnId);
|
||||
getUninterruptibly(commandStore.submit(contextFor(txnId, route, SYNC), safe -> {
|
||||
return AccordTestUtils.processTxnResultDirect(safe, txnId, partialTxn, txnId);
|
||||
}).flatMap(i -> i).flatMap(result -> commandStore.execute(contextFor(txnId, route, SYNC), safe -> {
|
||||
CheckedCommands.apply(safe, txnId, route, txnId, partialDeps, partialTxn, result.left, result.right, (a, b) -> {});
|
||||
}).beginAsResult());
|
||||
})));
|
||||
flush(commandStore);
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -237,6 +237,7 @@ public class AccordDebugKeyspaceTest extends CQLTester
|
|||
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:
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ public class RouteIndexTest extends CQLTester
|
|||
Domain domain = state.domainGen.next(rs);
|
||||
TxnId txnId = state.nextTxnId(domain);
|
||||
Route<?> route = createRoute(state, rs, domain, rs.nextInt(1, 20));
|
||||
int storeId = state.accordService.node().commandStores().select(route.homeKey()).id();
|
||||
int storeId = state.accordService.node().commandStores().unsafeForKey(route.homeKey()).id();
|
||||
return new InsertTxn(storeId, txnId, SaveStatus.PreAccepted, route);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ import accord.primitives.TxnId;
|
|||
import accord.primitives.Writes;
|
||||
import accord.utils.ImmutableBitSet;
|
||||
import accord.utils.SimpleBitSet;
|
||||
import accord.utils.async.AsyncChains;
|
||||
import org.apache.cassandra.SchemaLoader;
|
||||
import org.apache.cassandra.cql3.QueryProcessor;
|
||||
import org.apache.cassandra.db.ColumnFamilyStore;
|
||||
|
|
@ -130,7 +131,7 @@ public class AccordCommandStoreTest
|
|||
SimpleBitSet waitingOnApply = new SimpleBitSet(3);
|
||||
waitingOnApply.set(1);
|
||||
Command.WaitingOn waitingOn = new Command.WaitingOn(dependencies.keyDeps.keys(), dependencies.rangeDeps, new ImmutableBitSet(waitingOnApply), new ImmutableBitSet(2));
|
||||
Pair<Writes, Result> result = AccordTestUtils.processTxnResult(commandStore, txnId, txn, executeAt);
|
||||
Pair<Writes, Result> result = AsyncChains.getBlocking(AccordTestUtils.processTxnResult(commandStore, txnId, txn, executeAt));
|
||||
|
||||
Command expected = Command.Executed.executed(txnId, SaveStatus.Applied, Majority, StoreParticipants.all(route),
|
||||
promised, executeAt, txn, dependencies, accepted,
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ public class AccordMessageSinkTest
|
|||
public void txnRead()
|
||||
{
|
||||
TxnId txnId = nextTxnId(42, Txn.Kind.Read, Routable.Domain.Key);
|
||||
Request request = new ReadTxnData(node, topologies, txnId, topology.ranges(), txnId.epoch());
|
||||
Request request = new ReadTxnData(node, topologies, txnId, topology.ranges(), null, null, txnId.epoch());
|
||||
checkRequestReplies(request,
|
||||
new ReadData.ReadOk(null, null, 0),
|
||||
CommitOrReadNack.Insufficient);
|
||||
|
|
|
|||
|
|
@ -207,14 +207,12 @@ public class AccordTaskTest
|
|||
}).beginAsResult());
|
||||
|
||||
// clear cache
|
||||
commandStore.executeBlocking(() -> {
|
||||
try (ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();)
|
||||
{
|
||||
long cacheSize = cache.global.capacity();
|
||||
cache.global.setCapacity(0);
|
||||
cache.global.setCapacity(cacheSize);
|
||||
}
|
||||
});
|
||||
try (ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();)
|
||||
{
|
||||
long cacheSize = cache.global.capacity();
|
||||
cache.global.setCapacity(0);
|
||||
cache.global.setCapacity(cacheSize);
|
||||
}
|
||||
|
||||
while (commandStore.executor().hasTasks())
|
||||
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
|
||||
|
|
@ -261,14 +259,12 @@ public class AccordTaskTest
|
|||
}).beginAsResult());
|
||||
|
||||
// clear cache
|
||||
commandStore.executeBlocking(() -> {
|
||||
try (ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();)
|
||||
{
|
||||
long cacheSize = cache.global.capacity();
|
||||
cache.global.setCapacity(0);
|
||||
cache.global.setCapacity(cacheSize);
|
||||
}
|
||||
});
|
||||
try (ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();)
|
||||
{
|
||||
long cacheSize = cache.global.capacity();
|
||||
cache.global.setCapacity(0);
|
||||
cache.global.setCapacity(cacheSize);
|
||||
}
|
||||
|
||||
while (commandStore.executor().hasTasks())
|
||||
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
|
||||
|
|
@ -288,7 +284,10 @@ public class AccordTaskTest
|
|||
// all txn use the same key; 0
|
||||
Keys keys = keys(Schema.instance.getTableMetadata("ks", "tbl"), 0);
|
||||
AccordCommandStore commandStore = createAccordCommandStore(clock::incrementAndGet, "ks", "tbl");
|
||||
commandStore.executeBlocking(() -> commandStore.executor().cacheUnsafe().setCapacity(0));
|
||||
try (AccordExecutor.ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();)
|
||||
{
|
||||
cache.global.setCapacity(0);
|
||||
}
|
||||
Gen<TxnId> txnIdGen = rs -> txnId(1, clock.incrementAndGet(), 1);
|
||||
|
||||
qt().withSeed(3447647345436261108L).withPure(false)
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import java.util.stream.IntStream;
|
|||
import com.google.common.collect.Sets;
|
||||
import org.junit.Assert;
|
||||
|
||||
import accord.api.AsyncExecutor;
|
||||
import accord.api.Data;
|
||||
import accord.api.Journal;
|
||||
import accord.api.ProgressLog.NoOpProgressLog;
|
||||
|
|
@ -52,6 +53,7 @@ import accord.local.Node.Id;
|
|||
import accord.local.NodeCommandStoreService;
|
||||
import accord.local.PreLoadContext;
|
||||
import accord.local.SafeCommandStore;
|
||||
import accord.local.SequentialAsyncExecutor;
|
||||
import accord.local.StoreParticipants;
|
||||
import accord.local.TimeService;
|
||||
import accord.local.durability.DurabilityService;
|
||||
|
|
@ -74,6 +76,7 @@ import accord.topology.Shard;
|
|||
import accord.topology.Topology;
|
||||
import accord.topology.TopologyManager;
|
||||
import accord.utils.SortedArrays.SortedArrayList;
|
||||
import accord.utils.async.AsyncChain;
|
||||
import accord.utils.async.AsyncChains;
|
||||
import org.apache.cassandra.ServerTestUtils;
|
||||
import org.apache.cassandra.concurrent.ExecutorPlus;
|
||||
|
|
@ -210,35 +213,24 @@ public class AccordTestUtils
|
|||
return Ballot.fromValues(epoch, hlc, new Node.Id(node));
|
||||
}
|
||||
|
||||
public static 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<Pair<Writes, Result>> result = new AtomicReference<>();
|
||||
AtomicReference<AsyncChain<Pair<Writes, Result>>> result = new AtomicReference<>();
|
||||
getUninterruptibly(commandStore.execute(PreLoadContext.contextFor(txn.keys().toParticipants()),
|
||||
safeStore -> result.set(processTxnResultDirect(safeStore, txnId, txn, executeAt))));
|
||||
return result.get();
|
||||
}
|
||||
|
||||
public static Pair<Writes, Result> processTxnResultDirect(SafeCommandStore safeStore, TxnId txnId, PartialTxn txn, Timestamp executeAt)
|
||||
public static AsyncChain<Pair<Writes, Result>> processTxnResultDirect(SafeCommandStore safeStore, TxnId txnId, PartialTxn txn, Timestamp executeAt)
|
||||
{
|
||||
TxnRead read = (TxnRead) txn.read();
|
||||
Data readData = read.keys().stream().map(key -> {
|
||||
try
|
||||
{
|
||||
return AsyncChains.getBlocking(read.read(key, safeStore, executeAt, null));
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
throw new UncheckedInterruptedException(e);
|
||||
}
|
||||
catch (ExecutionException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
})
|
||||
.reduce(null, TxnData::merge);
|
||||
return Pair.create(txnId.is(Write) ? txn.execute(txnId, executeAt, readData) : null,
|
||||
txn.query().compute(txnId, executeAt, txn.keys(), readData, txn.read(), txn.update()));
|
||||
|
||||
return AsyncChains.allOf(read.keys().stream().map(key -> read.read(safeStore, key, executeAt))
|
||||
.collect(Collectors.toList()))
|
||||
.map(list -> {
|
||||
Data data = list.stream().reduce(Data::merge).orElse(new TxnData());
|
||||
return Pair.create(txnId.is(Write) ? txn.execute(txnId, executeAt, data) : null,
|
||||
txn.query().compute(txnId, executeAt, txn.keys(), data, txn.read(), txn.update()));
|
||||
});
|
||||
}
|
||||
|
||||
public static String wrapInTxn(String query)
|
||||
|
|
@ -342,7 +334,20 @@ public class AccordTestUtils
|
|||
{
|
||||
NodeCommandStoreService time = new NodeCommandStoreService()
|
||||
{
|
||||
@Override
|
||||
public AsyncExecutor someExecutor()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SequentialAsyncExecutor someSequentialExecutor()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private ToLongFunction<TimeUnit> elapsed = TimeService.elapsedWrapperFromNonMonotonicSource(TimeUnit.MICROSECONDS, this::now);
|
||||
private long stamp = 0;
|
||||
|
||||
@Override public Timeouts timeouts() { return null; }
|
||||
@Override public DurableBefore durableBefore() { return DurableBefore.EMPTY; }
|
||||
|
|
@ -353,6 +358,8 @@ public class AccordTestUtils
|
|||
@Override public long uniqueNow(long atLeast) { return now.getAsLong(); }
|
||||
@Override public long elapsed(TimeUnit timeUnit) { return elapsed.applyAsLong(timeUnit); }
|
||||
@Override public TopologyManager topology() { throw new UnsupportedOperationException(); }
|
||||
@Override public long currentStamp() { return stamp; }
|
||||
@Override public void updateStamp() {++stamp;}
|
||||
};
|
||||
|
||||
AccordAgent agent = new AccordAgent();
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import java.util.function.Predicate;
|
|||
import java.util.function.ToLongFunction;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import accord.api.AsyncExecutor;
|
||||
import accord.api.Journal;
|
||||
import accord.api.LocalListeners;
|
||||
import accord.api.ProgressLog;
|
||||
|
|
@ -52,6 +53,7 @@ import accord.local.NodeCommandStoreService;
|
|||
import accord.local.PreLoadContext;
|
||||
import accord.local.SafeCommand;
|
||||
import accord.local.SafeCommandStore;
|
||||
import accord.local.SequentialAsyncExecutor;
|
||||
import accord.local.TimeService;
|
||||
import accord.local.durability.DurabilityService;
|
||||
import accord.messages.BeginRecovery;
|
||||
|
|
@ -164,8 +166,21 @@ public class SimulatedAccordCommandStore implements AutoCloseable
|
|||
|
||||
this.storeService = new NodeCommandStoreService()
|
||||
{
|
||||
@Override
|
||||
public AsyncExecutor someExecutor()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SequentialAsyncExecutor someSequentialExecutor()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private final ToLongFunction<TimeUnit> elapsed = TimeService.elapsedWrapperFromNonMonotonicSource(TimeUnit.NANOSECONDS, this::now);
|
||||
final Timeouts timeouts = new DefaultTimeouts(this);
|
||||
long stamp;
|
||||
|
||||
@Override public Timeouts timeouts() { return timeouts; }
|
||||
|
||||
|
|
@ -215,6 +230,18 @@ public class SimulatedAccordCommandStore implements AutoCloseable
|
|||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long currentStamp()
|
||||
{
|
||||
return stamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStamp()
|
||||
{
|
||||
++stamp;
|
||||
}
|
||||
};
|
||||
|
||||
TestAgent.RethrowAgent agent = new TestAgent.RethrowAgent()
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ import org.junit.BeforeClass;
|
|||
import org.junit.Test;
|
||||
|
||||
import accord.api.Agent;
|
||||
import accord.api.AsyncExecutor;
|
||||
import accord.api.DataStore;
|
||||
import accord.api.Journal;
|
||||
import accord.api.Key;
|
||||
|
|
@ -65,6 +66,7 @@ import accord.local.NodeCommandStoreService;
|
|||
import accord.local.PreLoadContext;
|
||||
import accord.local.SafeCommand;
|
||||
import accord.local.SafeCommandStore;
|
||||
import accord.local.SequentialAsyncExecutor;
|
||||
import accord.local.StoreParticipants;
|
||||
import accord.local.TimeService;
|
||||
import accord.local.cfk.CommandsForKey;
|
||||
|
|
@ -244,7 +246,6 @@ public class CommandsForKeySerializerTest
|
|||
return Command.Committed.committed(builder(), saveStatus);
|
||||
|
||||
case PreApplied:
|
||||
case Applying:
|
||||
case Applied:
|
||||
return Command.Executed.executed(builder(), saveStatus);
|
||||
|
||||
|
|
@ -423,7 +424,7 @@ public class CommandsForKeySerializerTest
|
|||
@Test
|
||||
public void serde()
|
||||
{
|
||||
testOne(629993588068216851L);
|
||||
testOne(-4567266914751633833L);
|
||||
Random random = new Random();
|
||||
for (int i = 0 ; i < 10000 ; ++i)
|
||||
{
|
||||
|
|
@ -479,7 +480,7 @@ public class CommandsForKeySerializerTest
|
|||
}
|
||||
}
|
||||
|
||||
Choices<SaveStatus> saveStatusChoices = Choices.uniform(EnumSet.complementOf(EnumSet.of(SaveStatus.TruncatedApply, SaveStatus.TruncatedUnapplied, SaveStatus.TruncatedApplyWithOutcome)).toArray(SaveStatus[]::new));
|
||||
Choices<SaveStatus> saveStatusChoices = Choices.uniform(EnumSet.complementOf(EnumSet.of(SaveStatus.Applying, SaveStatus.TruncatedApply, SaveStatus.TruncatedUnapplied, SaveStatus.TruncatedApplyWithOutcome)).toArray(SaveStatus[]::new));
|
||||
Supplier<SaveStatus> saveStatusSupplier = () -> {
|
||||
SaveStatus result = saveStatusChoices.choose(source);
|
||||
while (result.is(Status.Truncated)) // we don't currently process truncations
|
||||
|
|
@ -697,6 +698,8 @@ public class CommandsForKeySerializerTest
|
|||
@Override public ProgressLog progressLog() { return null; }
|
||||
@Override public NodeCommandStoreService node() { return new NodeCommandStoreService()
|
||||
{
|
||||
@Override public AsyncExecutor someExecutor() { throw new UnsupportedOperationException(); }
|
||||
@Override public SequentialAsyncExecutor someSequentialExecutor() { throw new UnsupportedOperationException(); }
|
||||
@Override public long epoch() { return 0;}
|
||||
@Override public Node.Id id() { return Node.Id.NONE; }
|
||||
@Override public Timeouts timeouts() { return null; }
|
||||
|
|
@ -704,6 +707,8 @@ public class CommandsForKeySerializerTest
|
|||
@Override public DurabilityService durability() { return null; }
|
||||
@Override public long uniqueNow(long atLeast) { return 0; }
|
||||
@Override public TopologyManager topology() { return null; }
|
||||
@Override public long currentStamp() { return 0; }
|
||||
@Override public void updateStamp() { throw new UnsupportedOperationException(); }
|
||||
@Override public long now() { return 0; }
|
||||
@Override public long elapsed(TimeUnit unit) { return 0; }
|
||||
}; }
|
||||
|
|
|
|||
Loading…
Reference in New Issue