mirror of https://github.com/apache/cassandra
Make TopologyException a checked exception to ensure they are handled carefully, as they may occur at surprising times
Also Fix: - Restore MaxDecidedRX on replay - When catchup_on_start_exit_on_failure == false, should startup on any kind of failure, not only timeout - lazy vtable LIMIT clause regression - DurabilityService.onEpochRetired - Command.validate when uniqueHlc differs - Avoid unsafe publication of AccordExecutor to scheduledFastTasks - AccordCache hitRate metric names - use long for return type of DurationSpec.toNanoseconds - Repair without all replicas should not request all Accord replicas participate - ExecuteAtSerializer - SyncPoints should be coordinated in an epoch that contains the ranges Also Improve: - Split Accord startup into local+distributed, ensure we - Add logging to FetchDurableBefore on startup - Add randomised testing of AbstractLazyVirtualTable - Add validation of lazy virtual table key ordering - Don't send requests to faulty replicas - shrinkOrEvict large objects without holding lock - Accord dtest shutdown patch by Benedict; reviewed by Alex Petrov for CASSANDRA-21042
This commit is contained in:
parent
dbfcd52fd5
commit
4defbb9b1c
|
|
@ -1 +1 @@
|
||||||
Subproject commit 938ba19adaf70bf9901fb2dc177dce313d7ee15c
|
Subproject commit 973335bfe7e930c6646016a4a48cff084f49b660
|
||||||
|
|
@ -228,6 +228,7 @@ public enum CassandraRelevantProperties
|
||||||
DTEST_ACCORD_ENABLED("jvm_dtest.accord.enabled", "true"),
|
DTEST_ACCORD_ENABLED("jvm_dtest.accord.enabled", "true"),
|
||||||
DTEST_ACCORD_JOURNAL_SANITY_CHECK_ENABLED("jvm_dtest.accord.journal_sanity_check_enabled", "false"),
|
DTEST_ACCORD_JOURNAL_SANITY_CHECK_ENABLED("jvm_dtest.accord.journal_sanity_check_enabled", "false"),
|
||||||
DTEST_API_LOG_TOPOLOGY("cassandra.dtest.api.log.topology"),
|
DTEST_API_LOG_TOPOLOGY("cassandra.dtest.api.log.topology"),
|
||||||
|
DTEST_IGNORE_SHUTDOWN_THREADCOUNT("jvm_dtests.ignore_shutdown_threadcount"),
|
||||||
/** This property indicates if the code is running under the in-jvm dtest framework */
|
/** This property indicates if the code is running under the in-jvm dtest framework */
|
||||||
DTEST_IS_IN_JVM_DTEST("org.apache.cassandra.dtest.is_in_jvm_dtest"),
|
DTEST_IS_IN_JVM_DTEST("org.apache.cassandra.dtest.is_in_jvm_dtest"),
|
||||||
/** In_JVM dtest property indicating that the test should use "latest" configuration */
|
/** In_JVM dtest property indicating that the test should use "latest" configuration */
|
||||||
|
|
|
||||||
|
|
@ -587,13 +587,11 @@ public abstract class DurationSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns this duration in the number of nanoseconds as an {@code int}
|
* Returns this duration in the number of nanoseconds as a {@code long}
|
||||||
*
|
|
||||||
* @return this duration in number of nanoseconds or {@code Integer.MAX_VALUE} if the number of nanoseconds is too large.
|
|
||||||
*/
|
*/
|
||||||
public int toNanoseconds()
|
public long toNanoseconds()
|
||||||
{
|
{
|
||||||
return Ints.saturatedCast(unit().toNanos(quantity()));
|
return unit().toNanos(quantity());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,14 @@ public class CompactionIterator extends CompactionInfo.Holder implements Unfilte
|
||||||
TopPartitionTracker.Collector topPartitionCollector)
|
TopPartitionTracker.Collector topPartitionCollector)
|
||||||
{
|
{
|
||||||
this(type, scanners, controller, nowInSec, compactionId, activeCompactions, topPartitionCollector,
|
this(type, scanners, controller, nowInSec, compactionId, activeCompactions, topPartitionCollector,
|
||||||
AccordService.isSetup() ? AccordService.instance() : null);
|
accord(controller));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IAccordService accord(AbstractCompactionController controller)
|
||||||
|
{
|
||||||
|
IAccordService accord = AccordService.tryGetUnsafe();
|
||||||
|
Invariants.require(accord != null || (!isAccordJournal(controller.cfs) && !isAccordCommandsForKey(controller.cfs)));
|
||||||
|
return accord;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompactionIterator(OperationType type,
|
public CompactionIterator(OperationType type,
|
||||||
|
|
@ -194,7 +201,7 @@ public class CompactionIterator extends CompactionInfo.Holder implements Unfilte
|
||||||
IAccordService accord)
|
IAccordService accord)
|
||||||
{
|
{
|
||||||
this(type, scanners, controller, nowInSec, compactionId, activeCompactions, topPartitionCollector,
|
this(type, scanners, controller, nowInSec, compactionId, activeCompactions, topPartitionCollector,
|
||||||
() -> accord.getCompactionInfo(),
|
() -> Invariants.nonNull(accord).getCompactionInfo(),
|
||||||
() -> Version.fromVersion(accord.journalConfiguration().userVersion()));
|
() -> Version.fromVersion(accord.journalConfiguration().userVersion()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.NavigableMap;
|
import java.util.NavigableMap;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
@ -223,6 +224,8 @@ public abstract class AbstractLazyVirtualTable implements VirtualTable
|
||||||
if (!dataRange.contains(partitionKey))
|
if (!dataRange.contains(partitionKey))
|
||||||
return dropCks -> {};
|
return dropCks -> {};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return partitions.computeIfAbsent(partitionKey, SimplePartition::new);
|
return partitions.computeIfAbsent(partitionKey, SimplePartition::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -232,36 +235,50 @@ public abstract class AbstractLazyVirtualTable implements VirtualTable
|
||||||
final Iterator<SimplePartition> partitions = this.partitions.values().iterator();
|
final Iterator<SimplePartition> partitions = this.partitions.values().iterator();
|
||||||
return new UnfilteredPartitionIterator()
|
return new UnfilteredPartitionIterator()
|
||||||
{
|
{
|
||||||
|
private UnfilteredRowIterator next;
|
||||||
|
|
||||||
@Override public TableMetadata metadata() { return metadata; }
|
@Override public TableMetadata metadata() { return metadata; }
|
||||||
@Override public void close() {}
|
@Override public void close() {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext()
|
public boolean hasNext()
|
||||||
{
|
{
|
||||||
return partitions.hasNext();
|
while (next == null && partitions.hasNext())
|
||||||
|
{
|
||||||
|
SimplePartition partition = partitions.next();
|
||||||
|
Iterator<Row> rows = partition.rows();
|
||||||
|
|
||||||
|
if (!rows.hasNext())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
next = new UnfilteredRowIterator()
|
||||||
|
{
|
||||||
|
@Override public TableMetadata metadata() { return metadata; }
|
||||||
|
@Override public boolean isReverseOrder() { return dataRange.isReversed(); }
|
||||||
|
@Override public RegularAndStaticColumns columns() { return columnFilter.fetchedColumns(); }
|
||||||
|
@Override public DecoratedKey partitionKey() { return partition.key; }
|
||||||
|
|
||||||
|
@Override public Row staticRow() { return partition.staticRow(); }
|
||||||
|
@Override public boolean hasNext() { return rows.hasNext(); }
|
||||||
|
@Override public Unfiltered next() { return rows.next(); }
|
||||||
|
|
||||||
|
@Override public void close() {}
|
||||||
|
@Override public DeletionTime partitionLevelDeletion() { return DeletionTime.LIVE; }
|
||||||
|
@Override public EncodingStats stats() { return EncodingStats.NO_STATS; }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return next != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UnfilteredRowIterator next()
|
public UnfilteredRowIterator next()
|
||||||
{
|
{
|
||||||
SimplePartition partition = partitions.next();
|
if (!hasNext())
|
||||||
Iterator<Row> rows = partition.rows();
|
throw new NoSuchElementException();
|
||||||
|
|
||||||
return new UnfilteredRowIterator()
|
UnfilteredRowIterator result = next;
|
||||||
{
|
next = null;
|
||||||
@Override public TableMetadata metadata() { return metadata; }
|
return result;
|
||||||
@Override public boolean isReverseOrder() { return dataRange.isReversed(); }
|
|
||||||
@Override public RegularAndStaticColumns columns() { return columnFilter.fetchedColumns(); }
|
|
||||||
@Override public DecoratedKey partitionKey() { return partition.key; }
|
|
||||||
|
|
||||||
@Override public Row staticRow() { return partition.staticRow(); }
|
|
||||||
@Override public boolean hasNext() { return rows.hasNext(); }
|
|
||||||
@Override public Unfiltered next() { return rows.next(); }
|
|
||||||
|
|
||||||
@Override public void close() {}
|
|
||||||
@Override public DeletionTime partitionLevelDeletion() { return DeletionTime.LIVE; }
|
|
||||||
@Override public EncodingStats stats() { return EncodingStats.NO_STATS; }
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -312,9 +329,23 @@ public abstract class AbstractLazyVirtualTable implements VirtualTable
|
||||||
if (!dataRange.contains(partitionKey) || !dataRange.clusteringIndexFilter(partitionKey).selects(clustering))
|
if (!dataRange.contains(partitionKey) || !dataRange.clusteringIndexFilter(partitionKey).selects(clustering))
|
||||||
return drop -> {};
|
return drop -> {};
|
||||||
|
|
||||||
|
if (isSortedByPartitionKey)
|
||||||
|
checkCorrectlySorted(partitionKey);
|
||||||
|
|
||||||
return partitions.computeIfAbsent(partitionKey, SimplePartition::new).row(clustering);
|
return partitions.computeIfAbsent(partitionKey, SimplePartition::new).row(clustering);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkCorrectlySorted(DecoratedKey newPartitionKey)
|
||||||
|
{
|
||||||
|
if (partitions.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
DecoratedKey prevKey = partitions.lastKey();
|
||||||
|
int c = metadata.partitionKeyType.compare(prevKey.getKey(), newPartitionKey.getKey());
|
||||||
|
if (dataRange.isReversed() ? c < 0 : c > 0)
|
||||||
|
throw new IllegalArgumentException(Arrays.toString(composePartitionKeys(prevKey, metadata)) + (dataRange.isReversed() ? " < " : " > ") + Arrays.toString(composePartitionKeys(newPartitionKey, metadata)));
|
||||||
|
}
|
||||||
|
|
||||||
private final class SimplePartition implements PartitionCollector, RowsCollector
|
private final class SimplePartition implements PartitionCollector, RowsCollector
|
||||||
{
|
{
|
||||||
private final DecoratedKey key;
|
private final DecoratedKey key;
|
||||||
|
|
@ -323,6 +354,7 @@ public abstract class AbstractLazyVirtualTable implements VirtualTable
|
||||||
private int rowCount;
|
private int rowCount;
|
||||||
private SimpleRow staticRow;
|
private SimpleRow staticRow;
|
||||||
private boolean dropRows;
|
private boolean dropRows;
|
||||||
|
private boolean isSortedAndFiltered = true;
|
||||||
|
|
||||||
private SimplePartition(DecoratedKey key)
|
private SimplePartition(DecoratedKey key)
|
||||||
{
|
{
|
||||||
|
|
@ -346,6 +378,17 @@ public abstract class AbstractLazyVirtualTable implements VirtualTable
|
||||||
return row(decomposeClusterings(metadata, clusteringKeys));
|
return row(decomposeClusterings(metadata, clusteringKeys));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkCorrectlySorted(Clustering<?> newClustering)
|
||||||
|
{
|
||||||
|
if (rowCount == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Clustering<?> prevClustering = rows[rowCount - 1].clustering;
|
||||||
|
int c = metadata.comparator.compare(prevClustering, newClustering);
|
||||||
|
if (dataRange.isReversed() ? c <= 0 : c >= 0)
|
||||||
|
throw new IllegalArgumentException(Arrays.toString(composeClusterings(prevClustering, metadata)) + (dataRange.isReversed() ? " <= " : " >= ") + Arrays.toString(composeClusterings(newClustering, metadata)));
|
||||||
|
}
|
||||||
|
|
||||||
RowCollector row(Clustering<?> clustering)
|
RowCollector row(Clustering<?> clustering)
|
||||||
{
|
{
|
||||||
if (nanoTime() > deadlineNanos)
|
if (nanoTime() > deadlineNanos)
|
||||||
|
|
@ -354,6 +397,9 @@ public abstract class AbstractLazyVirtualTable implements VirtualTable
|
||||||
if (dropRows || !dataRange.clusteringIndexFilter(key).selects(clustering))
|
if (dropRows || !dataRange.clusteringIndexFilter(key).selects(clustering))
|
||||||
return drop -> {};
|
return drop -> {};
|
||||||
|
|
||||||
|
if (isSorted)
|
||||||
|
checkCorrectlySorted(clustering);
|
||||||
|
|
||||||
if (totalRowCount >= limits.count())
|
if (totalRowCount >= limits.count())
|
||||||
{
|
{
|
||||||
boolean filter;
|
boolean filter;
|
||||||
|
|
@ -370,9 +416,13 @@ public abstract class AbstractLazyVirtualTable implements VirtualTable
|
||||||
|
|
||||||
if (filter)
|
if (filter)
|
||||||
{
|
{
|
||||||
// first filter within each partition
|
|
||||||
for (SimplePartition partition : partitions.values())
|
for (SimplePartition partition : partitions.values())
|
||||||
|
{
|
||||||
|
// first filter within each partition
|
||||||
|
partition.filterAndSort();
|
||||||
|
// and truncate if there are per-partition limits
|
||||||
partition.truncate(limits.perPartitionCount());
|
partition.truncate(limits.perPartitionCount());
|
||||||
|
}
|
||||||
|
|
||||||
// then drop any partitions that completely fall outside our limit
|
// then drop any partitions that completely fall outside our limit
|
||||||
Iterator<SimplePartition> iter = partitions.descendingMap().values().iterator();
|
Iterator<SimplePartition> iter = partitions.descendingMap().values().iterator();
|
||||||
|
|
@ -418,12 +468,16 @@ public abstract class AbstractLazyVirtualTable implements VirtualTable
|
||||||
if (rowCount == rows.length)
|
if (rowCount == rows.length)
|
||||||
rows = Arrays.copyOf(rows, Math.max(8, rowCount * 2));
|
rows = Arrays.copyOf(rows, Math.max(8, rowCount * 2));
|
||||||
rows[rowCount++] = result;
|
rows[rowCount++] = result;
|
||||||
|
isSortedAndFiltered = false;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void filterAndSort()
|
void filterAndSort()
|
||||||
{
|
{
|
||||||
|
if (isSortedAndFiltered)
|
||||||
|
return;
|
||||||
|
|
||||||
int newCount = 0;
|
int newCount = 0;
|
||||||
for (int i = 0 ; i < rowCount; ++i)
|
for (int i = 0 ; i < rowCount; ++i)
|
||||||
{
|
{
|
||||||
|
|
@ -441,6 +495,7 @@ public abstract class AbstractLazyVirtualTable implements VirtualTable
|
||||||
rowCount = newCount;
|
rowCount = newCount;
|
||||||
}
|
}
|
||||||
Arrays.sort(rows, 0, newCount, rowComparator());
|
Arrays.sort(rows, 0, newCount, rowComparator());
|
||||||
|
isSortedAndFiltered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int truncate(int newCount)
|
int truncate(int newCount)
|
||||||
|
|
|
||||||
|
|
@ -946,7 +946,7 @@ public class AccordDebugKeyspace extends VirtualKeyspace
|
||||||
collector.partition(commandStore.id()).collect(collect -> {
|
collector.partition(commandStore.id()).collect(collect -> {
|
||||||
while (view.advance())
|
while (view.advance())
|
||||||
{
|
{
|
||||||
// TODO (required): view should return an immutable per-row view so that we can call lazyAdd
|
// TODO (desired): view should return an immutable per-row view so that we can call lazyAdd
|
||||||
collect.add(view.txnId().toString())
|
collect.add(view.txnId().toString())
|
||||||
.eagerCollect(columns -> {
|
.eagerCollect(columns -> {
|
||||||
columns.add("table_id", tableIdStr)
|
columns.add("table_id", tableIdStr)
|
||||||
|
|
@ -1516,7 +1516,7 @@ public class AccordDebugKeyspace extends VirtualKeyspace
|
||||||
|
|
||||||
AbstractJournalTable(TableMetadata metadata)
|
AbstractJournalTable(TableMetadata metadata)
|
||||||
{
|
{
|
||||||
super(metadata, FAIL, ASC);
|
super(metadata, FAIL, UNSORTED, ASC);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -466,7 +466,7 @@ public final class ActiveSegment<K, V> extends Segment<K, V>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO (required): Find a better way to test unwritten allocations and/or corruption
|
// TODO (expected): Find a better way to test unwritten allocations and/or corruption
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void consumeBufferUnsafe(Consumer<ByteBuffer> fn)
|
void consumeBufferUnsafe(Consumer<ByteBuffer> fn)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,9 @@ import java.util.Set;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import org.apache.cassandra.concurrent.ScheduledExecutorPlus;
|
import org.apache.cassandra.concurrent.ScheduledExecutorPlus;
|
||||||
import org.apache.cassandra.concurrent.Shutdownable;
|
import org.apache.cassandra.concurrent.Shutdownable;
|
||||||
|
|
||||||
|
|
@ -31,6 +34,8 @@ import static org.apache.cassandra.concurrent.ExecutorFactory.Global.executorFac
|
||||||
|
|
||||||
public final class Compactor<K, V> implements Runnable, Shutdownable
|
public final class Compactor<K, V> implements Runnable, Shutdownable
|
||||||
{
|
{
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(Compactor.class);
|
||||||
|
|
||||||
private final Journal<K, V> journal;
|
private final Journal<K, V> journal;
|
||||||
private final SegmentCompactor<K, V> segmentCompactor;
|
private final SegmentCompactor<K, V> segmentCompactor;
|
||||||
private final ScheduledExecutorPlus executor;
|
private final ScheduledExecutorPlus executor;
|
||||||
|
|
@ -99,6 +104,7 @@ public final class Compactor<K, V> implements Runnable, Shutdownable
|
||||||
@Override
|
@Override
|
||||||
public void shutdown()
|
public void shutdown()
|
||||||
{
|
{
|
||||||
|
logger.debug("Shutting down " + executor);
|
||||||
executor.shutdown();
|
executor.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -102,10 +102,12 @@ final class Flusher<K, V>
|
||||||
|
|
||||||
void shutdown() throws InterruptedException
|
void shutdown() throws InterruptedException
|
||||||
{
|
{
|
||||||
|
logger.debug("Shutting down " + flushExecutor + " and awaiting termination");
|
||||||
flushExecutor.shutdown();
|
flushExecutor.shutdown();
|
||||||
flushExecutor.awaitTermination(1, MINUTES);
|
flushExecutor.awaitTermination(1, MINUTES);
|
||||||
if (fsyncExecutor != null)
|
if (fsyncExecutor != null)
|
||||||
{
|
{
|
||||||
|
logger.debug("Shutting down " + fsyncExecutor + " and awaiting termination");
|
||||||
fsyncExecutor.shutdownNow(); // `now` to interrupt potentially parked runnable
|
fsyncExecutor.shutdownNow(); // `now` to interrupt potentially parked runnable
|
||||||
fsyncExecutor.awaitTermination(1, MINUTES);
|
fsyncExecutor.awaitTermination(1, MINUTES);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ public class Journal<K, V> implements Shutdownable
|
||||||
|
|
||||||
final AtomicReference<State> state = new AtomicReference<>(State.UNINITIALIZED);
|
final AtomicReference<State> state = new AtomicReference<>(State.UNINITIALIZED);
|
||||||
|
|
||||||
// TODO (required): we do not need wait queues here, we can just wait on a signal on a segment while its byte buffer is being allocated
|
// TODO (expected): we do not need wait queues here, we can just wait on a signal on a segment while its byte buffer is being allocated
|
||||||
private final WaitQueue segmentPrepared = newWaitQueue();
|
private final WaitQueue segmentPrepared = newWaitQueue();
|
||||||
private final WaitQueue allocatorThreadWaitQueue = newWaitQueue();
|
private final WaitQueue allocatorThreadWaitQueue = newWaitQueue();
|
||||||
private final BooleanSupplier allocatorThreadWaitCondition = () -> (availableSegment == null);
|
private final BooleanSupplier allocatorThreadWaitCondition = () -> (availableSegment == null);
|
||||||
|
|
@ -256,6 +256,7 @@ public class Journal<K, V> implements Shutdownable
|
||||||
{
|
{
|
||||||
Invariants.require(state.compareAndSet(State.NORMAL, State.SHUTDOWN),
|
Invariants.require(state.compareAndSet(State.NORMAL, State.SHUTDOWN),
|
||||||
"Unexpected journal state while trying to shut down", state);
|
"Unexpected journal state while trying to shut down", state);
|
||||||
|
logger.debug("Shutting down " + allocator + " and awaiting termination");
|
||||||
allocator.shutdown();
|
allocator.shutdown();
|
||||||
wakeAllocator(); // Wake allocator to force it into shutdown
|
wakeAllocator(); // Wake allocator to force it into shutdown
|
||||||
// TODO (expected): why are we awaitingTermination here when we have a separate method for it?
|
// TODO (expected): why are we awaitingTermination here when we have a separate method for it?
|
||||||
|
|
@ -265,6 +266,7 @@ public class Journal<K, V> implements Shutdownable
|
||||||
compactor.awaitTermination(1, TimeUnit.MINUTES);
|
compactor.awaitTermination(1, TimeUnit.MINUTES);
|
||||||
flusher.shutdown();
|
flusher.shutdown();
|
||||||
closeAllSegments();
|
closeAllSegments();
|
||||||
|
logger.debug("Shutting down " + releaser + " and " + closer + " and awaiting termination");
|
||||||
releaser.shutdown();
|
releaser.shutdown();
|
||||||
closer.shutdown();
|
closer.shutdown();
|
||||||
closer.awaitTermination(1, TimeUnit.MINUTES);
|
closer.awaitTermination(1, TimeUnit.MINUTES);
|
||||||
|
|
|
||||||
|
|
@ -94,13 +94,13 @@ public class AccordCacheMetrics
|
||||||
this.hits = Metrics.gauge(factory.createMetricName("Hits"), hitRate::totalHits);
|
this.hits = Metrics.gauge(factory.createMetricName("Hits"), hitRate::totalHits);
|
||||||
this.misses = Metrics.gauge(factory.createMetricName("Misses"), hitRate::totalMisses);
|
this.misses = Metrics.gauge(factory.createMetricName("Misses"), hitRate::totalMisses);
|
||||||
this.requests = Metrics.gauge(factory.createMetricName("Requests"), hitRate::totalRequests);
|
this.requests = Metrics.gauge(factory.createMetricName("Requests"), hitRate::totalRequests);
|
||||||
this.requestRate1m = Metrics.gauge(factory.createMetricName("Requests"), () -> hitRate.requestsPerSecond(1));
|
this.requestRate1m = Metrics.gauge(factory.createMetricName(RatioGaugeSet.ONE_MINUTE + "RequestRate"), () -> hitRate.requestsPerSecond(1));
|
||||||
this.requestRate5m = Metrics.gauge(factory.createMetricName("Requests"), () -> hitRate.requestsPerSecond(5));
|
this.requestRate5m = Metrics.gauge(factory.createMetricName(RatioGaugeSet.FIVE_MINUTE + "RequestRate"), () -> hitRate.requestsPerSecond(5));
|
||||||
this.requestRate15m = Metrics.gauge(factory.createMetricName(RatioGaugeSet.FIFTEEN_MINUTE + "RequestRate"), () -> hitRate.requestsPerSecond(15));
|
this.requestRate15m = Metrics.gauge(factory.createMetricName(RatioGaugeSet.FIFTEEN_MINUTE + "RequestRate"), () -> hitRate.requestsPerSecond(15));
|
||||||
this.hitRate1m = Metrics.gauge(factory.createMetricName(RatioGaugeSet.ONE_MINUTE + "HitRate"), () -> hitRate.hitRate(1));
|
this.hitRate1m = Metrics.gauge(factory.createMetricName(RatioGaugeSet.ONE_MINUTE + "HitRate"), () -> hitRate.hitRate(1));
|
||||||
this.hitRate5m = Metrics.gauge(factory.createMetricName(RatioGaugeSet.FIVE_MINUTE + "HitRate"), () -> hitRate.hitRate(5));
|
this.hitRate5m = Metrics.gauge(factory.createMetricName(RatioGaugeSet.FIVE_MINUTE + "HitRate"), () -> hitRate.hitRate(5));
|
||||||
this.hitRate15m = Metrics.gauge(factory.createMetricName(RatioGaugeSet.FIFTEEN_MINUTE + "HitRate"), () -> hitRate.hitRate(15));
|
this.hitRate15m = Metrics.gauge(factory.createMetricName(RatioGaugeSet.FIFTEEN_MINUTE + "HitRate"), () -> hitRate.hitRate(15));
|
||||||
this.hitRateAllTime = Metrics.gauge(factory.createMetricName("Misses"), hitRate::hitRateAllTime);
|
this.hitRateAllTime = Metrics.gauge(factory.createMetricName("HitRate"), hitRate::hitRateAllTime);
|
||||||
this.subTypeName = subTypeName;
|
this.subTypeName = subTypeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,9 +45,9 @@ public class AccordMetricUtils
|
||||||
|
|
||||||
static <I, T> Gauge<T> fromAccordService(Function<IAccordService, I> initialStep, Function<I, T> finalStep, T ifNotSetup)
|
static <I, T> Gauge<T> fromAccordService(Function<IAccordService, I> initialStep, Function<I, T> finalStep, T ifNotSetup)
|
||||||
{
|
{
|
||||||
if (AccordService.isSetup())
|
if (AccordService.isSetupOrStarting())
|
||||||
{
|
{
|
||||||
IAccordService service = AccordService.instance();
|
IAccordService service = AccordService.unsafeInstance();
|
||||||
if (!service.isEnabled())
|
if (!service.isEnabled())
|
||||||
return () -> ifNotSetup;
|
return () -> ifNotSetup;
|
||||||
I intermediate = initialStep.apply(service);
|
I intermediate = initialStep.apply(service);
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ public class AccordSystemMetrics
|
||||||
|
|
||||||
private AccordSystemMetrics()
|
private AccordSystemMetrics()
|
||||||
{
|
{
|
||||||
Invariants.expect(AccordService.isSetup());
|
Invariants.expect(AccordService.isSetupOrStarting());
|
||||||
DefaultNameFactory factory = new DefaultNameFactory(ACCORD_SYSTEM);
|
DefaultNameFactory factory = new DefaultNameFactory(ACCORD_SYSTEM);
|
||||||
minEpoch = Metrics.gauge(factory.createMetricName(MIN_EPOCH), fromTopologyManager(TopologyManager::minEpoch));
|
minEpoch = Metrics.gauge(factory.createMetricName(MIN_EPOCH), fromTopologyManager(TopologyManager::minEpoch));
|
||||||
maxEpoch = Metrics.gauge(factory.createMetricName(MAX_EPOCH), fromTopologyManager(TopologyManager::epoch));
|
maxEpoch = Metrics.gauge(factory.createMetricName(MAX_EPOCH), fromTopologyManager(TopologyManager::epoch));
|
||||||
|
|
@ -164,13 +164,13 @@ public class AccordSystemMetrics
|
||||||
|
|
||||||
private synchronized void refreshHistograms()
|
private synchronized void refreshHistograms()
|
||||||
{
|
{
|
||||||
if (!AccordService.isSetup())
|
if (!AccordService.isSetupOrStarting())
|
||||||
{
|
{
|
||||||
snapshot = new Snapshot();
|
snapshot = new Snapshot();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IAccordService service = AccordService.instance();
|
IAccordService service = AccordService.unsafeInstance();
|
||||||
if (!service.isEnabled())
|
if (!service.isEnabled())
|
||||||
{
|
{
|
||||||
snapshot = new Snapshot();
|
snapshot = new Snapshot();
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ public interface MessageDelivery
|
||||||
public void onFailure(InetAddressAndPort from, RequestFailure failure)
|
public void onFailure(InetAddressAndPort from, RequestFailure failure)
|
||||||
{
|
{
|
||||||
long retryDelay = backoff.computeWait(attempt + 1, NANOSECONDS);
|
long retryDelay = backoff.computeWait(attempt + 1, NANOSECONDS);
|
||||||
// TODO (required): we already have a separate retry predicate, retries should not be taken into consideration when retrying
|
// TODO (expected): we already have a separate retry predicate, retries should not be taken into consideration when retrying
|
||||||
if (retryDelay < 0)
|
if (retryDelay < 0)
|
||||||
{
|
{
|
||||||
onResult.result(attempt, null, new GivingUpException(attempt, errorMessage.apply(attempt, ResponseFailureReason.GiveUp, from, failure)));
|
onResult.result(attempt, null, new GivingUpException(attempt, errorMessage.apply(attempt, ResponseFailureReason.GiveUp, from, failure)));
|
||||||
|
|
|
||||||
|
|
@ -355,13 +355,13 @@ public enum Verb
|
||||||
ACCORD_GET_LATEST_DEPS_REQ (151, P2, readTimeout, IMMEDIATE, () -> accordEmbedded(LatestDepsSerializers.request), AccordService::requestHandlerOrNoop, ACCORD_GET_LATEST_DEPS_RSP),
|
ACCORD_GET_LATEST_DEPS_REQ (151, P2, readTimeout, IMMEDIATE, () -> accordEmbedded(LatestDepsSerializers.request), AccordService::requestHandlerOrNoop, ACCORD_GET_LATEST_DEPS_RSP),
|
||||||
ACCORD_GET_MAX_CONFLICT_RSP (152, P2, readTimeout, IMMEDIATE, () -> accordEmbedded(GetMaxConflictSerializers.reply), AccordService::responseHandlerOrNoop ),
|
ACCORD_GET_MAX_CONFLICT_RSP (152, P2, readTimeout, IMMEDIATE, () -> accordEmbedded(GetMaxConflictSerializers.reply), AccordService::responseHandlerOrNoop ),
|
||||||
ACCORD_GET_MAX_CONFLICT_REQ (153, P2, readTimeout, IMMEDIATE, () -> accordEmbedded(GetMaxConflictSerializers.request), AccordService::requestHandlerOrNoop, ACCORD_GET_MAX_CONFLICT_RSP),
|
ACCORD_GET_MAX_CONFLICT_REQ (153, P2, readTimeout, IMMEDIATE, () -> accordEmbedded(GetMaxConflictSerializers.request), AccordService::requestHandlerOrNoop, ACCORD_GET_MAX_CONFLICT_RSP),
|
||||||
ACCORD_GET_DURABLE_BEFORE_RSP (154, P2, readTimeout, MISC, () -> accordEmbedded(GetDurableBeforeSerializers.reply), AccordService::responseHandlerOrNoop ),
|
ACCORD_GET_DURABLE_BEFORE_RSP (154, P2, rpcTimeout, IMMEDIATE, () -> accordEmbedded(GetDurableBeforeSerializers.reply), AccordService::responseHandlerOrNoop ),
|
||||||
ACCORD_GET_DURABLE_BEFORE_REQ (155, P2, readTimeout, MISC, () -> accordEmbedded(GetDurableBeforeSerializers.request), AccordService::requestHandlerOrNoop, ACCORD_GET_DURABLE_BEFORE_RSP ),
|
ACCORD_GET_DURABLE_BEFORE_REQ (155, P2, rpcTimeout, IMMEDIATE, () -> accordEmbedded(GetDurableBeforeSerializers.request), AccordService::requestHandlerOrNoop, ACCORD_GET_DURABLE_BEFORE_RSP ),
|
||||||
ACCORD_SET_SHARD_DURABLE_REQ (156, P2, rpcTimeout, MISC, () -> accordEmbedded(SetDurableSerializers.shardDurable), AccordService::requestHandlerOrNoop, ACCORD_SIMPLE_RSP ),
|
ACCORD_SET_SHARD_DURABLE_REQ (156, P2, rpcTimeout, IMMEDIATE, () -> accordEmbedded(SetDurableSerializers.shardDurable), AccordService::requestHandlerOrNoop, ACCORD_SIMPLE_RSP ),
|
||||||
ACCORD_SET_GLOBALLY_DURABLE_REQ (157, P2, rpcTimeout, MISC, () -> accordEmbedded(SetDurableSerializers.globallyDurable),AccordService::requestHandlerOrNoop, ACCORD_SIMPLE_RSP ),
|
ACCORD_SET_GLOBALLY_DURABLE_REQ (157, P2, rpcTimeout, IMMEDIATE, () -> accordEmbedded(SetDurableSerializers.globallyDurable),AccordService::requestHandlerOrNoop, ACCORD_SIMPLE_RSP ),
|
||||||
|
|
||||||
ACCORD_SYNC_NOTIFY_RSP (158, P2, writeTimeout, MISC, () -> accordEmbedded(EnumSerializer.simpleReply), RESPONSE_HANDLER),
|
ACCORD_SYNC_NOTIFY_RSP (158, P2, writeTimeout, MISC, () -> accordEmbedded(EnumSerializer.simpleReply), RESPONSE_HANDLER),
|
||||||
ACCORD_SYNC_NOTIFY_REQ (159, P2, writeTimeout, MISC, () -> accordEmbedded(Notification.serializer), () -> AccordSyncPropagator.verbHandler, ACCORD_SYNC_NOTIFY_RSP ),
|
ACCORD_SYNC_NOTIFY_REQ (159, P2, writeTimeout, MISC, () -> accordEmbedded(Notification.serializer), () -> AccordSyncPropagator.verbHandler, ACCORD_SYNC_NOTIFY_RSP ),
|
||||||
|
|
||||||
CONSENSUS_KEY_MIGRATION (160, P1, writeTimeout, MISC, () -> accordEmbedded(ConsensusKeyMigrationFinished.serializer),() -> ConsensusKeyMigrationState.consensusKeyMigrationFinishedHandler),
|
CONSENSUS_KEY_MIGRATION (160, P1, writeTimeout, MISC, () -> accordEmbedded(ConsensusKeyMigrationFinished.serializer),() -> ConsensusKeyMigrationState.consensusKeyMigrationFinishedHandler),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import org.apache.cassandra.concurrent.ExecutorPlus;
|
||||||
import org.apache.cassandra.dht.Range;
|
import org.apache.cassandra.dht.Range;
|
||||||
import org.apache.cassandra.dht.Token;
|
import org.apache.cassandra.dht.Token;
|
||||||
import org.apache.cassandra.locator.InetAddressAndPort;
|
import org.apache.cassandra.locator.InetAddressAndPort;
|
||||||
|
import org.apache.cassandra.repair.RepairCoordinator.NeighborsAndRanges;
|
||||||
import org.apache.cassandra.repair.messages.RepairOption;
|
import org.apache.cassandra.repair.messages.RepairOption;
|
||||||
import org.apache.cassandra.utils.TimeUUID;
|
import org.apache.cassandra.utils.TimeUUID;
|
||||||
import org.apache.cassandra.utils.concurrent.Future;
|
import org.apache.cassandra.utils.concurrent.Future;
|
||||||
|
|
@ -44,13 +45,15 @@ public abstract class AbstractRepairTask implements RepairTask
|
||||||
protected final InetAddressAndPort broadcastAddressAndPort;
|
protected final InetAddressAndPort broadcastAddressAndPort;
|
||||||
protected final RepairOption options;
|
protected final RepairOption options;
|
||||||
protected final String keyspace;
|
protected final String keyspace;
|
||||||
|
protected final NeighborsAndRanges neighborsAndRanges;
|
||||||
|
|
||||||
protected AbstractRepairTask(RepairCoordinator coordinator)
|
protected AbstractRepairTask(RepairCoordinator coordinator, NeighborsAndRanges neighborsAndRanges)
|
||||||
{
|
{
|
||||||
this.coordinator = Objects.requireNonNull(coordinator);
|
this.coordinator = Objects.requireNonNull(coordinator);
|
||||||
this.broadcastAddressAndPort = coordinator.ctx.broadcastAddressAndPort();
|
this.broadcastAddressAndPort = coordinator.ctx.broadcastAddressAndPort();
|
||||||
this.options = Objects.requireNonNull(coordinator.state.options);
|
this.options = Objects.requireNonNull(coordinator.state.options);
|
||||||
this.keyspace = Objects.requireNonNull(coordinator.state.keyspace);
|
this.keyspace = Objects.requireNonNull(coordinator.state.keyspace);
|
||||||
|
this.neighborsAndRanges = neighborsAndRanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<RepairSession> submitRepairSessions(TimeUUID parentSession,
|
private List<RepairSession> submitRepairSessions(TimeUUID parentSession,
|
||||||
|
|
@ -71,6 +74,7 @@ public abstract class AbstractRepairTask implements RepairTask
|
||||||
excludedDeadNodes,
|
excludedDeadNodes,
|
||||||
keyspace,
|
keyspace,
|
||||||
options.getParallelism(),
|
options.getParallelism(),
|
||||||
|
neighborsAndRanges.includesAllReplicas,
|
||||||
isIncremental,
|
isIncremental,
|
||||||
options.isPullRepair(),
|
options.isPullRepair(),
|
||||||
options.getPreviewKind(),
|
options.getPreviewKind(),
|
||||||
|
|
@ -79,6 +83,7 @@ public abstract class AbstractRepairTask implements RepairTask
|
||||||
options.repairPaxos(),
|
options.repairPaxos(),
|
||||||
options.dontPurgeTombstones(),
|
options.dontPurgeTombstones(),
|
||||||
options.repairAccord(),
|
options.repairAccord(),
|
||||||
|
options.permitNoQuorum(),
|
||||||
executor,
|
executor,
|
||||||
validationScheduler,
|
validationScheduler,
|
||||||
cfnames);
|
cfnames);
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ import org.apache.cassandra.utils.concurrent.Future;
|
||||||
public class IncrementalRepairTask extends AbstractRepairTask
|
public class IncrementalRepairTask extends AbstractRepairTask
|
||||||
{
|
{
|
||||||
private final TimeUUID parentSession;
|
private final TimeUUID parentSession;
|
||||||
private final RepairCoordinator.NeighborsAndRanges neighborsAndRanges;
|
|
||||||
private final String[] cfnames;
|
private final String[] cfnames;
|
||||||
|
|
||||||
protected IncrementalRepairTask(RepairCoordinator coordinator,
|
protected IncrementalRepairTask(RepairCoordinator coordinator,
|
||||||
|
|
@ -39,9 +38,8 @@ public class IncrementalRepairTask extends AbstractRepairTask
|
||||||
RepairCoordinator.NeighborsAndRanges neighborsAndRanges,
|
RepairCoordinator.NeighborsAndRanges neighborsAndRanges,
|
||||||
String[] cfnames)
|
String[] cfnames)
|
||||||
{
|
{
|
||||||
super(coordinator);
|
super(coordinator, neighborsAndRanges);
|
||||||
this.parentSession = parentSession;
|
this.parentSession = parentSession;
|
||||||
this.neighborsAndRanges = neighborsAndRanges;
|
|
||||||
this.cfnames = cfnames;
|
this.cfnames = cfnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,7 +58,7 @@ public class IncrementalRepairTask extends AbstractRepairTask
|
||||||
.add(broadcastAddressAndPort)
|
.add(broadcastAddressAndPort)
|
||||||
.build();
|
.build();
|
||||||
// Not necessary to include self for filtering. The common ranges only contains neighbhor node endpoints.
|
// Not necessary to include self for filtering. The common ranges only contains neighbhor node endpoints.
|
||||||
List<CommonRange> allRanges = neighborsAndRanges.filterCommonRanges(keyspace, cfnames);
|
List<CommonRange> allRanges = neighborsAndRanges.filterCommonRanges(keyspace, cfnames).commonRanges;
|
||||||
|
|
||||||
CoordinatorSession coordinatorSession = coordinator.ctx.repair().consistent.coordinated.registerSession(parentSession, allParticipants, neighborsAndRanges.shouldExcludeDeadParticipants);
|
CoordinatorSession coordinatorSession = coordinator.ctx.repair().consistent.coordinated.registerSession(parentSession, allParticipants, neighborsAndRanges.shouldExcludeDeadParticipants);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,6 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.cassandra.repair;
|
package org.apache.cassandra.repair;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.apache.cassandra.concurrent.ExecutorPlus;
|
import org.apache.cassandra.concurrent.ExecutorPlus;
|
||||||
import org.apache.cassandra.utils.TimeUUID;
|
import org.apache.cassandra.utils.TimeUUID;
|
||||||
import org.apache.cassandra.utils.concurrent.Future;
|
import org.apache.cassandra.utils.concurrent.Future;
|
||||||
|
|
@ -26,20 +24,15 @@ import org.apache.cassandra.utils.concurrent.Future;
|
||||||
public class NormalRepairTask extends AbstractRepairTask
|
public class NormalRepairTask extends AbstractRepairTask
|
||||||
{
|
{
|
||||||
private final TimeUUID parentSession;
|
private final TimeUUID parentSession;
|
||||||
private final List<CommonRange> commonRanges;
|
|
||||||
private final boolean excludedDeadNodes;
|
|
||||||
private final String[] cfnames;
|
private final String[] cfnames;
|
||||||
|
|
||||||
protected NormalRepairTask(RepairCoordinator coordinator,
|
protected NormalRepairTask(RepairCoordinator coordinator,
|
||||||
TimeUUID parentSession,
|
TimeUUID parentSession,
|
||||||
List<CommonRange> commonRanges,
|
RepairCoordinator.NeighborsAndRanges neighborsAndRanges,
|
||||||
boolean excludedDeadNodes,
|
|
||||||
String[] cfnames)
|
String[] cfnames)
|
||||||
{
|
{
|
||||||
super(coordinator);
|
super(coordinator, neighborsAndRanges);
|
||||||
this.parentSession = parentSession;
|
this.parentSession = parentSession;
|
||||||
this.commonRanges = commonRanges;
|
|
||||||
this.excludedDeadNodes = excludedDeadNodes;
|
|
||||||
this.cfnames = cfnames;
|
this.cfnames = cfnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,6 +45,6 @@ public class NormalRepairTask extends AbstractRepairTask
|
||||||
@Override
|
@Override
|
||||||
public Future<CoordinatedRepairResult> performUnsafe(ExecutorPlus executor, Scheduler validationScheduler)
|
public Future<CoordinatedRepairResult> performUnsafe(ExecutorPlus executor, Scheduler validationScheduler)
|
||||||
{
|
{
|
||||||
return runRepair(parentSession, false, executor, validationScheduler, commonRanges, excludedDeadNodes, cfnames);
|
return runRepair(parentSession, false, executor, validationScheduler, neighborsAndRanges.commonRanges, neighborsAndRanges.shouldExcludeDeadParticipants, cfnames);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,17 +42,13 @@ import org.apache.cassandra.utils.concurrent.Future;
|
||||||
public class PreviewRepairTask extends AbstractRepairTask
|
public class PreviewRepairTask extends AbstractRepairTask
|
||||||
{
|
{
|
||||||
private final TimeUUID parentSession;
|
private final TimeUUID parentSession;
|
||||||
private final List<CommonRange> commonRanges;
|
|
||||||
private final boolean excludedDeadNodes;
|
|
||||||
private final String[] cfnames;
|
private final String[] cfnames;
|
||||||
private volatile String successMessage = name() + " completed successfully";
|
private volatile String successMessage = name() + " completed successfully";
|
||||||
|
|
||||||
protected PreviewRepairTask(RepairCoordinator coordinator, TimeUUID parentSession, List<CommonRange> commonRanges, boolean excludedDeadNodes, String[] cfnames)
|
protected PreviewRepairTask(RepairCoordinator coordinator, TimeUUID parentSession, RepairCoordinator.NeighborsAndRanges neighborsAndRanges, String[] cfnames)
|
||||||
{
|
{
|
||||||
super(coordinator);
|
super(coordinator, neighborsAndRanges);
|
||||||
this.parentSession = parentSession;
|
this.parentSession = parentSession;
|
||||||
this.commonRanges = commonRanges;
|
|
||||||
this.excludedDeadNodes = excludedDeadNodes;
|
|
||||||
this.cfnames = cfnames;
|
this.cfnames = cfnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,7 +67,7 @@ public class PreviewRepairTask extends AbstractRepairTask
|
||||||
@Override
|
@Override
|
||||||
public Future<CoordinatedRepairResult> performUnsafe(ExecutorPlus executor, Scheduler validationScheduler)
|
public Future<CoordinatedRepairResult> performUnsafe(ExecutorPlus executor, Scheduler validationScheduler)
|
||||||
{
|
{
|
||||||
Future<CoordinatedRepairResult> f = runRepair(parentSession, false, executor, validationScheduler, commonRanges, excludedDeadNodes, cfnames);
|
Future<CoordinatedRepairResult> f = runRepair(parentSession, false, executor, validationScheduler, neighborsAndRanges.commonRanges, neighborsAndRanges.shouldExcludeDeadParticipants, cfnames);
|
||||||
return f.map(result -> {
|
return f.map(result -> {
|
||||||
if (result.hasFailed())
|
if (result.hasFailed())
|
||||||
return result;
|
return result;
|
||||||
|
|
|
||||||
|
|
@ -394,6 +394,7 @@ public class RepairCoordinator implements Runnable, ProgressEventNotifier, Repai
|
||||||
private NeighborsAndRanges getNeighborsAndRanges() throws RepairException
|
private NeighborsAndRanges getNeighborsAndRanges() throws RepairException
|
||||||
{
|
{
|
||||||
Set<InetAddressAndPort> allNeighbors = new HashSet<>();
|
Set<InetAddressAndPort> allNeighbors = new HashSet<>();
|
||||||
|
Set<InetAddressAndPort> includeNeighbors = new HashSet<>();
|
||||||
List<CommonRange> commonRanges = new ArrayList<>();
|
List<CommonRange> commonRanges = new ArrayList<>();
|
||||||
|
|
||||||
//pre-calculate output of getLocalReplicas and pass it to getNeighbors to increase performance and prevent
|
//pre-calculate output of getLocalReplicas and pass it to getNeighbors to increase performance and prevent
|
||||||
|
|
@ -403,10 +404,14 @@ public class RepairCoordinator implements Runnable, ProgressEventNotifier, Repai
|
||||||
boolean isCMS = ClusterMetadata.current().isCMSMember(FBUtilities.getBroadcastAddressAndPort());
|
boolean isCMS = ClusterMetadata.current().isCMSMember(FBUtilities.getBroadcastAddressAndPort());
|
||||||
for (Range<Token> range : state.options.getRanges())
|
for (Range<Token> range : state.options.getRanges())
|
||||||
{
|
{
|
||||||
EndpointsForRange neighbors = ctx.repair().getNeighbors(state.keyspace, keyspaceLocalRanges, range,
|
EndpointsForRange allForRange = ctx.repair().getNeighbors(state.keyspace, keyspaceLocalRanges, range);
|
||||||
state.options.getDataCenters(),
|
allNeighbors.addAll(allForRange.endpoints());
|
||||||
state.options.getHosts());
|
|
||||||
if (neighbors.isEmpty())
|
EndpointsForRange includeForRange = ctx.repair().filterNeighbors(allForRange, range,
|
||||||
|
state.options.getDataCenters(),
|
||||||
|
state.options.getHosts());
|
||||||
|
|
||||||
|
if (includeForRange.isEmpty())
|
||||||
{
|
{
|
||||||
if (state.options.ignoreUnreplicatedKeyspaces())
|
if (state.options.ignoreUnreplicatedKeyspaces())
|
||||||
{
|
{
|
||||||
|
|
@ -423,11 +428,11 @@ public class RepairCoordinator implements Runnable, ProgressEventNotifier, Repai
|
||||||
throw RepairException.warn(String.format("Nothing to repair for %s in %s - aborting", range, state.keyspace));
|
throw RepairException.warn(String.format("Nothing to repair for %s in %s - aborting", range, state.keyspace));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addRangeToNeighbors(commonRanges, range, neighbors);
|
addRangeToNeighbors(commonRanges, range, includeForRange);
|
||||||
allNeighbors.addAll(neighbors.endpoints());
|
includeNeighbors.addAll(includeForRange.endpoints());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allNeighbors.isEmpty())
|
if (includeNeighbors.isEmpty())
|
||||||
{
|
{
|
||||||
if (state.options.ignoreUnreplicatedKeyspaces())
|
if (state.options.ignoreUnreplicatedKeyspaces())
|
||||||
{
|
{
|
||||||
|
|
@ -447,11 +452,12 @@ public class RepairCoordinator implements Runnable, ProgressEventNotifier, Repai
|
||||||
|
|
||||||
if (shouldExcludeDeadParticipants)
|
if (shouldExcludeDeadParticipants)
|
||||||
{
|
{
|
||||||
Set<InetAddressAndPort> actualNeighbors = Sets.newHashSet(Iterables.filter(allNeighbors, ctx.failureDetector()::isAlive));
|
Set<InetAddressAndPort> actualNeighbors = Sets.newHashSet(Iterables.filter(includeNeighbors, ctx.failureDetector()::isAlive));
|
||||||
shouldExcludeDeadParticipants = !allNeighbors.equals(actualNeighbors);
|
shouldExcludeDeadParticipants = !includeNeighbors.equals(actualNeighbors);
|
||||||
allNeighbors = actualNeighbors;
|
if (shouldExcludeDeadParticipants) includeNeighbors = actualNeighbors;
|
||||||
|
else logger.info("{} all replicas {} considered up and healthy; clearing force flag for this job", state.id, includeNeighbors);
|
||||||
}
|
}
|
||||||
return new NeighborsAndRanges(shouldExcludeDeadParticipants, allNeighbors, commonRanges);
|
return new NeighborsAndRanges(shouldExcludeDeadParticipants, includeNeighbors.containsAll(allNeighbors), includeNeighbors, commonRanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeStoreParentRepairStart(String[] cfnames)
|
private void maybeStoreParentRepairStart(String[] cfnames)
|
||||||
|
|
@ -496,7 +502,7 @@ public class RepairCoordinator implements Runnable, ProgressEventNotifier, Repai
|
||||||
RepairTask task;
|
RepairTask task;
|
||||||
if (state.options.isPreview())
|
if (state.options.isPreview())
|
||||||
{
|
{
|
||||||
task = new PreviewRepairTask(this, state.id, neighborsAndRanges.filterCommonRanges(state.keyspace, cfnames), neighborsAndRanges.shouldExcludeDeadParticipants, cfnames);
|
task = new PreviewRepairTask(this, state.id, neighborsAndRanges.filterCommonRanges(state.keyspace, cfnames), cfnames);
|
||||||
}
|
}
|
||||||
else if (state.options.isIncremental())
|
else if (state.options.isIncremental())
|
||||||
{
|
{
|
||||||
|
|
@ -504,7 +510,7 @@ public class RepairCoordinator implements Runnable, ProgressEventNotifier, Repai
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
task = new NormalRepairTask(this, state.id, neighborsAndRanges.filterCommonRanges(state.keyspace, cfnames), neighborsAndRanges.shouldExcludeDeadParticipants, cfnames);
|
task = new NormalRepairTask(this, state.id, neighborsAndRanges.filterCommonRanges(state.keyspace, cfnames), cfnames);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecutorPlus executor = createExecutor();
|
ExecutorPlus executor = createExecutor();
|
||||||
|
|
@ -635,12 +641,14 @@ public class RepairCoordinator implements Runnable, ProgressEventNotifier, Repai
|
||||||
public static final class NeighborsAndRanges
|
public static final class NeighborsAndRanges
|
||||||
{
|
{
|
||||||
final boolean shouldExcludeDeadParticipants;
|
final boolean shouldExcludeDeadParticipants;
|
||||||
|
public final boolean includesAllReplicas;
|
||||||
public final Set<InetAddressAndPort> participants;
|
public final Set<InetAddressAndPort> participants;
|
||||||
public final List<CommonRange> commonRanges;
|
public final List<CommonRange> commonRanges;
|
||||||
|
|
||||||
public NeighborsAndRanges(boolean shouldExcludeDeadParticipants, Set<InetAddressAndPort> participants, List<CommonRange> commonRanges)
|
public NeighborsAndRanges(boolean shouldExcludeDeadParticipants, boolean includesAllReplicas, Set<InetAddressAndPort> participants, List<CommonRange> commonRanges)
|
||||||
{
|
{
|
||||||
this.shouldExcludeDeadParticipants = shouldExcludeDeadParticipants;
|
this.shouldExcludeDeadParticipants = shouldExcludeDeadParticipants;
|
||||||
|
this.includesAllReplicas = includesAllReplicas;
|
||||||
this.participants = participants;
|
this.participants = participants;
|
||||||
this.commonRanges = commonRanges;
|
this.commonRanges = commonRanges;
|
||||||
}
|
}
|
||||||
|
|
@ -650,11 +658,11 @@ public class RepairCoordinator implements Runnable, ProgressEventNotifier, Repai
|
||||||
* and exludes ranges left without any participants
|
* and exludes ranges left without any participants
|
||||||
* When not in the force mode, no-op.
|
* When not in the force mode, no-op.
|
||||||
*/
|
*/
|
||||||
public List<CommonRange> filterCommonRanges(String keyspace, String[] tableNames)
|
public NeighborsAndRanges filterCommonRanges(String keyspace, String[] tableNames)
|
||||||
{
|
{
|
||||||
if (!shouldExcludeDeadParticipants)
|
if (!shouldExcludeDeadParticipants)
|
||||||
{
|
{
|
||||||
return commonRanges;
|
return this;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -682,7 +690,7 @@ public class RepairCoordinator implements Runnable, ProgressEventNotifier, Repai
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Preconditions.checkState(!filtered.isEmpty(), "Not enough live endpoints for a repair");
|
Preconditions.checkState(!filtered.isEmpty(), "Not enough live endpoints for a repair");
|
||||||
return filtered;
|
return new NeighborsAndRanges(shouldExcludeDeadParticipants, includesAllReplicas, participants, filtered);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ import com.google.common.util.concurrent.FutureCallback;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import accord.local.durability.DurabilityService.SyncRemote;
|
||||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
import org.apache.cassandra.db.ColumnFamilyStore;
|
import org.apache.cassandra.db.ColumnFamilyStore;
|
||||||
import org.apache.cassandra.db.Keyspace;
|
import org.apache.cassandra.db.Keyspace;
|
||||||
|
|
@ -70,6 +71,9 @@ import org.apache.cassandra.utils.concurrent.Future;
|
||||||
import org.apache.cassandra.utils.concurrent.FutureCombiner;
|
import org.apache.cassandra.utils.concurrent.FutureCombiner;
|
||||||
import org.apache.cassandra.utils.concurrent.ImmediateFuture;
|
import org.apache.cassandra.utils.concurrent.ImmediateFuture;
|
||||||
|
|
||||||
|
import static accord.local.durability.DurabilityService.SyncRemote.All;
|
||||||
|
import static accord.local.durability.DurabilityService.SyncRemote.NoRemote;
|
||||||
|
import static accord.local.durability.DurabilityService.SyncRemote.Quorum;
|
||||||
import static com.google.common.util.concurrent.Futures.getUnchecked;
|
import static com.google.common.util.concurrent.Futures.getUnchecked;
|
||||||
import static org.apache.cassandra.config.DatabaseDescriptor.paxosRepairEnabled;
|
import static org.apache.cassandra.config.DatabaseDescriptor.paxosRepairEnabled;
|
||||||
import static org.apache.cassandra.schema.SchemaConstants.METADATA_KEYSPACE_NAME;
|
import static org.apache.cassandra.schema.SchemaConstants.METADATA_KEYSPACE_NAME;
|
||||||
|
|
@ -179,20 +183,18 @@ public class RepairJob extends AsyncFuture<RepairResult> implements Runnable
|
||||||
if (doAccordRepair)
|
if (doAccordRepair)
|
||||||
{
|
{
|
||||||
accordRepair = paxosRepair.flatMap(unused -> {
|
accordRepair = paxosRepair.flatMap(unused -> {
|
||||||
boolean requireAllEndpoints;
|
SyncRemote sync;
|
||||||
// If the session excluded dead nodes it's not eligible for migration and is not supposed to occur at ALL anyways
|
// If the session is doing a data repair (which flushes sstables if not incremental) we can do the barriers at QUORUM
|
||||||
if (session.excludedDeadNodes)
|
if (session.excludedDeadNodes || !session.allReplicas || (session.repairData && !session.isIncremental))
|
||||||
requireAllEndpoints = false;
|
{
|
||||||
|
sync = session.permitNoQuorum ? NoRemote : Quorum;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If the session is doing a data repair (which flushes sstables if not incremental) we can do the barriers at QUORUM
|
sync = All;
|
||||||
if (session.repairData && !session.isIncremental)
|
|
||||||
requireAllEndpoints = false;
|
|
||||||
else
|
|
||||||
requireAllEndpoints = true;
|
|
||||||
}
|
}
|
||||||
logger.info("{} {}.{} starting accord repair, require all endpoints {}", session.previewKind.logPrefix(session.getId()), desc.keyspace, desc.columnFamily, requireAllEndpoints);
|
logger.info("{} {}.{} starting accord repair, sync {}", session.previewKind.logPrefix(session.getId()), desc.keyspace, desc.columnFamily, sync);
|
||||||
AccordRepair repair = new AccordRepair(ctx, cfs, desc.sessionId, desc.keyspace, desc.ranges, requireAllEndpoints, allEndpoints);
|
AccordRepair repair = new AccordRepair(ctx, cfs, desc.sessionId, desc.keyspace, desc.ranges, sync, sync == All ? null : allEndpoints);
|
||||||
return repair.repair(taskExecutor).flatMap(accordRepairResult -> {
|
return repair.repair(taskExecutor).flatMap(accordRepairResult -> {
|
||||||
// Propagate the HLC discovered during Accord repair to Paxos so Paxos doesn't use ballots < Accord has already used
|
// Propagate the HLC discovered during Accord repair to Paxos so Paxos doesn't use ballots < Accord has already used
|
||||||
if (accordRepairResult.maxHlc != IAccordService.NO_HLC)
|
if (accordRepairResult.maxHlc != IAccordService.NO_HLC)
|
||||||
|
|
|
||||||
|
|
@ -119,12 +119,14 @@ public class RepairSession extends AsyncFuture<RepairSessionResult> implements I
|
||||||
|
|
||||||
/** Range to repair */
|
/** Range to repair */
|
||||||
public final boolean isIncremental;
|
public final boolean isIncremental;
|
||||||
|
public final boolean allReplicas;
|
||||||
public final PreviewKind previewKind;
|
public final PreviewKind previewKind;
|
||||||
public final boolean repairData;
|
public final boolean repairData;
|
||||||
public final boolean repairPaxos; // TODO (now): rename to repairPaxosIfSupported
|
public final boolean repairPaxos;
|
||||||
public final boolean repairAccord;
|
public final boolean repairAccord;
|
||||||
public final boolean dontPurgeTombstones;
|
public final boolean dontPurgeTombstones;
|
||||||
public final boolean excludedDeadNodes;
|
public final boolean excludedDeadNodes;
|
||||||
|
public final boolean permitNoQuorum;
|
||||||
|
|
||||||
private final AtomicBoolean isFailed = new AtomicBoolean(false);
|
private final AtomicBoolean isFailed = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
|
@ -161,6 +163,7 @@ public class RepairSession extends AsyncFuture<RepairSessionResult> implements I
|
||||||
boolean excludedDeadNodes,
|
boolean excludedDeadNodes,
|
||||||
String keyspace,
|
String keyspace,
|
||||||
RepairParallelism parallelismDegree,
|
RepairParallelism parallelismDegree,
|
||||||
|
boolean allReplicas,
|
||||||
boolean isIncremental,
|
boolean isIncremental,
|
||||||
boolean pullRepair,
|
boolean pullRepair,
|
||||||
PreviewKind previewKind,
|
PreviewKind previewKind,
|
||||||
|
|
@ -168,7 +171,7 @@ public class RepairSession extends AsyncFuture<RepairSessionResult> implements I
|
||||||
boolean repairData,
|
boolean repairData,
|
||||||
boolean repairPaxos,
|
boolean repairPaxos,
|
||||||
boolean dontPurgeTombstones,
|
boolean dontPurgeTombstones,
|
||||||
boolean repairAccord,
|
boolean repairAccord, boolean permitNoQuorum,
|
||||||
String... cfnames)
|
String... cfnames)
|
||||||
{
|
{
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
|
|
@ -176,12 +179,14 @@ public class RepairSession extends AsyncFuture<RepairSessionResult> implements I
|
||||||
this.repairData = repairData;
|
this.repairData = repairData;
|
||||||
this.repairPaxos = repairPaxos;
|
this.repairPaxos = repairPaxos;
|
||||||
this.repairAccord = repairAccord;
|
this.repairAccord = repairAccord;
|
||||||
|
this.permitNoQuorum = permitNoQuorum;
|
||||||
assert cfnames.length > 0 : "Repairing no column families seems pointless, doesn't it";
|
assert cfnames.length > 0 : "Repairing no column families seems pointless, doesn't it";
|
||||||
this.state = new SessionState(ctx, parentRepairSession, keyspace, cfnames, commonRange);
|
this.state = new SessionState(ctx, parentRepairSession, keyspace, cfnames, commonRange);
|
||||||
this.parallelismDegree = parallelismDegree;
|
this.parallelismDegree = parallelismDegree;
|
||||||
this.isIncremental = isIncremental;
|
this.isIncremental = isIncremental;
|
||||||
this.previewKind = previewKind;
|
this.previewKind = previewKind;
|
||||||
this.pullRepair = pullRepair;
|
this.pullRepair = pullRepair;
|
||||||
|
this.allReplicas = allReplicas && !commonRange.hasSkippedReplicas;
|
||||||
this.optimiseStreams = optimiseStreams;
|
this.optimiseStreams = optimiseStreams;
|
||||||
this.dontPurgeTombstones = dontPurgeTombstones;
|
this.dontPurgeTombstones = dontPurgeTombstones;
|
||||||
this.taskExecutor = new SafeExecutor(createExecutor(ctx));
|
this.taskExecutor = new SafeExecutor(createExecutor(ctx));
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ import org.apache.cassandra.utils.TimeUUID;
|
||||||
*
|
*
|
||||||
* See {@link Global#instance} for the main production path
|
* See {@link Global#instance} for the main production path
|
||||||
*/
|
*/
|
||||||
// TODO (required, clarity): move under Util since this is a class with shared logic
|
// TODO (expected): move under Util since this is a class with shared logic
|
||||||
public interface SharedContext
|
public interface SharedContext
|
||||||
{
|
{
|
||||||
InetAddressAndPort broadcastAddressAndPort();
|
InetAddressAndPort broadcastAddressAndPort();
|
||||||
|
|
|
||||||
|
|
@ -311,7 +311,7 @@ class PreviewRepairedState extends AutoRepairState
|
||||||
public RepairCoordinator getRepairRunnable(String keyspace, List<String> tables, Set<Range<Token>> ranges, boolean primaryRangeOnly)
|
public RepairCoordinator getRepairRunnable(String keyspace, List<String> tables, Set<Range<Token>> ranges, boolean primaryRangeOnly)
|
||||||
{
|
{
|
||||||
RepairOption option = new RepairOption(RepairParallelism.PARALLEL, primaryRangeOnly, false, false,
|
RepairOption option = new RepairOption(RepairParallelism.PARALLEL, primaryRangeOnly, false, false,
|
||||||
AutoRepairService.instance.getAutoRepairConfig().getRepairThreads(repairType), ranges, false, false, PreviewKind.REPAIRED, false, true, true, false, false, false);
|
AutoRepairService.instance.getAutoRepairConfig().getRepairThreads(repairType), ranges, false, false, PreviewKind.REPAIRED, false, true, true, false, false, false, false);
|
||||||
|
|
||||||
option.getColumnFamilies().addAll(tables);
|
option.getColumnFamilies().addAll(tables);
|
||||||
|
|
||||||
|
|
@ -331,7 +331,7 @@ class IncrementalRepairState extends AutoRepairState
|
||||||
{
|
{
|
||||||
RepairOption option = new RepairOption(RepairParallelism.PARALLEL, primaryRangeOnly, true, false,
|
RepairOption option = new RepairOption(RepairParallelism.PARALLEL, primaryRangeOnly, true, false,
|
||||||
AutoRepairService.instance.getAutoRepairConfig().getRepairThreads(repairType), ranges,
|
AutoRepairService.instance.getAutoRepairConfig().getRepairThreads(repairType), ranges,
|
||||||
false, false, PreviewKind.NONE, true, true, true, false, false, false);
|
false, false, PreviewKind.NONE, true, true, true, false, false, false, false);
|
||||||
|
|
||||||
option.getColumnFamilies().addAll(filterOutUnsafeTables(keyspace, tables));
|
option.getColumnFamilies().addAll(filterOutUnsafeTables(keyspace, tables));
|
||||||
|
|
||||||
|
|
@ -376,7 +376,7 @@ class FullRepairState extends AutoRepairState
|
||||||
{
|
{
|
||||||
RepairOption option = new RepairOption(RepairParallelism.PARALLEL, primaryRangeOnly, false, false,
|
RepairOption option = new RepairOption(RepairParallelism.PARALLEL, primaryRangeOnly, false, false,
|
||||||
AutoRepairService.instance.getAutoRepairConfig().getRepairThreads(repairType), ranges,
|
AutoRepairService.instance.getAutoRepairConfig().getRepairThreads(repairType), ranges,
|
||||||
false, false, PreviewKind.NONE, true, true, true, false, false, false);
|
false, false, PreviewKind.NONE, true, true, true, false, false, false, false);
|
||||||
|
|
||||||
option.getColumnFamilies().addAll(tables);
|
option.getColumnFamilies().addAll(tables);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ public class RepairOption
|
||||||
public static final String REPAIR_PAXOS_KEY = "repairPaxos";
|
public static final String REPAIR_PAXOS_KEY = "repairPaxos";
|
||||||
public static final String NO_TOMBSTONE_PURGING = "nopurge";
|
public static final String NO_TOMBSTONE_PURGING = "nopurge";
|
||||||
public static final String REPAIR_ACCORD_KEY = "repairAccord";
|
public static final String REPAIR_ACCORD_KEY = "repairAccord";
|
||||||
|
public static final String PERMIT_NO_QUORUM_KEY = "permitNoQuorum";
|
||||||
|
|
||||||
// we don't want to push nodes too much for repair
|
// we don't want to push nodes too much for repair
|
||||||
public static final int MAX_JOB_THREADS = 4;
|
public static final int MAX_JOB_THREADS = 4;
|
||||||
|
|
@ -209,6 +210,7 @@ public class RepairOption
|
||||||
logger.info("Overriding and disabling Accord repair because Accord is not enabled");
|
logger.info("Overriding and disabling Accord repair because Accord is not enabled");
|
||||||
repairAccord = false;
|
repairAccord = false;
|
||||||
}
|
}
|
||||||
|
boolean permitNoQuorum = Boolean.parseBoolean(options.get(PERMIT_NO_QUORUM_KEY));
|
||||||
|
|
||||||
if (previewKind != PreviewKind.NONE)
|
if (previewKind != PreviewKind.NONE)
|
||||||
{
|
{
|
||||||
|
|
@ -233,7 +235,7 @@ public class RepairOption
|
||||||
|
|
||||||
boolean asymmetricSyncing = Boolean.parseBoolean(options.get(OPTIMISE_STREAMS_KEY));
|
boolean asymmetricSyncing = Boolean.parseBoolean(options.get(OPTIMISE_STREAMS_KEY));
|
||||||
|
|
||||||
RepairOption option = new RepairOption(parallelism, primaryRange, incremental, trace, jobThreads, ranges, pullRepair, force, previewKind, asymmetricSyncing, ignoreUnreplicatedKeyspaces, repairData, repairPaxos, dontPurgeTombstones, repairAccord);
|
RepairOption option = new RepairOption(parallelism, primaryRange, incremental, trace, jobThreads, ranges, pullRepair, force, previewKind, asymmetricSyncing, ignoreUnreplicatedKeyspaces, repairData, repairPaxos, dontPurgeTombstones, repairAccord, permitNoQuorum);
|
||||||
|
|
||||||
// data centers
|
// data centers
|
||||||
String dataCentersStr = options.get(DATACENTERS_KEY);
|
String dataCentersStr = options.get(DATACENTERS_KEY);
|
||||||
|
|
@ -316,13 +318,14 @@ public class RepairOption
|
||||||
private final boolean repairPaxos;
|
private final boolean repairPaxos;
|
||||||
private final boolean dontPurgeTombstones;
|
private final boolean dontPurgeTombstones;
|
||||||
private final boolean repairAccord;
|
private final boolean repairAccord;
|
||||||
|
private final boolean permitNoQuorum; // has an effect only for Accord at present
|
||||||
|
|
||||||
private final Collection<String> columnFamilies = new HashSet<>();
|
private final Collection<String> columnFamilies = new HashSet<>();
|
||||||
private final Collection<String> dataCenters = new HashSet<>();
|
private final Collection<String> dataCenters = new HashSet<>();
|
||||||
private final Collection<String> hosts = new HashSet<>();
|
private final Collection<String> hosts = new HashSet<>();
|
||||||
private final Collection<Range<Token>> ranges = new HashSet<>();
|
private final Collection<Range<Token>> ranges = new HashSet<>();
|
||||||
|
|
||||||
public RepairOption(RepairParallelism parallelism, boolean primaryRange, boolean incremental, boolean trace, int jobThreads, Collection<Range<Token>> ranges, boolean pullRepair, boolean forceRepair, PreviewKind previewKind, boolean optimiseStreams, boolean ignoreUnreplicatedKeyspaces, boolean repairData, boolean repairPaxos, boolean dontPurgeTombstones, boolean repairAccord)
|
public RepairOption(RepairParallelism parallelism, boolean primaryRange, boolean incremental, boolean trace, int jobThreads, Collection<Range<Token>> ranges, boolean pullRepair, boolean forceRepair, PreviewKind previewKind, boolean optimiseStreams, boolean ignoreUnreplicatedKeyspaces, boolean repairData, boolean repairPaxos, boolean dontPurgeTombstones, boolean repairAccord, boolean permitNoQuorum)
|
||||||
{
|
{
|
||||||
checkArgument(repairData || repairAccord || repairPaxos, "Repair needs to repair at least one of data, Paxos, or Accord");
|
checkArgument(repairData || repairAccord || repairPaxos, "Repair needs to repair at least one of data, Paxos, or Accord");
|
||||||
this.parallelism = parallelism;
|
this.parallelism = parallelism;
|
||||||
|
|
@ -340,6 +343,7 @@ public class RepairOption
|
||||||
this.repairPaxos = repairPaxos;
|
this.repairPaxos = repairPaxos;
|
||||||
this.dontPurgeTombstones = dontPurgeTombstones;
|
this.dontPurgeTombstones = dontPurgeTombstones;
|
||||||
this.repairAccord = repairAccord;
|
this.repairAccord = repairAccord;
|
||||||
|
this.permitNoQuorum = permitNoQuorum;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RepairParallelism getParallelism()
|
public RepairParallelism getParallelism()
|
||||||
|
|
@ -460,6 +464,11 @@ public class RepairOption
|
||||||
return repairAccord;
|
return repairAccord;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean permitNoQuorum()
|
||||||
|
{
|
||||||
|
return repairAccord;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean dontPurgeTombstones()
|
public boolean dontPurgeTombstones()
|
||||||
{
|
{
|
||||||
return dontPurgeTombstones;
|
return dontPurgeTombstones;
|
||||||
|
|
@ -514,6 +523,7 @@ public class RepairOption
|
||||||
options.put(REPAIR_PAXOS_KEY, Boolean.toString(repairPaxos));
|
options.put(REPAIR_PAXOS_KEY, Boolean.toString(repairPaxos));
|
||||||
options.put(NO_TOMBSTONE_PURGING, Boolean.toString(dontPurgeTombstones));
|
options.put(NO_TOMBSTONE_PURGING, Boolean.toString(dontPurgeTombstones));
|
||||||
options.put(REPAIR_ACCORD_KEY, Boolean.toString(repairAccord));
|
options.put(REPAIR_ACCORD_KEY, Boolean.toString(repairAccord));
|
||||||
|
options.put(PERMIT_NO_QUORUM_KEY, Boolean.toString(permitNoQuorum));
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ public class CoordinatorState extends AbstractState<CoordinatorState.State, Time
|
||||||
{
|
{
|
||||||
if (neighborsAndRanges == null)
|
if (neighborsAndRanges == null)
|
||||||
return null;
|
return null;
|
||||||
return neighborsAndRanges.filterCommonRanges(keyspace, getColumnFamilyNames());
|
return neighborsAndRanges.filterCommonRanges(keyspace, getColumnFamilyNames()).commonRanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -458,6 +458,7 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai
|
||||||
boolean excludedDeadNodes,
|
boolean excludedDeadNodes,
|
||||||
String keyspace,
|
String keyspace,
|
||||||
RepairParallelism parallelismDegree,
|
RepairParallelism parallelismDegree,
|
||||||
|
boolean allReplicas,
|
||||||
boolean isIncremental,
|
boolean isIncremental,
|
||||||
boolean pullRepair,
|
boolean pullRepair,
|
||||||
PreviewKind previewKind,
|
PreviewKind previewKind,
|
||||||
|
|
@ -466,6 +467,7 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai
|
||||||
boolean repairPaxos,
|
boolean repairPaxos,
|
||||||
boolean dontPurgeTombstones,
|
boolean dontPurgeTombstones,
|
||||||
boolean repairAccord,
|
boolean repairAccord,
|
||||||
|
boolean permitNoQuorum,
|
||||||
ExecutorPlus executor,
|
ExecutorPlus executor,
|
||||||
Scheduler validationScheduler,
|
Scheduler validationScheduler,
|
||||||
String... cfnames)
|
String... cfnames)
|
||||||
|
|
@ -481,9 +483,9 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai
|
||||||
|
|
||||||
final RepairSession session = new RepairSession(ctx, validationScheduler, parentRepairSession,
|
final RepairSession session = new RepairSession(ctx, validationScheduler, parentRepairSession,
|
||||||
range, excludedDeadNodes, keyspace,
|
range, excludedDeadNodes, keyspace,
|
||||||
parallelismDegree, isIncremental, pullRepair,
|
parallelismDegree, allReplicas, isIncremental, pullRepair,
|
||||||
previewKind, optimiseStreams, repairData, repairPaxos,
|
previewKind, optimiseStreams, repairData, repairPaxos,
|
||||||
dontPurgeTombstones, repairAccord, cfnames);
|
dontPurgeTombstones, repairAccord, permitNoQuorum, cfnames);
|
||||||
repairs.getIfPresent(parentRepairSession).register(session.state);
|
repairs.getIfPresent(parentRepairSession).register(session.state);
|
||||||
|
|
||||||
sessions.put(session.getId(), session);
|
sessions.put(session.getId(), session);
|
||||||
|
|
@ -567,6 +569,19 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai
|
||||||
public EndpointsForRange getNeighbors(String keyspaceName, Iterable<Range<Token>> keyspaceLocalRanges,
|
public EndpointsForRange getNeighbors(String keyspaceName, Iterable<Range<Token>> keyspaceLocalRanges,
|
||||||
Range<Token> toRepair, Collection<String> dataCenters,
|
Range<Token> toRepair, Collection<String> dataCenters,
|
||||||
Collection<String> hosts)
|
Collection<String> hosts)
|
||||||
|
{
|
||||||
|
return filterNeighbors(getNeighbors(keyspaceName, keyspaceLocalRanges, toRepair), toRepair, dataCenters, hosts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all of the neighbors with whom we share the provided range.
|
||||||
|
*
|
||||||
|
* @param keyspaceName keyspace to repair
|
||||||
|
* @param keyspaceLocalRanges local-range for given keyspaceName
|
||||||
|
* @param toRepair token to repair
|
||||||
|
* @return neighbors with whom we share the provided range
|
||||||
|
*/
|
||||||
|
public EndpointsForRange getNeighbors(String keyspaceName, Iterable<Range<Token>> keyspaceLocalRanges, Range<Token> toRepair)
|
||||||
{
|
{
|
||||||
StorageService ss = StorageService.instance;
|
StorageService ss = StorageService.instance;
|
||||||
EndpointsByRange replicaSets = ss.getRangeToAddressMap(keyspaceName);
|
EndpointsByRange replicaSets = ss.getRangeToAddressMap(keyspaceName);
|
||||||
|
|
@ -589,7 +604,22 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai
|
||||||
return EndpointsForRange.empty(toRepair);
|
return EndpointsForRange.empty(toRepair);
|
||||||
|
|
||||||
// same as withoutSelf(), but done this way for testing
|
// same as withoutSelf(), but done this way for testing
|
||||||
EndpointsForRange neighbors = replicaSets.get(rangeSuperSet).filter(r -> !ctx.broadcastAddressAndPort().equals(r.endpoint()));
|
return replicaSets.get(rangeSuperSet).filter(r -> !ctx.broadcastAddressAndPort().equals(r.endpoint()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all of the neighbors in the listed data center or host lists
|
||||||
|
*
|
||||||
|
* @param toRepair token to repair
|
||||||
|
* @param dataCenters the data centers to involve in the repair
|
||||||
|
* @return neighbors with whom we share the provided range
|
||||||
|
*/
|
||||||
|
public EndpointsForRange filterNeighbors(EndpointsForRange neighbors, Range<Token> toRepair, Collection<String> dataCenters,
|
||||||
|
Collection<String> hosts)
|
||||||
|
{
|
||||||
|
if (neighbors.isEmpty())
|
||||||
|
return neighbors;
|
||||||
|
|
||||||
ClusterMetadata metadata = ClusterMetadata.current();
|
ClusterMetadata metadata = ClusterMetadata.current();
|
||||||
if (dataCenters != null && !dataCenters.isEmpty())
|
if (dataCenters != null && !dataCenters.isEmpty())
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ import org.apache.cassandra.io.util.File;
|
||||||
import org.apache.cassandra.io.util.FileUtils;
|
import org.apache.cassandra.io.util.FileUtils;
|
||||||
import org.apache.cassandra.locator.InetAddressAndPort;
|
import org.apache.cassandra.locator.InetAddressAndPort;
|
||||||
import org.apache.cassandra.locator.Locator;
|
import org.apache.cassandra.locator.Locator;
|
||||||
|
import org.apache.cassandra.service.accord.AccordService;
|
||||||
import org.apache.cassandra.tcm.CMSOperations;
|
import org.apache.cassandra.tcm.CMSOperations;
|
||||||
import org.apache.cassandra.tcm.ClusterMetadataService;
|
import org.apache.cassandra.tcm.ClusterMetadataService;
|
||||||
import org.apache.cassandra.tcm.RegistrationStatus;
|
import org.apache.cassandra.tcm.RegistrationStatus;
|
||||||
|
|
@ -91,6 +92,7 @@ import org.apache.cassandra.streaming.StreamManager;
|
||||||
import org.apache.cassandra.tcm.ClusterMetadata;
|
import org.apache.cassandra.tcm.ClusterMetadata;
|
||||||
import org.apache.cassandra.tcm.MultiStepOperation;
|
import org.apache.cassandra.tcm.MultiStepOperation;
|
||||||
import org.apache.cassandra.tcm.membership.NodeState;
|
import org.apache.cassandra.tcm.membership.NodeState;
|
||||||
|
import org.apache.cassandra.tcm.membership.NodeId;
|
||||||
import org.apache.cassandra.utils.FBUtilities;
|
import org.apache.cassandra.utils.FBUtilities;
|
||||||
import org.apache.cassandra.utils.JMXServerUtils;
|
import org.apache.cassandra.utils.JMXServerUtils;
|
||||||
import org.apache.cassandra.utils.JVMStabilityInspector;
|
import org.apache.cassandra.utils.JVMStabilityInspector;
|
||||||
|
|
@ -343,11 +345,12 @@ public class CassandraDaemon
|
||||||
PaxosState.initializeTrackers();
|
PaxosState.initializeTrackers();
|
||||||
|
|
||||||
// replay the log if necessary
|
// replay the log if necessary
|
||||||
// TODO samt - when restarting a previously running instance, this needs to happen after reconstructing schema
|
|
||||||
// from the cluster metadata log or all mutations will throw IncompatibleSchemaException on deserialisation
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CommitLog.instance.recoverSegmentsOnDisk();
|
CommitLog.instance.recoverSegmentsOnDisk();
|
||||||
|
NodeId self = ClusterMetadata.current().myNodeId();
|
||||||
|
if (self != null)
|
||||||
|
AccordService.localStartup(self);
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -847,7 +847,10 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
|
||||||
Gossiper.waitToSettle();
|
Gossiper.waitToSettle();
|
||||||
|
|
||||||
NodeId self = Register.maybeRegister();
|
NodeId self = Register.maybeRegister();
|
||||||
AccordService.startup(self);
|
if (!AccordService.isSetupOrStarting())
|
||||||
|
AccordService.localStartup(self);
|
||||||
|
AccordService.distributedStartup();
|
||||||
|
|
||||||
RegistrationStatus.instance.onRegistration();
|
RegistrationStatus.instance.onRegistration();
|
||||||
Startup.maybeExecuteStartupTransformation(self);
|
Startup.maybeExecuteStartupTransformation(self);
|
||||||
|
|
||||||
|
|
@ -3165,7 +3168,8 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
|
||||||
true, // repairData
|
true, // repairData
|
||||||
false, // repairPaxos
|
false, // repairPaxos
|
||||||
true, // dontPurgeTombstones
|
true, // dontPurgeTombstones
|
||||||
false // repairAccord
|
false, // repairAccord
|
||||||
|
false // permit no quorum
|
||||||
);
|
);
|
||||||
|
|
||||||
return new RepairCoordinator(this, cmd, options, keyspace);
|
return new RepairCoordinator(this, cmd, options, keyspace);
|
||||||
|
|
@ -3846,8 +3850,8 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
|
||||||
transientMode = Optional.of(Mode.DRAINING);
|
transientMode = Optional.of(Mode.DRAINING);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AccordService.isSetup())
|
if (AccordService.isSetupOrStarting())
|
||||||
AccordService.instance().markShuttingDown();
|
AccordService.unsafeInstance().markShuttingDown();
|
||||||
|
|
||||||
// In-progress writes originating here could generate hints to be written,
|
// In-progress writes originating here could generate hints to be written,
|
||||||
// which is currently scheduled on the mutation stage. So shut down MessagingService
|
// which is currently scheduled on the mutation stage. So shut down MessagingService
|
||||||
|
|
@ -3863,14 +3867,14 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
|
||||||
logger.error("Messaging service timed out shutting down", t);
|
logger.error("Messaging service timed out shutting down", t);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AccordService.isSetup())
|
if (AccordService.isSetupOrStarting())
|
||||||
{
|
{
|
||||||
logger.info("Flushing Accord caches");
|
logger.info("Flushing Accord caches");
|
||||||
if (!AccordService.instance().flushCaches().awaitUninterruptibly(1, MINUTES))
|
if (!AccordService.unsafeInstance().flushCaches().awaitUninterruptibly(1, MINUTES))
|
||||||
logger.error("Could not flush Accord caches promptly");
|
logger.error("Could not flush Accord caches promptly");
|
||||||
if (AccordColumnFamilyStores.commandsForKey != null)
|
if (AccordColumnFamilyStores.commandsForKey != null)
|
||||||
AccordColumnFamilyStores.commandsForKey.forceBlockingFlush(INTERNALLY_FORCED);
|
AccordColumnFamilyStores.commandsForKey.forceBlockingFlush(INTERNALLY_FORCED);
|
||||||
AccordService.instance().shutdownAndWait(1, MINUTES);
|
AccordService.unsafeInstance().shutdownAndWait(1, MINUTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScheduledExecutors shuts down after MessagingService, as MessagingService may issue tasks to it.
|
// ScheduledExecutors shuts down after MessagingService, as MessagingService may issue tasks to it.
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
@ -60,6 +61,7 @@ import org.apache.cassandra.io.util.DataInputBuffer;
|
||||||
import org.apache.cassandra.metrics.AccordCacheMetrics;
|
import org.apache.cassandra.metrics.AccordCacheMetrics;
|
||||||
import org.apache.cassandra.metrics.LogLinearHistogram;
|
import org.apache.cassandra.metrics.LogLinearHistogram;
|
||||||
import org.apache.cassandra.metrics.ShardedHitRate;
|
import org.apache.cassandra.metrics.ShardedHitRate;
|
||||||
|
import org.apache.cassandra.service.accord.AccordCache.Adapter.Shrink;
|
||||||
import org.apache.cassandra.service.accord.AccordCacheEntry.LoadExecutor;
|
import org.apache.cassandra.service.accord.AccordCacheEntry.LoadExecutor;
|
||||||
import org.apache.cassandra.service.accord.AccordCacheEntry.Status;
|
import org.apache.cassandra.service.accord.AccordCacheEntry.Status;
|
||||||
import org.apache.cassandra.service.accord.events.CacheEvents;
|
import org.apache.cassandra.service.accord.events.CacheEvents;
|
||||||
|
|
@ -100,11 +102,14 @@ public class AccordCache implements CacheSize
|
||||||
|
|
||||||
public interface Adapter<K, V, S>
|
public interface Adapter<K, V, S>
|
||||||
{
|
{
|
||||||
|
enum Shrink { EVICT, DONE, PERFORM_WITHOUT_LOCK }
|
||||||
|
|
||||||
@Nullable V load(AccordCommandStore commandStore, K key);
|
@Nullable V load(AccordCommandStore commandStore, K key);
|
||||||
@Nullable Runnable save(AccordCommandStore commandStore, K key, @Nullable V value, @Nullable Object shrunk);
|
@Nullable Runnable save(AccordCommandStore commandStore, K key, @Nullable V value, @Nullable Object shrunk);
|
||||||
default boolean canSave(@Nullable V value, @Nullable Object shrunk) { return true; }
|
default boolean canSave(@Nullable V value, @Nullable Object shrunk) { return true; }
|
||||||
// a result of null means we can immediately evict, without saving
|
// a result of null means we can immediately evict, without saving
|
||||||
@Nullable V quickShrink(V value);
|
@Nullable V quickShrink(V value);
|
||||||
|
Shrink decideFullShrink(K key, V value);
|
||||||
// a result of null means we cannot shrink, and should save/evict as appropriate
|
// a result of null means we cannot shrink, and should save/evict as appropriate
|
||||||
@Nullable Object fullShrink(K key, V value);
|
@Nullable Object fullShrink(K key, V value);
|
||||||
@Nullable V inflate(AccordCommandStore commandStore, K key, Object shrunk);
|
@Nullable V inflate(AccordCommandStore commandStore, K key, Object shrunk);
|
||||||
|
|
@ -149,6 +154,7 @@ public class AccordCache implements CacheSize
|
||||||
private long bytesCached;
|
private long bytesCached;
|
||||||
private int noEvictGeneration;
|
private int noEvictGeneration;
|
||||||
private boolean shrinkingOn = true;
|
private boolean shrinkingOn = true;
|
||||||
|
private boolean tryShrinkOrEvict;
|
||||||
|
|
||||||
public AccordCache(AccordCacheEntry.SaveExecutor saveExecutor, long maxSizeInBytes)
|
public AccordCache(AccordCacheEntry.SaveExecutor saveExecutor, long maxSizeInBytes)
|
||||||
{
|
{
|
||||||
|
|
@ -156,11 +162,12 @@ public class AccordCache implements CacheSize
|
||||||
this.maxSizeInBytes = maxSizeInBytes;
|
this.maxSizeInBytes = maxSizeInBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// note: only affects current contents after lock is released
|
||||||
@Override
|
@Override
|
||||||
public void setCapacity(long sizeInBytes)
|
public void setCapacity(long sizeInBytes)
|
||||||
{
|
{
|
||||||
maxSizeInBytes = sizeInBytes;
|
maxSizeInBytes = sizeInBytes;
|
||||||
maybeShrinkOrEvictSomeNodes();
|
tryShrinkOrEvict = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setShrinkingOn(boolean shrinkingOn)
|
public void setShrinkingOn(boolean shrinkingOn)
|
||||||
|
|
@ -206,31 +213,56 @@ public class AccordCache implements CacheSize
|
||||||
* Roughly respects LRU semantics when evicting. Might consider prioritising keeping MODIFIED nodes around
|
* Roughly respects LRU semantics when evicting. Might consider prioritising keeping MODIFIED nodes around
|
||||||
* for longer to maximise the chances of hitting system tables fewer times (or not at all).
|
* for longer to maximise the chances of hitting system tables fewer times (or not at all).
|
||||||
*/
|
*/
|
||||||
private void maybeShrinkOrEvictSomeNodes()
|
void tryShrinkOrEvict(Lock lock)
|
||||||
{
|
{
|
||||||
|
if (!tryShrinkOrEvict)
|
||||||
|
return;
|
||||||
|
|
||||||
while (bytesCached > maxSizeInBytes && !evictQueue.isEmpty())
|
while (bytesCached > maxSizeInBytes && !evictQueue.isEmpty())
|
||||||
{
|
{
|
||||||
AccordCacheEntry<?, ?> node = evictQueue.peek();
|
AccordCacheEntry<?, ?> node = evictQueue.peek();
|
||||||
shrinkOrEvict(node);
|
shrinkOrEvict(lock, node);
|
||||||
}
|
}
|
||||||
|
tryShrinkOrEvict = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
private <K, V> void shrinkOrEvict(AccordCacheEntry<K, V> node)
|
private <K, V> void shrinkOrEvict(Lock lock, AccordCacheEntry<K, V> node)
|
||||||
{
|
{
|
||||||
require(node.references() == 0);
|
require(node.references() == 0);
|
||||||
|
|
||||||
// TODO (desired): special-case evict queue with 1 element as no point shrinking first
|
Shrink shrink = shrinkingOn ? node.tryShrink() : Shrink.EVICT;
|
||||||
if (shrinkingOn && node.tryShrink())
|
if (shrink == Shrink.EVICT)
|
||||||
|
{
|
||||||
|
tryEvict(node);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
IntrusiveLinkedList<AccordCacheEntry<?,?>> queue;
|
IntrusiveLinkedList<AccordCacheEntry<?,?>> queue;
|
||||||
queue = node.isNoEvict() ? noEvictQueue : evictQueue;
|
queue = node.isNoEvict() ? noEvictQueue : evictQueue;
|
||||||
node.unlink();
|
node.unlink();
|
||||||
queue.addLast(node);
|
if (shrink == Shrink.DONE)
|
||||||
}
|
{
|
||||||
else
|
queue.addLast(node);
|
||||||
{
|
}
|
||||||
tryEvict(node);
|
else
|
||||||
|
{
|
||||||
|
K key = node.key();
|
||||||
|
V cur = node.tryGetExclusive();
|
||||||
|
Object upd = null;
|
||||||
|
lock.unlock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
upd = node.owner.parent().adapter().fullShrink(key, cur);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
//noinspection LockAcquiredButNotSafelyReleased
|
||||||
|
lock.lock();
|
||||||
|
node.tryApplyShrink(cur, upd);
|
||||||
|
queue.addLast(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -463,7 +495,7 @@ public class AccordCache implements CacheSize
|
||||||
node.initSize(parent());
|
node.initSize(parent());
|
||||||
++size;
|
++size;
|
||||||
node.notifyListeners(Listener::onAdd);
|
node.notifyListeners(Listener::onAdd);
|
||||||
maybeShrinkOrEvictSomeNodes();
|
tryShrinkOrEvict = true;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -557,7 +589,7 @@ public class AccordCache implements CacheSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
maybeShrinkOrEvictSomeNodes();
|
tryShrinkOrEvict = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
AccordCacheEntry<K, ?> remove(K key)
|
AccordCacheEntry<K, ?> remove(K key)
|
||||||
|
|
@ -1009,6 +1041,12 @@ public class AccordCache implements CacheSize
|
||||||
return quickShrink.apply(value);
|
return quickShrink.apply(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Shrink decideFullShrink(K key, V value)
|
||||||
|
{
|
||||||
|
return Shrink.DONE;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object fullShrink(K key, V value)
|
public Object fullShrink(K key, V value)
|
||||||
{
|
{
|
||||||
|
|
@ -1087,6 +1125,7 @@ public class AccordCache implements CacheSize
|
||||||
@Override public V load(AccordCommandStore commandStore, K key) { return null; }
|
@Override public V load(AccordCommandStore commandStore, K key) { return null; }
|
||||||
@Override public Runnable save(AccordCommandStore commandStore, K key, @Nullable V value, @Nullable Object shrunk) { return null; }
|
@Override public Runnable save(AccordCommandStore commandStore, K key, @Nullable V value, @Nullable Object shrunk) { return null; }
|
||||||
@Override public V quickShrink(V value) { return null; }
|
@Override public V quickShrink(V value) { return null; }
|
||||||
|
@Override public Shrink decideFullShrink(K key, V value) { return Shrink.DONE; }
|
||||||
@Override public Object fullShrink(K key, V value) { return null; }
|
@Override public Object fullShrink(K key, V value) { return null; }
|
||||||
@Override public V inflate(AccordCommandStore commandStore, K key, Object shrunk) { return null; }
|
@Override public V inflate(AccordCommandStore commandStore, K key, Object shrunk) { return null; }
|
||||||
@Override public long estimateHeapSize(V value) { return 0; }
|
@Override public long estimateHeapSize(V value) { return 0; }
|
||||||
|
|
@ -1129,6 +1168,18 @@ public class AccordCache implements CacheSize
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Shrink decideFullShrink(RoutingKey key, CommandsForKey value)
|
||||||
|
{
|
||||||
|
if (value.isEmpty() || value.isLoadingPruned())
|
||||||
|
return Shrink.EVICT;
|
||||||
|
|
||||||
|
if (value.size() < 64)
|
||||||
|
return Shrink.DONE;
|
||||||
|
|
||||||
|
return Shrink.PERFORM_WITHOUT_LOCK;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object fullShrink(RoutingKey key, CommandsForKey value)
|
public Object fullShrink(RoutingKey key, CommandsForKey value)
|
||||||
{
|
{
|
||||||
|
|
@ -1249,6 +1300,19 @@ public class AccordCache implements CacheSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Shrink decideFullShrink(TxnId txnId, Command value)
|
||||||
|
{
|
||||||
|
if (txnId.is(Txn.Kind.EphemeralRead))
|
||||||
|
Invariants.expect(value.saveStatus().compareTo(SaveStatus.ReadyToExecute) < 0);
|
||||||
|
|
||||||
|
// TODO (expected): improve heuristics and consider transaction size
|
||||||
|
if (value.partialDeps() == null || value.partialDeps().txnIds().size() < 64)
|
||||||
|
return Shrink.DONE;
|
||||||
|
|
||||||
|
return Shrink.PERFORM_WITHOUT_LOCK;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Command inflate(AccordCommandStore commandStore, TxnId key, Object serialized)
|
public @Nullable Command inflate(AccordCommandStore commandStore, TxnId key, Object serialized)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import accord.utils.IntrusiveLinkedListNode;
|
||||||
import accord.utils.Invariants;
|
import accord.utils.Invariants;
|
||||||
import accord.utils.async.Cancellable;
|
import accord.utils.async.Cancellable;
|
||||||
import org.apache.cassandra.service.accord.AccordCache.Adapter;
|
import org.apache.cassandra.service.accord.AccordCache.Adapter;
|
||||||
|
import org.apache.cassandra.service.accord.AccordCache.Adapter.Shrink;
|
||||||
import org.apache.cassandra.utils.ObjectSizes;
|
import org.apache.cassandra.utils.ObjectSizes;
|
||||||
|
|
||||||
import static org.apache.cassandra.service.accord.AccordCacheEntry.Status.EVICTED;
|
import static org.apache.cassandra.service.accord.AccordCacheEntry.Status.EVICTED;
|
||||||
|
|
@ -462,16 +463,26 @@ public class AccordCacheEntry<K, V> extends IntrusiveLinkedListNode
|
||||||
state = null;
|
state = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean tryShrink()
|
Shrink tryShrink()
|
||||||
{
|
{
|
||||||
if (!isLoaded())
|
if (!isLoaded())
|
||||||
return false;
|
return Shrink.EVICT;
|
||||||
|
|
||||||
AccordCache.Type<K, V, ?> parent = owner.parent();
|
AccordCache.Type<K, V, ?> parent = owner.parent();
|
||||||
if (!tryShrink(key, parent.adapter()))
|
Adapter<K, V, ?> adapter = parent.adapter();
|
||||||
return false;
|
if (isShrunk() || state == null)
|
||||||
updateSize(parent);
|
return Shrink.EVICT;
|
||||||
return true;
|
|
||||||
|
V cur = (V)unwrap();
|
||||||
|
Shrink shrink = adapter.decideFullShrink(key, cur);
|
||||||
|
if (shrink == Shrink.PERFORM_WITHOUT_LOCK)
|
||||||
|
return Shrink.PERFORM_WITHOUT_LOCK;
|
||||||
|
|
||||||
|
Object upd = adapter.fullShrink(key, cur);
|
||||||
|
if (upd == null || upd == cur)
|
||||||
|
return Shrink.EVICT;
|
||||||
|
applyShrink(parent, cur, upd);
|
||||||
|
return Shrink.DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
V tryGetFull()
|
V tryGetFull()
|
||||||
|
|
@ -584,20 +595,18 @@ public class AccordCacheEntry<K, V> extends IntrusiveLinkedListNode
|
||||||
return ((FailedToSave)state).cause;
|
return ((FailedToSave)state).cause;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean tryShrink(K key, Adapter<K, V, ?> adapter)
|
void tryApplyShrink(Object cur, Object upd)
|
||||||
{
|
{
|
||||||
if (isShrunk() || state == null)
|
if (isLoaded() && unwrap() == cur && upd != cur && upd != null)
|
||||||
return false;
|
applyShrink(owner.parent(), cur, upd);
|
||||||
|
}
|
||||||
Object cur = unwrap();
|
|
||||||
Object upd = adapter.fullShrink(key, (V)cur);
|
|
||||||
if (upd == null || upd == cur)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
|
private void applyShrink(AccordCache.Type<K, V, ?> parent, Object cur, Object upd)
|
||||||
|
{
|
||||||
if (isNested()) ((Nested)this.state).state = upd;
|
if (isNested()) ((Nested)this.state).state = upd;
|
||||||
else this.state = upd;
|
else this.state = upd;
|
||||||
status |= SHRUNK;
|
status |= SHRUNK;
|
||||||
return true;
|
updateSize(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void inflate(AccordCommandStore commandStore, K key, Adapter<K, V, ?> adapter)
|
private void inflate(AccordCommandStore commandStore, K key, Adapter<K, V, ?> adapter)
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,7 @@ public class AccordCommandStore extends CommandStore
|
||||||
@Override
|
@Override
|
||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
|
global().tryShrinkOrEvict(lock);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -208,10 +209,14 @@ public class AccordCommandStore extends CommandStore
|
||||||
ranges = update.newRangesForEpoch;
|
ranges = update.newRangesForEpoch;
|
||||||
Invariants.require(ranges != null, "CommandStore %d created with no ranges", id);
|
Invariants.require(ranges != null, "CommandStore %d created with no ranges", id);
|
||||||
}
|
}
|
||||||
|
|
||||||
tableId = (TableId)ranges.all().stream().map(r -> r.start().prefix()).reduce((a, b) -> {
|
tableId = (TableId)ranges.all().stream().map(r -> r.start().prefix()).reduce((a, b) -> {
|
||||||
Invariants.require(a.equals(b), "CommandStore created with multiple distinct TableId (%s and %s)", a, b);
|
Invariants.require(a.equals(b), "CommandStore created with multiple distinct TableId (%s and %s)", a, b);
|
||||||
return a;
|
return a;
|
||||||
}).orElseThrow(() -> Invariants.illegalState("CommandStore %d created with no ranges", id));
|
}).orElseThrow(() -> Invariants.illegalState("CommandStore %d created with no ranges", id));
|
||||||
|
|
||||||
|
if (AccordService.isStarted())
|
||||||
|
progressLog.unsafeStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Factory factory(IntFunction<AccordExecutor> executorFactory)
|
static Factory factory(IntFunction<AccordExecutor> executorFactory)
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import accord.local.SequentialAsyncExecutor;
|
||||||
import accord.local.ShardDistributor;
|
import accord.local.ShardDistributor;
|
||||||
import accord.utils.RandomSource;
|
import accord.utils.RandomSource;
|
||||||
import org.apache.cassandra.cache.CacheSize;
|
import org.apache.cassandra.cache.CacheSize;
|
||||||
|
import org.apache.cassandra.concurrent.ScheduledExecutors;
|
||||||
import org.apache.cassandra.config.AccordSpec.QueueShardModel;
|
import org.apache.cassandra.config.AccordSpec.QueueShardModel;
|
||||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
import org.apache.cassandra.service.accord.AccordExecutor.AccordExecutorFactory;
|
import org.apache.cassandra.service.accord.AccordExecutor.AccordExecutorFactory;
|
||||||
|
|
@ -66,6 +67,14 @@ public class AccordCommandStores extends CommandStores implements CacheSize
|
||||||
maxQueuedRangeLoads = DatabaseDescriptor.getAccordMaxQueuedRangeLoadCount();
|
maxQueuedRangeLoads = DatabaseDescriptor.getAccordMaxQueuedRangeLoadCount();
|
||||||
shrinkingOn = DatabaseDescriptor.getAccordCacheShrinkingOn();
|
shrinkingOn = DatabaseDescriptor.getAccordCacheShrinkingOn();
|
||||||
refreshCapacities();
|
refreshCapacities();
|
||||||
|
ScheduledExecutors.scheduledFastTasks.scheduleWithFixedDelay(() -> {
|
||||||
|
for (AccordExecutor executor : executors)
|
||||||
|
{
|
||||||
|
executor.executeDirectlyWithLock(() -> {
|
||||||
|
executor.cacheExclusive().processNoEvictQueue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 1L, 1L, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Factory factory()
|
static Factory factory()
|
||||||
|
|
@ -204,6 +213,9 @@ public class AccordCommandStores extends CommandStores implements CacheSize
|
||||||
for (AccordExecutor executor : executors)
|
for (AccordExecutor executor : executors)
|
||||||
{
|
{
|
||||||
executor.shutdown();
|
executor.shutdown();
|
||||||
|
}
|
||||||
|
for (AccordExecutor executor : executors)
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
executor.awaitTermination(1, TimeUnit.MINUTES);
|
executor.awaitTermination(1, TimeUnit.MINUTES);
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,16 @@ public class AccordDataStore implements DataStore
|
||||||
default: throw new UnhandledEnum(kind);
|
default: throw new UnhandledEnum(kind);
|
||||||
case Image:
|
case Image:
|
||||||
{
|
{
|
||||||
AccordFetchCoordinator coordinator = new AccordFetchCoordinator(node, ranges, syncPoint, callback, safeStore.commandStore());
|
AccordFetchCoordinator coordinator;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
coordinator = new AccordFetchCoordinator(node, ranges, syncPoint, callback, safeStore.commandStore());
|
||||||
|
}
|
||||||
|
catch (Throwable t)
|
||||||
|
{
|
||||||
|
return new FetchResult.Failure(t);
|
||||||
|
}
|
||||||
|
|
||||||
coordinator.start();
|
coordinator.start();
|
||||||
return coordinator.result();
|
return coordinator.result();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import java.util.List;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.LockSupport;
|
import java.util.concurrent.locks.LockSupport;
|
||||||
|
|
@ -66,7 +65,6 @@ import accord.utils.async.Cancellable;
|
||||||
import org.apache.cassandra.cache.CacheSize;
|
import org.apache.cassandra.cache.CacheSize;
|
||||||
import org.apache.cassandra.concurrent.DebuggableTask;
|
import org.apache.cassandra.concurrent.DebuggableTask;
|
||||||
import org.apache.cassandra.concurrent.DebuggableTask.DebuggableTaskRunner;
|
import org.apache.cassandra.concurrent.DebuggableTask.DebuggableTaskRunner;
|
||||||
import org.apache.cassandra.concurrent.ScheduledExecutors;
|
|
||||||
import org.apache.cassandra.concurrent.Shutdownable;
|
import org.apache.cassandra.concurrent.Shutdownable;
|
||||||
import org.apache.cassandra.metrics.AccordCacheMetrics;
|
import org.apache.cassandra.metrics.AccordCacheMetrics;
|
||||||
import org.apache.cassandra.metrics.AccordExecutorMetrics;
|
import org.apache.cassandra.metrics.AccordExecutorMetrics;
|
||||||
|
|
@ -130,6 +128,7 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
|
||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
executor.beforeUnlock();
|
executor.beforeUnlock();
|
||||||
|
global.tryShrinkOrEvict(lock);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -235,9 +234,6 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
|
||||||
this.elapsedRunning = AccordExecutorMetrics.INSTANCE.elapsedRunning.forShard(histogramsShard);
|
this.elapsedRunning = AccordExecutorMetrics.INSTANCE.elapsedRunning.forShard(histogramsShard);
|
||||||
this.keys = AccordExecutorMetrics.INSTANCE.keys.forShard(histogramsShard);
|
this.keys = AccordExecutorMetrics.INSTANCE.keys.forShard(histogramsShard);
|
||||||
this.replicaMetrics = new AccordReplicaMetrics.Shard(histogramsShard);
|
this.replicaMetrics = new AccordReplicaMetrics.Shard(histogramsShard);
|
||||||
ScheduledExecutors.scheduledFastTasks.scheduleAtFixedRate(() -> {
|
|
||||||
executeDirectlyWithLock(cache::processNoEvictQueue);
|
|
||||||
}, 1L, 1L, TimeUnit.SECONDS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int executorId()
|
public int executorId()
|
||||||
|
|
@ -493,7 +489,7 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
|
||||||
}
|
}
|
||||||
catch (Throwable t)
|
catch (Throwable t)
|
||||||
{
|
{
|
||||||
agent.onUncaughtException(t);
|
agent.onException(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -663,6 +659,8 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
|
||||||
|
|
||||||
if (waitingForCompletion != null && waitingForCompletion.peek().maybeNotify - position >= 0)
|
if (waitingForCompletion != null && waitingForCompletion.peek().maybeNotify - position >= 0)
|
||||||
maybeNotifyWaitingForCompletion();
|
maybeNotifyWaitingForCompletion();
|
||||||
|
|
||||||
|
cache.tryShrinkOrEvict(lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -745,7 +743,7 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try { task.failExclusive(fail); }
|
try { task.failExclusive(fail); }
|
||||||
catch (Throwable t) { agent.onUncaughtException(t); }
|
catch (Throwable t) { agent.onException(t); }
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
task.unqueueIfQueued();
|
task.unqueueIfQueued();
|
||||||
|
|
@ -1197,7 +1195,7 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
try { run.run(); }
|
try { run.run(); }
|
||||||
catch (Throwable t) { agent.onUncaughtException(t); }
|
catch (Throwable t) { agent.onException(t); }
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (owner == null)
|
if (owner == null)
|
||||||
|
|
@ -1322,7 +1320,7 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
|
||||||
queue.remove(this);
|
queue.remove(this);
|
||||||
completeTaskExclusive(this);
|
completeTaskExclusive(this);
|
||||||
try { fail(new CancellationException()); }
|
try { fail(new CancellationException()); }
|
||||||
catch (Throwable t) { agent.onUncaughtException(t); }
|
catch (Throwable t) { agent.onException(t); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1373,7 +1371,7 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
|
||||||
{
|
{
|
||||||
if (result != null)
|
if (result != null)
|
||||||
result.tryFailure(t);
|
result.tryFailure(t);
|
||||||
agent.onUncaughtException(t);
|
agent.onException(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -1606,7 +1604,7 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
|
||||||
}
|
}
|
||||||
catch (Throwable t)
|
catch (Throwable t)
|
||||||
{
|
{
|
||||||
agent.onUncaughtException(t);
|
agent.onException(t);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1621,7 +1619,7 @@ public abstract class AccordExecutor implements CacheSize, LoadExecutor<AccordTa
|
||||||
catch (Throwable t)
|
catch (Throwable t)
|
||||||
{
|
{
|
||||||
fail.addSuppressed(t);
|
fail.addSuppressed(t);
|
||||||
agent.onUncaughtException(fail);
|
agent.onException(fail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,7 @@ abstract class AccordExecutorAbstractLockLoop extends AccordExecutor
|
||||||
pauseExclusive();
|
pauseExclusive();
|
||||||
exitLockExclusive();
|
exitLockExclusive();
|
||||||
|
|
||||||
try { agent.onUncaughtException(t); }
|
try { agent.onException(t); }
|
||||||
catch (Throwable t2) { }
|
catch (Throwable t2) { }
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
@ -271,13 +271,13 @@ abstract class AccordExecutorAbstractLockLoop extends AccordExecutor
|
||||||
catch (Throwable t2) { t.addSuppressed(t2); }
|
catch (Throwable t2) { t.addSuppressed(t2); }
|
||||||
try { completeTaskExclusive(task); }
|
try { completeTaskExclusive(task); }
|
||||||
catch (Throwable t2) { t.addSuppressed(t2); }
|
catch (Throwable t2) { t.addSuppressed(t2); }
|
||||||
try { agent.onUncaughtException(t); }
|
try { agent.onException(t); }
|
||||||
catch (Throwable t2) { /* nothing we can sensibly do after already reporting */ }
|
catch (Throwable t2) { /* nothing we can sensibly do after already reporting */ }
|
||||||
task = null;
|
task = null;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
try { agent.onUncaughtException(t); }
|
try { agent.onException(t); }
|
||||||
catch (Throwable t2) { /* nothing we can sensibly do after already reporting */ }
|
catch (Throwable t2) { /* nothing we can sensibly do after already reporting */ }
|
||||||
}
|
}
|
||||||
if (isHeldByExecutor)
|
if (isHeldByExecutor)
|
||||||
|
|
@ -302,7 +302,7 @@ abstract class AccordExecutorAbstractLockLoop extends AccordExecutor
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
t2.addSuppressed(t);
|
t2.addSuppressed(t);
|
||||||
agent.onUncaughtException(t2);
|
agent.onException(t2);
|
||||||
}
|
}
|
||||||
catch (Throwable t3)
|
catch (Throwable t3)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ import accord.primitives.SyncPoint;
|
||||||
import accord.primitives.Timestamp;
|
import accord.primitives.Timestamp;
|
||||||
import accord.primitives.Txn;
|
import accord.primitives.Txn;
|
||||||
import accord.primitives.TxnId;
|
import accord.primitives.TxnId;
|
||||||
|
import accord.topology.TopologyException;
|
||||||
import accord.utils.Invariants;
|
import accord.utils.Invariants;
|
||||||
import accord.utils.async.AsyncChain;
|
import accord.utils.async.AsyncChain;
|
||||||
import accord.utils.async.AsyncChains;
|
import accord.utils.async.AsyncChains;
|
||||||
|
|
@ -78,6 +79,7 @@ import org.apache.cassandra.tcm.ClusterMetadata;
|
||||||
import org.apache.cassandra.utils.FBUtilities;
|
import org.apache.cassandra.utils.FBUtilities;
|
||||||
import org.apache.cassandra.utils.TimeUUID;
|
import org.apache.cassandra.utils.TimeUUID;
|
||||||
|
|
||||||
|
import static accord.primitives.Routables.Slice.Minimal;
|
||||||
import static org.apache.cassandra.utils.CollectionSerializers.deserializeMap;
|
import static org.apache.cassandra.utils.CollectionSerializers.deserializeMap;
|
||||||
import static org.apache.cassandra.utils.CollectionSerializers.serializeMap;
|
import static org.apache.cassandra.utils.CollectionSerializers.serializeMap;
|
||||||
import static org.apache.cassandra.utils.CollectionSerializers.serializedMapSize;
|
import static org.apache.cassandra.utils.CollectionSerializers.serializedMapSize;
|
||||||
|
|
@ -219,7 +221,6 @@ public class AccordFetchCoordinator extends AbstractFetchCoordinator implements
|
||||||
Invariants.nonNull(future);
|
Invariants.nonNull(future);
|
||||||
Invariants.require(this.future == null, "future was not null: %s", this.future);
|
Invariants.require(this.future == null, "future was not null: %s", this.future);
|
||||||
this.future = future;
|
this.future = future;
|
||||||
logger.info("StreamFuture for plan {} received for bootstrap of {} from {}", planId, range, from);
|
|
||||||
maybeListen();
|
maybeListen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -229,6 +230,8 @@ public class AccordFetchCoordinator extends AbstractFetchCoordinator implements
|
||||||
if (range == null || future == null)
|
if (range == null || future == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
logger.info("StreamFuture for plan {} received for bootstrap of {} from {}", planId, range, from);
|
||||||
|
|
||||||
Invariants.nonNull(from);
|
Invariants.nonNull(from);
|
||||||
future.addCallback((state, fail) -> {
|
future.addCallback((state, fail) -> {
|
||||||
if (fail == null)
|
if (fail == null)
|
||||||
|
|
@ -328,10 +331,10 @@ public class AccordFetchCoordinator extends AbstractFetchCoordinator implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Read slice(Ranges ranges) { return new StreamingRead(to, this.ranges.slice(ranges)); }
|
public Read slice(Ranges ranges) { return new StreamingRead(to, this.ranges.slice(ranges, Minimal)); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Read intersecting(Participants<?> participants) { return new StreamingRead(to, this.ranges.slice(ranges)); }
|
public Read intersecting(Participants<?> participants) { return new StreamingRead(to, this.ranges.slice(ranges, Minimal)); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Read merge(Read other) { throw new UnsupportedOperationException(); }
|
public Read merge(Read other) { throw new UnsupportedOperationException(); }
|
||||||
|
|
@ -391,7 +394,7 @@ public class AccordFetchCoordinator extends AbstractFetchCoordinator implements
|
||||||
|
|
||||||
private final Map<TimeUUID, IncomingStream> streams = new HashMap<>();
|
private final Map<TimeUUID, IncomingStream> streams = new HashMap<>();
|
||||||
|
|
||||||
public AccordFetchCoordinator(Node node, Ranges ranges, SyncPoint syncPoint, DataStore.FetchRanges fetchRanges, CommandStore commandStore)
|
public AccordFetchCoordinator(Node node, Ranges ranges, SyncPoint syncPoint, DataStore.FetchRanges fetchRanges, CommandStore commandStore) throws TopologyException
|
||||||
{
|
{
|
||||||
super(node, node.someSequentialExecutor(), ranges, syncPoint, fetchRanges, commandStore);
|
super(node, node.someSequentialExecutor(), ranges, syncPoint, fetchRanges, commandStore);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -798,6 +798,7 @@ public class AccordJournal implements accord.api.Journal, RangeSearcher.Supplier
|
||||||
|
|
||||||
public static @Nullable ByteBuffer asSerializedChange(Command before, Command after, Version userVersion) throws IOException
|
public static @Nullable ByteBuffer asSerializedChange(Command before, Command after, Version userVersion) throws IOException
|
||||||
{
|
{
|
||||||
|
// TODO (expected): reusable buffer to build, or pre-size
|
||||||
try (DataOutputBuffer out = new DataOutputBuffer())
|
try (DataOutputBuffer out = new DataOutputBuffer())
|
||||||
{
|
{
|
||||||
Writer writer = Writer.make(before, after);
|
Writer writer = Writer.make(before, after);
|
||||||
|
|
@ -1149,7 +1150,7 @@ public class AccordJournal implements accord.api.Journal, RangeSearcher.Supplier
|
||||||
partialTxn = CommandSerializers.partialTxn.deserialize(in, userVersion);
|
partialTxn = CommandSerializers.partialTxn.deserialize(in, userVersion);
|
||||||
break;
|
break;
|
||||||
case PARTIAL_DEPS:
|
case PARTIAL_DEPS:
|
||||||
// TODO (required): this optimisation will be easily disabled;
|
// TODO (expected): this optimisation will be easily disabled;
|
||||||
// should either operate natively on ByteBuffer
|
// should either operate natively on ByteBuffer
|
||||||
// or else use some explicit API for copying bytes while skipping
|
// or else use some explicit API for copying bytes while skipping
|
||||||
if (deserializeDeps || !(in instanceof DataInputBuffer))
|
if (deserializeDeps || !(in instanceof DataInputBuffer))
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ import org.apache.cassandra.service.accord.serializers.Version;
|
||||||
|
|
||||||
import static accord.local.CommandStores.RangesForEpoch;
|
import static accord.local.CommandStores.RangesForEpoch;
|
||||||
|
|
||||||
// TODO (required): test with large collection values, and perhaps split out some fields if they have a tendency to grow larger
|
|
||||||
public class AccordJournalValueSerializers
|
public class AccordJournalValueSerializers
|
||||||
{
|
{
|
||||||
public interface FlyweightImage
|
public interface FlyweightImage
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,6 @@ import java.util.Set;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import accord.api.AsyncExecutor;
|
import accord.api.AsyncExecutor;
|
||||||
import accord.api.MessageSink;
|
import accord.api.MessageSink;
|
||||||
|
|
@ -48,7 +46,6 @@ import org.apache.cassandra.net.MessagingService;
|
||||||
import org.apache.cassandra.net.ResponseContext;
|
import org.apache.cassandra.net.ResponseContext;
|
||||||
import org.apache.cassandra.net.Verb;
|
import org.apache.cassandra.net.Verb;
|
||||||
import org.apache.cassandra.service.TimeoutStrategy;
|
import org.apache.cassandra.service.TimeoutStrategy;
|
||||||
import org.apache.cassandra.service.accord.api.AccordAgent;
|
|
||||||
import org.apache.cassandra.utils.Clock;
|
import org.apache.cassandra.utils.Clock;
|
||||||
|
|
||||||
import static accord.messages.MessageType.StandardMessage.*;
|
import static accord.messages.MessageType.StandardMessage.*;
|
||||||
|
|
@ -59,8 +56,6 @@ import static org.apache.cassandra.service.accord.api.AccordWaitStrategies.slowR
|
||||||
|
|
||||||
public class AccordMessageSink implements MessageSink
|
public class AccordMessageSink implements MessageSink
|
||||||
{
|
{
|
||||||
private static final Logger logger = LoggerFactory.getLogger(AccordMessageSink.class);
|
|
||||||
|
|
||||||
public enum AccordMessageType implements MessageType
|
public enum AccordMessageType implements MessageType
|
||||||
{
|
{
|
||||||
INTEROP_READ_REQ(Verb.ACCORD_INTEROP_READ_REQ),
|
INTEROP_READ_REQ(Verb.ACCORD_INTEROP_READ_REQ),
|
||||||
|
|
@ -150,22 +145,20 @@ public class AccordMessageSink implements MessageSink
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final AccordAgent agent;
|
|
||||||
private final MessageDelivery messaging;
|
private final MessageDelivery messaging;
|
||||||
private final AccordEndpointMapper endpointMapper;
|
private final AccordEndpointMapper endpointMapper;
|
||||||
private final RequestCallbacks callbacks;
|
private final RequestCallbacks callbacks;
|
||||||
|
|
||||||
public AccordMessageSink(AccordAgent agent, MessageDelivery messaging, AccordEndpointMapper endpointMapper, RequestCallbacks callbacks)
|
public AccordMessageSink(MessageDelivery messaging, AccordEndpointMapper endpointMapper, RequestCallbacks callbacks)
|
||||||
{
|
{
|
||||||
this.agent = agent;
|
|
||||||
this.messaging = messaging;
|
this.messaging = messaging;
|
||||||
this.endpointMapper = endpointMapper;
|
this.endpointMapper = endpointMapper;
|
||||||
this.callbacks = callbacks;
|
this.callbacks = callbacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccordMessageSink(AccordAgent agent, AccordEndpointMapper endpointMapper, RequestCallbacks callbacks)
|
public AccordMessageSink(AccordEndpointMapper endpointMapper, RequestCallbacks callbacks)
|
||||||
{
|
{
|
||||||
this(agent, MessagingService.instance(), endpointMapper, callbacks);
|
this(MessagingService.instance(), endpointMapper, callbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -177,7 +170,7 @@ public class AccordMessageSink implements MessageSink
|
||||||
InetAddressAndPort endpoint = endpointMapper.mappedEndpointOrNull(to, message);
|
InetAddressAndPort endpoint = endpointMapper.mappedEndpointOrNull(to, message);
|
||||||
if (endpoint == null)
|
if (endpoint == null)
|
||||||
return;
|
return;
|
||||||
logger.trace("Sending {} {} to {}", verb, message.payload, endpoint);
|
|
||||||
messaging.send(message, endpoint);
|
messaging.send(message, endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -222,7 +215,6 @@ public class AccordMessageSink implements MessageSink
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.trace("Sending {} {} to {}", verb, message.payload, endpoint);
|
|
||||||
Cancellable cancellable = callbacks.registerAt(message.id(), executor, callback, to, nowNanos, slowAtNanos, expiresAtNanos, NANOSECONDS);
|
Cancellable cancellable = callbacks.registerAt(message.id(), executor, callback, to, nowNanos, slowAtNanos, expiresAtNanos, NANOSECONDS);
|
||||||
messaging.send(message, endpoint);
|
messaging.send(message, endpoint);
|
||||||
return cancellable;
|
return cancellable;
|
||||||
|
|
@ -240,7 +232,6 @@ public class AccordMessageSink implements MessageSink
|
||||||
if (endpoint == null)
|
if (endpoint == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
logger.trace("Replying {} {} to {}", message.verb(), message.payload, endpoint);
|
|
||||||
messaging.send(message, endpoint);
|
messaging.send(message, endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -252,7 +243,7 @@ public class AccordMessageSink implements MessageSink
|
||||||
InetAddressAndPort endpoint = endpointMapper.mappedEndpointOrNull(replyingTo, message);
|
InetAddressAndPort endpoint = endpointMapper.mappedEndpointOrNull(replyingTo, message);
|
||||||
if (endpoint == null)
|
if (endpoint == null)
|
||||||
return;
|
return;
|
||||||
logger.trace("Replying with failure {} {} to {}", message.verb(), message.payload, endpoint);
|
|
||||||
messaging.send(message, endpoint);
|
messaging.send(message, endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,12 +54,6 @@ class AccordResponseVerbHandler<T extends Reply> implements IVerbHandler<T>
|
||||||
@Override
|
@Override
|
||||||
public void doVerb(Message message)
|
public void doVerb(Message message)
|
||||||
{
|
{
|
||||||
if (!AccordService.instance().shouldAcceptMessages())
|
|
||||||
{
|
|
||||||
dropping.debug(message.verb(), message.from());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Node.Id from = endpointMapper.mappedIdOrNull(message.from(), message);
|
Node.Id from = endpointMapper.mappedIdOrNull(message.from(), message);
|
||||||
if (from == null)
|
if (from == null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,10 @@ import accord.coordinate.CoordinationFailed;
|
||||||
import accord.coordinate.Exhausted;
|
import accord.coordinate.Exhausted;
|
||||||
import accord.coordinate.Preempted;
|
import accord.coordinate.Preempted;
|
||||||
import accord.coordinate.Timeout;
|
import accord.coordinate.Timeout;
|
||||||
import accord.coordinate.TopologyMismatch;
|
|
||||||
import accord.primitives.Seekable;
|
import accord.primitives.Seekable;
|
||||||
import accord.primitives.Seekables;
|
import accord.primitives.Seekables;
|
||||||
import accord.primitives.TxnId;
|
import accord.primitives.TxnId;
|
||||||
|
import accord.topology.TopologyMismatch;
|
||||||
import accord.utils.UnhandledEnum;
|
import accord.utils.UnhandledEnum;
|
||||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||||
import org.apache.cassandra.exceptions.ReadFailureException;
|
import org.apache.cassandra.exceptions.ReadFailureException;
|
||||||
|
|
@ -152,26 +152,6 @@ public class AccordResult<V> extends AsyncFuture<V> implements BiConsumer<V, Thr
|
||||||
{
|
{
|
||||||
report = bookkeeping.newExhausted(txnId, keysOrRanges);
|
report = bookkeeping.newExhausted(txnId, keysOrRanges);
|
||||||
}
|
}
|
||||||
else if (isTxnRequest && coordinationFailed instanceof TopologyMismatch)
|
|
||||||
{
|
|
||||||
// Excluding bugs topology mismatch can occur because a table was dropped in between creating the txn
|
|
||||||
// and executing it.
|
|
||||||
// It could also race with the table stopping/starting being managed by Accord.
|
|
||||||
// The caller can retry if the table indeed exists and is managed by Accord.
|
|
||||||
Set<TableId> txnDroppedTables = txnDroppedTables(keysOrRanges);
|
|
||||||
Tracing.trace("Accord returned topology mismatch: " + coordinationFailed.getMessage());
|
|
||||||
logger.debug("Accord returned topology mismatch", coordinationFailed);
|
|
||||||
bookkeeping.markTopologyMismatch();
|
|
||||||
// Throw IRE in case the caller fails to check if the table still exists
|
|
||||||
if (!txnDroppedTables.isEmpty())
|
|
||||||
{
|
|
||||||
Tracing.trace("Accord txn uses dropped tables {}", txnDroppedTables);
|
|
||||||
logger.debug("Accord txn uses dropped tables {}", txnDroppedTables);
|
|
||||||
throw new InvalidRequestException("Accord transaction uses dropped tables");
|
|
||||||
}
|
|
||||||
trySuccess((V) RetryWithNewProtocolResult.instance);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (fail instanceof TimeoutException)
|
else if (fail instanceof TimeoutException)
|
||||||
{
|
{
|
||||||
report = bookkeeping.newTimeout(txnId, keysOrRanges);
|
report = bookkeeping.newTimeout(txnId, keysOrRanges);
|
||||||
|
|
@ -184,6 +164,26 @@ public class AccordResult<V> extends AsyncFuture<V> implements BiConsumer<V, Thr
|
||||||
if (txnId != null && txnId.isSyncPoint())
|
if (txnId != null && txnId.isSyncPoint())
|
||||||
AccordAgent.onFailedBarrier(txnId, fail);
|
AccordAgent.onFailedBarrier(txnId, fail);
|
||||||
}
|
}
|
||||||
|
else if (isTxnRequest && fail instanceof TopologyMismatch)
|
||||||
|
{
|
||||||
|
// Excluding bugs topology mismatch can occur because a table was dropped in between creating the txn
|
||||||
|
// and executing it.
|
||||||
|
// It could also race with the table stopping/starting being managed by Accord.
|
||||||
|
// The caller can retry if the table indeed exists and is managed by Accord.
|
||||||
|
Set<TableId> txnDroppedTables = txnDroppedTables(keysOrRanges);
|
||||||
|
Tracing.trace("Accord returned topology mismatch: " + coordinationFailed.getMessage());
|
||||||
|
logger.debug("Accord returned topology mismatch", coordinationFailed);
|
||||||
|
bookkeeping.markTopologyMismatch();
|
||||||
|
// Throw IRE in case the caller fails to check if the table still exists
|
||||||
|
if (!txnDroppedTables.isEmpty())
|
||||||
|
{
|
||||||
|
Tracing.trace("Accord txn uses dropped tables {}", txnDroppedTables);
|
||||||
|
logger.debug("Accord txn uses dropped tables {}", txnDroppedTables);
|
||||||
|
throw new InvalidRequestException("Accord transaction uses dropped tables");
|
||||||
|
}
|
||||||
|
trySuccess((V) RetryWithNewProtocolResult.instance);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
else if (fail instanceof RequestTimeoutException || fail instanceof TimeoutException)
|
else if (fail instanceof RequestTimeoutException || fail instanceof TimeoutException)
|
||||||
{
|
{
|
||||||
report = bookkeeping.newTimeout(txnId, keysOrRanges);
|
report = bookkeeping.newTimeout(txnId, keysOrRanges);
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ import accord.local.durability.DurabilityService;
|
||||||
import accord.local.durability.ShardDurability;
|
import accord.local.durability.ShardDurability;
|
||||||
import accord.messages.Reply;
|
import accord.messages.Reply;
|
||||||
import accord.messages.Request;
|
import accord.messages.Request;
|
||||||
import accord.primitives.FullRoute;
|
|
||||||
import accord.primitives.Keys;
|
import accord.primitives.Keys;
|
||||||
import accord.primitives.Ranges;
|
import accord.primitives.Ranges;
|
||||||
import accord.primitives.Seekable;
|
import accord.primitives.Seekable;
|
||||||
|
|
@ -149,6 +148,7 @@ import static org.apache.cassandra.db.SystemKeyspace.BootstrapState.COMPLETED;
|
||||||
import static org.apache.cassandra.journal.Params.ReplayMode.RESET;
|
import static org.apache.cassandra.journal.Params.ReplayMode.RESET;
|
||||||
import static org.apache.cassandra.metrics.ClientRequestsMetricsHolder.accordReadBookkeeping;
|
import static org.apache.cassandra.metrics.ClientRequestsMetricsHolder.accordReadBookkeeping;
|
||||||
import static org.apache.cassandra.metrics.ClientRequestsMetricsHolder.accordWriteBookkeeping;
|
import static org.apache.cassandra.metrics.ClientRequestsMetricsHolder.accordWriteBookkeeping;
|
||||||
|
import static org.apache.cassandra.service.accord.AccordTopology.tcmIdToAccord;
|
||||||
import static org.apache.cassandra.service.consensus.migration.ConsensusRequestRouter.getTableMetadata;
|
import static org.apache.cassandra.service.consensus.migration.ConsensusRequestRouter.getTableMetadata;
|
||||||
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
|
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
|
||||||
|
|
||||||
|
|
@ -233,7 +233,7 @@ public class AccordService implements IAccordService, Shutdownable
|
||||||
private final AccordResponseVerbHandler<? extends Reply> responseHandler;
|
private final AccordResponseVerbHandler<? extends Reply> responseHandler;
|
||||||
|
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
private State state = State.INIT;
|
private volatile State state = State.INIT;
|
||||||
|
|
||||||
private static final IAccordService NOOP_SERVICE = new NoOpAccordService();
|
private static final IAccordService NOOP_SERVICE = new NoOpAccordService();
|
||||||
|
|
||||||
|
|
@ -254,11 +254,26 @@ public class AccordService implements IAccordService, Shutdownable
|
||||||
unsafeInstance = instance = NOOP_SERVICE;
|
unsafeInstance = instance = NOOP_SERVICE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IAccordService tryGetUnsafe()
|
||||||
|
{
|
||||||
|
return unsafeInstance;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isSetup()
|
public static boolean isSetup()
|
||||||
{
|
{
|
||||||
return instance != null;
|
return instance != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isSetupOrStarting()
|
||||||
|
{
|
||||||
|
return unsafeInstance != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isStarted()
|
||||||
|
{
|
||||||
|
return isSetup() && unsafeInstance instanceof AccordService && ((AccordService) unsafeInstance).state == State.STARTED;
|
||||||
|
}
|
||||||
|
|
||||||
public static IVerbHandler<Void> watermarkHandlerOrNoop()
|
public static IVerbHandler<Void> watermarkHandlerOrNoop()
|
||||||
{
|
{
|
||||||
if (!isSetup()) return ignore -> {};
|
if (!isSetup()) return ignore -> {};
|
||||||
|
|
@ -268,38 +283,47 @@ public class AccordService implements IAccordService, Shutdownable
|
||||||
|
|
||||||
public static IVerbHandler<? extends Request> requestHandlerOrNoop()
|
public static IVerbHandler<? extends Request> requestHandlerOrNoop()
|
||||||
{
|
{
|
||||||
if (!isSetup()) return ignore -> {};
|
if (instance == null) return ignore -> {};
|
||||||
return instance().requestHandler();
|
return instance.requestHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IVerbHandler<? extends Reply> responseHandlerOrNoop()
|
public static IVerbHandler<? extends Reply> responseHandlerOrNoop()
|
||||||
{
|
{
|
||||||
if (!isSetup()) return ignore -> {};
|
if (unsafeInstance == null) return ignore -> {};
|
||||||
return instance().responseHandler();
|
return unsafeInstance.responseHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public synchronized static AccordService startup(NodeId tcmId)
|
public synchronized static void localStartup(NodeId tcmId)
|
||||||
{
|
{
|
||||||
|
Invariants.require(instance == null);
|
||||||
if (!DatabaseDescriptor.getAccordTransactionsEnabled())
|
if (!DatabaseDescriptor.getAccordTransactionsEnabled())
|
||||||
{
|
{
|
||||||
unsafeInstance = instance = NOOP_SERVICE;
|
unsafeInstance = instance = NOOP_SERVICE;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AccordService as = new AccordService(tcmIdToAccord(tcmId));
|
||||||
|
unsafeInstance = as;
|
||||||
|
as.localStartup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (instance != null)
|
public synchronized static AccordService distributedStartup()
|
||||||
return (AccordService) instance;
|
{
|
||||||
|
if (unsafeInstance == NOOP_SERVICE)
|
||||||
|
return null;
|
||||||
|
|
||||||
AccordService as = new AccordService(AccordTopology.tcmIdToAccord(tcmId));
|
AccordService as = (AccordService) unsafeInstance;
|
||||||
unsafeInstance = as;
|
if (as.state != State.INIT)
|
||||||
as.startup();
|
return as;
|
||||||
|
|
||||||
|
as.distributedStartupInternal();
|
||||||
instance = as;
|
instance = as;
|
||||||
|
|
||||||
AccordReplicaMetrics.touch();
|
AccordReplicaMetrics.touch();
|
||||||
AccordSystemMetrics.touch();
|
AccordSystemMetrics.touch();
|
||||||
AccordViolationHandler.setup();
|
AccordViolationHandler.setup();
|
||||||
|
|
||||||
WatermarkCollector.fetchAndReportWatermarksAsync(as.topology());
|
|
||||||
return as;
|
return as;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -323,12 +347,6 @@ public class AccordService implements IAccordService, Shutdownable
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldAcceptMessages()
|
|
||||||
{
|
|
||||||
return state == State.STARTED && journal.started();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IAccordService instance()
|
public static IAccordService instance()
|
||||||
{
|
{
|
||||||
IAccordService i = instance;
|
IAccordService i = instance;
|
||||||
|
|
@ -365,7 +383,7 @@ public class AccordService implements IAccordService, Shutdownable
|
||||||
this.endpointMapper = new EndpointMapping.Updateable();
|
this.endpointMapper = new EndpointMapping.Updateable();
|
||||||
this.topologyService = new AccordTopologyService(localId, endpointMapper);
|
this.topologyService = new AccordTopologyService(localId, endpointMapper);
|
||||||
this.fastPathCoordinator = AccordFastPathCoordinator.create(localId, endpointMapper);
|
this.fastPathCoordinator = AccordFastPathCoordinator.create(localId, endpointMapper);
|
||||||
this.messageSink = new AccordMessageSink(agent, endpointMapper, callbacks);
|
this.messageSink = new AccordMessageSink(endpointMapper, callbacks);
|
||||||
this.node = new Node(localId,
|
this.node = new Node(localId,
|
||||||
messageSink,
|
messageSink,
|
||||||
topologyService,
|
topologyService,
|
||||||
|
|
@ -391,7 +409,7 @@ public class AccordService implements IAccordService, Shutdownable
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void startup()
|
public synchronized void localStartup()
|
||||||
{
|
{
|
||||||
if (state != State.INIT)
|
if (state != State.INIT)
|
||||||
return;
|
return;
|
||||||
|
|
@ -428,8 +446,13 @@ public class AccordService implements IAccordService, Shutdownable
|
||||||
{
|
{
|
||||||
node.unsafeSetReplaying(false);
|
node.unsafeSetReplaying(false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void distributedStartupInternal()
|
||||||
|
{
|
||||||
|
finishTopologyInitialization();
|
||||||
|
WatermarkCollector.fetchAndReportWatermarksAsync(topology());
|
||||||
|
|
||||||
finishInitialization();
|
|
||||||
catchup();
|
catchup();
|
||||||
|
|
||||||
fastPathCoordinator.start();
|
fastPathCoordinator.start();
|
||||||
|
|
@ -439,9 +462,12 @@ public class AccordService implements IAccordService, Shutdownable
|
||||||
Ints.checkedCast(getAccordShardDurabilityMaxSplits()),
|
Ints.checkedCast(getAccordShardDurabilityMaxSplits()),
|
||||||
Ints.checkedCast(getAccordShardDurabilityCycle(SECONDS)), SECONDS);
|
Ints.checkedCast(getAccordShardDurabilityCycle(SECONDS)), SECONDS);
|
||||||
node.durability().global().setGlobalCycleTime(Ints.checkedCast(getAccordGlobalDurabilityCycle(SECONDS)), SECONDS);
|
node.durability().global().setGlobalCycleTime(Ints.checkedCast(getAccordGlobalDurabilityCycle(SECONDS)), SECONDS);
|
||||||
// Only enable durability scheduling _after_ we have fully replayed journal
|
// Only enable durability scheduling and progress logs _after_ we have fully replayed journal
|
||||||
node.durability().start();
|
node.durability().start();
|
||||||
|
// we set ourselves to STARTED before starting progress logs as this is the condition we use to decide if we
|
||||||
|
// start the progress log on command store initialisation (so creates a synchronisation point)
|
||||||
state = State.STARTED;
|
state = State.STARTED;
|
||||||
|
node.commandStores().forAll("", safeStore -> safeStore.progressLog().start());
|
||||||
}
|
}
|
||||||
|
|
||||||
void catchup()
|
void catchup()
|
||||||
|
|
@ -477,13 +503,18 @@ public class AccordService implements IAccordService, Shutdownable
|
||||||
|
|
||||||
Throwable failed = f.cause();
|
Throwable failed = f.cause();
|
||||||
if (failed != null)
|
if (failed != null)
|
||||||
throw new RuntimeException("Could not catch up with peers", failed);
|
{
|
||||||
|
if (spec.catchup_on_start_exit_on_failure)
|
||||||
|
throw new RuntimeException("Could not catch up with peers", failed);
|
||||||
|
|
||||||
|
logger.error("Could not catch up with peers; continuing to startup");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
long end = nanoTime();
|
long end = nanoTime();
|
||||||
double seconds = NANOSECONDS.toMillis(end - start)/1000.0;
|
double seconds = NANOSECONDS.toMillis(end - start)/1000.0;
|
||||||
logger.info("Finished catching up with all quorums. {}s elapsed.", String.format("%.2f", seconds));
|
logger.info("Finished catching up with all quorums. {}s elapsed.", String.format("%.2f", seconds));
|
||||||
|
|
||||||
// TODO (expected): make configurable
|
|
||||||
if (seconds <= spec.catchup_on_start_success_latency.toSeconds())
|
if (seconds <= spec.catchup_on_start_success_latency.toSeconds())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -511,9 +542,9 @@ public class AccordService implements IAccordService, Shutdownable
|
||||||
/**
|
/**
|
||||||
* Startup is broken up in two phases: local and distributed startup. During local startup, we replay up to
|
* Startup is broken up in two phases: local and distributed startup. During local startup, we replay up to
|
||||||
* the latest epoch known to the node prior to restart. After that, we replay journal itself, and only after
|
* the latest epoch known to the node prior to restart. After that, we replay journal itself, and only after
|
||||||
* that we finish initializaiton and replay the rest of epochs.
|
* that we finish initialization and replay the rest of epochs.
|
||||||
*/
|
*/
|
||||||
void finishInitialization()
|
void finishTopologyInitialization()
|
||||||
{
|
{
|
||||||
endpointMapper.updateMapping(ClusterMetadata.current());
|
endpointMapper.updateMapping(ClusterMetadata.current());
|
||||||
TopologyManager topology = node.topology();
|
TopologyManager topology = node.topology();
|
||||||
|
|
@ -686,10 +717,9 @@ public class AccordService implements IAccordService, Shutdownable
|
||||||
private AsyncChain<Void> syncInternal(Timestamp minBound, Keys keys, DurabilityService.SyncLocal syncLocal, DurabilityService.SyncRemote syncRemote)
|
private AsyncChain<Void> syncInternal(Timestamp minBound, Keys keys, DurabilityService.SyncLocal syncLocal, DurabilityService.SyncRemote syncRemote)
|
||||||
{
|
{
|
||||||
TxnId txnId = node.nextTxnId(minBound, keys, Write);
|
TxnId txnId = node.nextTxnId(minBound, keys, Write);
|
||||||
FullRoute<?> route = node.computeRoute(txnId, keys);
|
|
||||||
return node.withEpochAtLeast(txnId.epoch(), null, () -> {
|
return node.withEpochAtLeast(txnId.epoch(), null, () -> {
|
||||||
Txn txn = new Txn.InMemory(Write, keys, TxnRead.createNoOpRead(keys), TxnQuery.UNSAFE_EMPTY, TxnUpdate.empty(), new TableMetadatasAndKeys(TableMetadatas.none(), keys));
|
Txn txn = new Txn.InMemory(Write, keys, TxnRead.createNoOpRead(keys), TxnQuery.UNSAFE_EMPTY, TxnUpdate.empty(), new TableMetadatasAndKeys(TableMetadatas.none(), keys));
|
||||||
return CoordinateTransaction.coordinate(node, route, txnId, txn)
|
return CoordinateTransaction.coordinate(node, txnId, txn)
|
||||||
.mapToNull();
|
.mapToNull();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -782,7 +812,7 @@ public class AccordService implements IAccordService, Shutdownable
|
||||||
return keys;
|
return keys;
|
||||||
|
|
||||||
TableId tableId = tableId(keys, r -> ((AccordRoutableKey)r).table());
|
TableId tableId = tableId(keys, r -> ((AccordRoutableKey)r).table());
|
||||||
return sliceToAccord(tableId, keys, Keys::slice);
|
return sliceToAccord(tableId, keys, Keys::overlapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Ranges intersecting(Ranges ranges)
|
public static Ranges intersecting(Ranges ranges)
|
||||||
|
|
@ -791,7 +821,7 @@ public class AccordService implements IAccordService, Shutdownable
|
||||||
return ranges;
|
return ranges;
|
||||||
|
|
||||||
TableId tableId = tableId(ranges, r -> ((TokenRange)r).table());
|
TableId tableId = tableId(ranges, r -> ((TokenRange)r).table());
|
||||||
return sliceToAccord(tableId, ranges, Ranges::slice);
|
return sliceToAccord(tableId, ranges, Ranges::overlapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <C extends Seekables<?, ?>> C sliceToAccord(TableId tableId, C collection, BiFunction<C, Ranges, C> slice)
|
private static <C extends Seekables<?, ?>> C sliceToAccord(TableId tableId, C collection, BiFunction<C, Ranges, C> slice)
|
||||||
|
|
@ -974,7 +1004,7 @@ public class AccordService implements IAccordService, Shutdownable
|
||||||
@Override
|
@Override
|
||||||
public void shutdownAndWait(long timeout, TimeUnit unit)
|
public void shutdownAndWait(long timeout, TimeUnit unit)
|
||||||
{
|
{
|
||||||
if (!ExecutorUtils.shutdownSequentiallyAndWait(shutdownableSubsystems(), timeout, unit))
|
if (!ExecutorUtils.shutdownThenWait(shutdownableSubsystems(), timeout, unit))
|
||||||
logger.error("One or more subsystems did not shut down cleanly.");
|
logger.error("One or more subsystems did not shut down cleanly.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -728,12 +728,12 @@ public abstract class AccordTask<R> extends SubmittableTask implements Function<
|
||||||
|
|
||||||
public void fail(Throwable throwable)
|
public void fail(Throwable throwable)
|
||||||
{
|
{
|
||||||
commandStore.agent().onUncaughtException(throwable);
|
commandStore.agent().onException(throwable);
|
||||||
if (state.isComplete())
|
if (state.isComplete())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (commandStore.hasSafeStore())
|
if (commandStore.hasSafeStore())
|
||||||
commandStore.agent().onUncaughtException(new IllegalStateException(String.format("Failure to cleanup safe store for %s; status=%s", this, state), throwable));
|
commandStore.agent().onException(new IllegalStateException(String.format("Failure to cleanup safe store for %s; status=%s", this, state), throwable));
|
||||||
|
|
||||||
state(FAILING);
|
state(FAILING);
|
||||||
if (callback != null)
|
if (callback != null)
|
||||||
|
|
@ -747,12 +747,12 @@ public abstract class AccordTask<R> extends SubmittableTask implements Function<
|
||||||
{
|
{
|
||||||
if (newFailure)
|
if (newFailure)
|
||||||
{
|
{
|
||||||
commandStore.agent().onUncaughtException(throwable);
|
commandStore.agent().onException(throwable);
|
||||||
if (state.isComplete())
|
if (state.isComplete())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (commandStore.hasSafeStore())
|
if (commandStore.hasSafeStore())
|
||||||
commandStore.agent().onUncaughtException(new IllegalStateException(String.format("Failure to cleanup safe store for %s; status=%s", this, state), throwable));
|
commandStore.agent().onException(new IllegalStateException(String.format("Failure to cleanup safe store for %s; status=%s", this, state), throwable));
|
||||||
}
|
}
|
||||||
|
|
||||||
state(FAILED);
|
state(FAILED);
|
||||||
|
|
@ -860,7 +860,7 @@ public abstract class AccordTask<R> extends SubmittableTask implements Function<
|
||||||
catch (Throwable t)
|
catch (Throwable t)
|
||||||
{
|
{
|
||||||
releaseResourcesSlow(caches, t);
|
releaseResourcesSlow(caches, t);
|
||||||
commandStore.agent().onUncaughtException(t);
|
commandStore.agent().onException(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -760,6 +760,9 @@ public class AccordTracing extends AccordCoordinatorMetrics.Listener
|
||||||
|
|
||||||
public Tracing trace(TxnId txnId, @Nullable Participants<?> participants, CoordinationKind kind)
|
public Tracing trace(TxnId txnId, @Nullable Participants<?> participants, CoordinationKind kind)
|
||||||
{
|
{
|
||||||
|
if (kind == CoordinationKind.FetchDurableBefore)
|
||||||
|
return (cs, msg) -> logger.info("Catchup/FetchDurableBefore: {}", msg);
|
||||||
|
|
||||||
if (!txnIdMap.containsKey(txnId) && null == maybeTrace(txnId, participants, kind, NewOrFailure.NEW, traceNewPatterns))
|
if (!txnIdMap.containsKey(txnId) && null == maybeTrace(txnId, participants, kind, NewOrFailure.NEW, traceNewPatterns))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,12 +46,6 @@ public class AccordVerbHandler<T extends Request> implements IVerbHandler<T>
|
||||||
@Override
|
@Override
|
||||||
public void doVerb(Message<T> message) throws IOException
|
public void doVerb(Message<T> message) throws IOException
|
||||||
{
|
{
|
||||||
if (!AccordService.instance().shouldAcceptMessages())
|
|
||||||
{
|
|
||||||
dropping.debug(message.verb(), message.from());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.trace("Receiving {} from {}", message.payload, message.from());
|
logger.trace("Receiving {} from {}", message.payload, message.from());
|
||||||
|
|
||||||
T request = message.payload;
|
T request = message.payload;
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@ public interface IAccordService
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isEnabled();
|
boolean isEnabled();
|
||||||
|
|
||||||
long currentEpoch();
|
long currentEpoch();
|
||||||
|
|
||||||
void setCacheSize(long kb);
|
void setCacheSize(long kb);
|
||||||
|
|
@ -115,7 +116,7 @@ public interface IAccordService
|
||||||
|
|
||||||
TopologyManager topology();
|
TopologyManager topology();
|
||||||
|
|
||||||
void startup();
|
void localStartup();
|
||||||
|
|
||||||
Future<Void> flushCaches();
|
Future<Void> flushCaches();
|
||||||
void markShuttingDown();
|
void markShuttingDown();
|
||||||
|
|
@ -186,8 +187,6 @@ public interface IAccordService
|
||||||
|
|
||||||
Params journalConfiguration();
|
Params journalConfiguration();
|
||||||
|
|
||||||
boolean shouldAcceptMessages();
|
|
||||||
|
|
||||||
Node node();
|
Node node();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -274,7 +273,7 @@ public interface IAccordService
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startup()
|
public void localStartup()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -369,12 +368,6 @@ public interface IAccordService
|
||||||
throw new UnsupportedOperationException("Cannot return configuration when accord.enabled = false in cassandra.yaml");
|
throw new UnsupportedOperationException("Cannot return configuration when accord.enabled = false in cassandra.yaml");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldAcceptMessages()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node node()
|
public Node node()
|
||||||
{
|
{
|
||||||
|
|
@ -490,9 +483,9 @@ public interface IAccordService
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startup()
|
public void localStartup()
|
||||||
{
|
{
|
||||||
delegate.startup();
|
delegate.localStartup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -573,12 +566,6 @@ public interface IAccordService
|
||||||
return delegate.journalConfiguration();
|
return delegate.journalConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldAcceptMessages()
|
|
||||||
{
|
|
||||||
return delegate.shouldAcceptMessages();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node node()
|
public Node node()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ public class ImmediateAsyncExecutor implements AbstractAsyncExecutor
|
||||||
}
|
}
|
||||||
catch (Throwable t)
|
catch (Throwable t)
|
||||||
{
|
{
|
||||||
AccordAgent.handleUncaughtException(t);
|
AccordAgent.handleException(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,6 @@ public class WatermarkCollector implements TopologyListener
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Snapshot snapshot = m.payload;
|
Snapshot snapshot = m.payload;
|
||||||
long minEpoch = topologyManager.minEpoch();
|
|
||||||
forEachEpoch(topologyManager::onEpochClosed, snapshot.closed);
|
forEachEpoch(topologyManager::onEpochClosed, snapshot.closed);
|
||||||
forEachEpoch(topologyManager::onEpochRetired, snapshot.retired);
|
forEachEpoch(topologyManager::onEpochRetired, snapshot.retired);
|
||||||
for (Map.Entry<Long, Long> e : snapshot.synced.entrySet())
|
for (Map.Entry<Long, Long> e : snapshot.synced.entrySet())
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ package org.apache.cassandra.service.accord.api;
|
||||||
|
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
@ -36,6 +37,7 @@ import accord.api.ProgressLog.BlockedUntil;
|
||||||
import accord.api.RoutingKey;
|
import accord.api.RoutingKey;
|
||||||
import accord.api.Tracing;
|
import accord.api.Tracing;
|
||||||
import accord.coordinate.Coordination;
|
import accord.coordinate.Coordination;
|
||||||
|
import accord.coordinate.Timeout;
|
||||||
import accord.local.Command;
|
import accord.local.Command;
|
||||||
import accord.local.Node;
|
import accord.local.Node;
|
||||||
import accord.local.SafeCommand;
|
import accord.local.SafeCommand;
|
||||||
|
|
@ -193,24 +195,23 @@ public class AccordAgent implements Agent, OwnershipEventListener
|
||||||
logger.error("This replica has become stale for {} as of {}", ranges, staleSince);
|
logger.error("This replica has become stale for {} as of {}", ranges, staleSince);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static void handleException(Throwable t)
|
||||||
public void onUncaughtException(Throwable t)
|
|
||||||
{
|
{
|
||||||
handleUncaughtException(t);
|
if (t instanceof RequestTimeoutException || t instanceof CancellationException || t instanceof TimeoutException || t instanceof Timeout)
|
||||||
}
|
|
||||||
|
|
||||||
public static void handleUncaughtException(Throwable t)
|
|
||||||
{
|
|
||||||
if (t instanceof RequestTimeoutException || t instanceof CancellationException)
|
|
||||||
return;
|
return;
|
||||||
JVMStabilityInspector.uncaughtException(Thread.currentThread(), t);
|
JVMStabilityInspector.uncaughtException(Thread.currentThread(), t);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCaughtException(Throwable t, String context)
|
public void onException(Throwable t)
|
||||||
{
|
{
|
||||||
logger.warn(context, t);
|
handleException(t);
|
||||||
JVMStabilityInspector.inspectThrowable(t);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onException(Throwable t, String context)
|
||||||
|
{
|
||||||
|
handleException(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -106,8 +106,18 @@ public class AccordInteropAdapter extends TxnAdapter
|
||||||
if (updateKind != AccordUpdate.Kind.UNRECOVERABLE_REPAIR && (consistencyLevel == null || consistencyLevel == ConsistencyLevel.ONE || txn.read().keys().isEmpty()))
|
if (updateKind != AccordUpdate.Kind.UNRECOVERABLE_REPAIR && (consistencyLevel == null || consistencyLevel == ConsistencyLevel.ONE || txn.read().keys().isEmpty()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
new AccordInteropExecution(node, txnId, txn, updateKind, route, ballot, executeAt, deps, callback, executor, consistencyLevel, endpointMapper)
|
AccordInteropExecution execution;
|
||||||
.start();
|
try
|
||||||
|
{
|
||||||
|
execution = new AccordInteropExecution(node, txnId, txn, updateKind, route, ballot, executeAt, deps, callback, executor, consistencyLevel, endpointMapper);
|
||||||
|
}
|
||||||
|
catch (Throwable t)
|
||||||
|
{
|
||||||
|
callback.accept(null, t);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
execution.start();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,9 +129,19 @@ public class AccordInteropAdapter extends TxnAdapter
|
||||||
if (consistencyLevel == null || consistencyLevel == ConsistencyLevel.ANY || writes.isEmpty())
|
if (consistencyLevel == null || consistencyLevel == ConsistencyLevel.ANY || writes.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Topologies all = execution(node, any, sendTo, selectSendTo, fullRoute, txnId, executeAt);
|
AccordInteropPersist persist;
|
||||||
new AccordInteropPersist(node, executor, all, txnId, require, ballot, txn, executeAt, deps, writes, result, fullRoute, consistencyLevel, CoordinationFlags.none(), informDurableOnDone, Minimal, callback)
|
try
|
||||||
.start();
|
{
|
||||||
|
Topologies all = execution(node, any, sendTo, selectSendTo, fullRoute, txnId, executeAt);
|
||||||
|
persist = new AccordInteropPersist(node, executor, all, txnId, require, ballot, txn, executeAt, deps, writes, result, fullRoute, consistencyLevel, CoordinationFlags.none(), informDurableOnDone, Minimal, callback);
|
||||||
|
}
|
||||||
|
catch (Throwable t)
|
||||||
|
{
|
||||||
|
callback.accept(null, t);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
persist.start();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,7 @@ public class AccordInteropApply extends Apply implements LocalListeners.ComplexL
|
||||||
else if (failure != null)
|
else if (failure != null)
|
||||||
{
|
{
|
||||||
node.reply(replyTo, replyContext, null, failure);
|
node.reply(replyTo, replyContext, null, failure);
|
||||||
node.agent().onUncaughtException(failure);
|
node.agent().onException(failure);
|
||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ import accord.topology.ActiveEpochs;
|
||||||
import accord.topology.Shard;
|
import accord.topology.Shard;
|
||||||
import accord.topology.Topologies;
|
import accord.topology.Topologies;
|
||||||
import accord.topology.Topology;
|
import accord.topology.Topology;
|
||||||
|
import accord.topology.TopologyException;
|
||||||
import accord.utils.Invariants;
|
import accord.utils.Invariants;
|
||||||
import accord.utils.UnhandledEnum;
|
import accord.utils.UnhandledEnum;
|
||||||
import accord.utils.async.AsyncChain;
|
import accord.utils.async.AsyncChain;
|
||||||
|
|
@ -139,7 +140,7 @@ public class AccordInteropExecution implements ReadCoordinator
|
||||||
private volatile long uniqueHlc;
|
private volatile long uniqueHlc;
|
||||||
|
|
||||||
public AccordInteropExecution(Node node, TxnId txnId, Txn txn, AccordUpdate.Kind updateKind, FullRoute<?> route, Ballot ballot, Timestamp executeAt, Deps deps, BiConsumer<? super Result, Throwable> callback,
|
public AccordInteropExecution(Node node, TxnId txnId, Txn txn, AccordUpdate.Kind updateKind, FullRoute<?> route, Ballot ballot, Timestamp executeAt, Deps deps, BiConsumer<? super Result, Throwable> callback,
|
||||||
SequentialAsyncExecutor executor, ConsistencyLevel consistencyLevel, AccordEndpointMapper endpointMapper)
|
SequentialAsyncExecutor executor, ConsistencyLevel consistencyLevel, AccordEndpointMapper endpointMapper) throws TopologyException
|
||||||
{
|
{
|
||||||
requireArgument(!txn.read().keys().isEmpty() || updateKind == AccordUpdate.Kind.UNRECOVERABLE_REPAIR);
|
requireArgument(!txn.read().keys().isEmpty() || updateKind == AccordUpdate.Kind.UNRECOVERABLE_REPAIR);
|
||||||
this.node = node;
|
this.node = node;
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,6 @@ import org.apache.cassandra.utils.concurrent.Future;
|
||||||
|
|
||||||
import static accord.local.durability.DurabilityService.SyncLocal.NoLocal;
|
import static accord.local.durability.DurabilityService.SyncLocal.NoLocal;
|
||||||
import static accord.local.durability.DurabilityService.SyncRemote.All;
|
import static accord.local.durability.DurabilityService.SyncRemote.All;
|
||||||
import static accord.local.durability.DurabilityService.SyncRemote.Quorum;
|
|
||||||
import static accord.primitives.Timestamp.mergeMax;
|
import static accord.primitives.Timestamp.mergeMax;
|
||||||
import static accord.primitives.Timestamp.minForEpoch;
|
import static accord.primitives.Timestamp.minForEpoch;
|
||||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||||
|
|
@ -79,15 +78,15 @@ public class AccordRepair
|
||||||
private volatile Throwable shouldAbort = null;
|
private volatile Throwable shouldAbort = null;
|
||||||
private volatile Thread waiting;
|
private volatile Thread waiting;
|
||||||
|
|
||||||
public AccordRepair(SharedContext ctx, ColumnFamilyStore cfs, TimeUUID repairId, String keyspace, Collection<Range<Token>> ranges, boolean requireAllEndpoints, List<InetAddressAndPort> endpoints)
|
public AccordRepair(SharedContext ctx, ColumnFamilyStore cfs, TimeUUID repairId, String keyspace, Collection<Range<Token>> ranges, SyncRemote syncRemote, List<InetAddressAndPort> endpoints)
|
||||||
{
|
{
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.cfs = cfs;
|
this.cfs = cfs;
|
||||||
this.repairId = repairId;
|
this.repairId = repairId;
|
||||||
|
this.syncRemote = syncRemote;
|
||||||
// TODO (desired): support unsafe configuration where we permit less than a quorum,
|
// TODO (desired): support unsafe configuration where we permit less than a quorum,
|
||||||
// but this is challenging to do safely as repair has no concept of participants from earlier epochs
|
// but this is challenging to do safely as repair has no concept of participants from earlier epochs
|
||||||
this.syncRemote = requireAllEndpoints ? All : Quorum;
|
if (syncRemote != All && endpoints != null)
|
||||||
if (endpoints != null)
|
|
||||||
{
|
{
|
||||||
including = new ArrayList<>(endpoints.size());
|
including = new ArrayList<>(endpoints.size());
|
||||||
AccordEndpointMapper mapper = AccordService.instance().endpointMapper();
|
AccordEndpointMapper mapper = AccordService.instance().endpointMapper();
|
||||||
|
|
|
||||||
|
|
@ -97,34 +97,38 @@ public abstract class AbstractSortedCollector<T, C> extends AbstractList<T>
|
||||||
return add;
|
return add;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear()
|
|
||||||
{
|
|
||||||
if (count > 1)
|
|
||||||
cachedAny().forceDiscard((Object[])buffer, count);
|
|
||||||
buffer = null;
|
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public C build()
|
public C build()
|
||||||
{
|
{
|
||||||
|
C result;
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
{
|
{
|
||||||
return empty();
|
result = empty();
|
||||||
}
|
}
|
||||||
else if (count == 1)
|
else if (count == 1)
|
||||||
{
|
{
|
||||||
return of((T)buffer);
|
result = of((T)buffer);
|
||||||
}
|
}
|
||||||
else if (count < BTREE_THRESHOLD)
|
else if (count < BTREE_THRESHOLD)
|
||||||
{
|
{
|
||||||
C result = copy((Object[])buffer, count);
|
result = copy((Object[])buffer, count);
|
||||||
cachedAny().forceDiscard((Object[])buffer, count);
|
cachedAny().forceDiscard((Object[])buffer, count);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return copyBtree((Object[])buffer, count);
|
result = copyBtree((Object[])buffer, count);
|
||||||
}
|
}
|
||||||
|
buffer = null;
|
||||||
|
count = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void clear()
|
||||||
|
{
|
||||||
|
if (count > 1 && count < BTREE_THRESHOLD)
|
||||||
|
cachedAny().forceDiscard((Object[])buffer, count);
|
||||||
|
buffer = null;
|
||||||
|
count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ public class CommandSerializers
|
||||||
if (executeAt.equals(Timestamp.NONE))
|
if (executeAt.equals(Timestamp.NONE))
|
||||||
return size + TypeSizes.sizeofUnsignedVInt(0L);
|
return size + TypeSizes.sizeofUnsignedVInt(0L);
|
||||||
|
|
||||||
size += TypeSizes.sizeofUnsignedVInt(executeAt.epoch() - txnId.epoch());
|
size += TypeSizes.sizeofUnsignedVInt(1 + executeAt.epoch() - txnId.epoch());
|
||||||
}
|
}
|
||||||
size += TypeSizes.sizeofUnsignedVInt(executeAt.hlc() - txnId.hlc());
|
size += TypeSizes.sizeofUnsignedVInt(executeAt.hlc() - txnId.hlc());
|
||||||
size += TypeSizes.sizeofUnsignedVInt(executeAt.node.id);
|
size += TypeSizes.sizeofUnsignedVInt(executeAt.node.id);
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ import org.apache.cassandra.utils.CollectionSerializers;
|
||||||
import org.apache.cassandra.utils.ObjectSizes;
|
import org.apache.cassandra.utils.ObjectSizes;
|
||||||
import org.apache.cassandra.utils.Pair;
|
import org.apache.cassandra.utils.Pair;
|
||||||
|
|
||||||
|
import static accord.primitives.Routables.Slice.Minimal;
|
||||||
import static accord.utils.ArrayBuffers.cachedInts;
|
import static accord.utils.ArrayBuffers.cachedInts;
|
||||||
import static accord.utils.Invariants.requireArgument;
|
import static accord.utils.Invariants.requireArgument;
|
||||||
import static accord.utils.SortedArrays.Search.CEIL;
|
import static accord.utils.SortedArrays.Search.CEIL;
|
||||||
|
|
@ -637,13 +638,13 @@ public class TxnUpdate extends AccordUpdate
|
||||||
@Override
|
@Override
|
||||||
public TxnUpdate slice(Ranges ranges)
|
public TxnUpdate slice(Ranges ranges)
|
||||||
{
|
{
|
||||||
return getTxnUpdate(keys -> keys.slice(ranges));
|
return getTxnUpdate(keys -> keys.slice(ranges, Minimal));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TxnUpdate intersecting(Participants<?> participants)
|
public TxnUpdate intersecting(Participants<?> participants)
|
||||||
{
|
{
|
||||||
return getTxnUpdate(keys -> keys.intersecting(participants));
|
return getTxnUpdate(keys -> keys.intersecting(participants, Minimal));
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,6 @@ import org.apache.cassandra.service.accord.api.PartitionKey;
|
||||||
import org.apache.cassandra.service.accord.serializers.TableMetadatas;
|
import org.apache.cassandra.service.accord.serializers.TableMetadatas;
|
||||||
import org.apache.cassandra.service.accord.serializers.TableMetadatasAndKeys;
|
import org.apache.cassandra.service.accord.serializers.TableMetadatasAndKeys;
|
||||||
import org.apache.cassandra.service.accord.serializers.Version;
|
import org.apache.cassandra.service.accord.serializers.Version;
|
||||||
import org.apache.cassandra.utils.BooleanSerializer;
|
|
||||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||||
import org.apache.cassandra.utils.ObjectSizes;
|
import org.apache.cassandra.utils.ObjectSizes;
|
||||||
import org.apache.cassandra.utils.SimpleBitSetSerializers;
|
import org.apache.cassandra.utils.SimpleBitSetSerializers;
|
||||||
|
|
@ -545,7 +544,7 @@ public class TxnWrite extends AbstractKeySorted<TxnWrite.Update> implements Writ
|
||||||
public void skip(Seekables keys, DataInputPlus in, Version version) throws IOException
|
public void skip(Seekables keys, DataInputPlus in, Version version) throws IOException
|
||||||
{
|
{
|
||||||
TableMetadatas tables = TableMetadatas.deserializeSelf(in);
|
TableMetadatas tables = TableMetadatas.deserializeSelf(in);
|
||||||
BooleanSerializer.serializer.deserialize(in);
|
SimpleBitSetSerializers.any.deserialize(in);
|
||||||
skipArray(new TableMetadatasAndKeys(tables, keys), in, version, Update.serializer);
|
skipArray(new TableMetadatasAndKeys(tables, keys), in, version, Update.serializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -380,7 +380,7 @@ public abstract class ConsensusTableMigration
|
||||||
boolean optimiseStreams = false;
|
boolean optimiseStreams = false;
|
||||||
boolean ignoreUnreplicatedKeyspaces = true;
|
boolean ignoreUnreplicatedKeyspaces = true;
|
||||||
boolean dontPurgeTombstones = false;
|
boolean dontPurgeTombstones = false;
|
||||||
RepairOption repairOption = new RepairOption(RepairParallelism.PARALLEL, primaryRange, incremental, trace, numJobThreads, intersectingRanges, pullRepair, forceRepair, PreviewKind.NONE, optimiseStreams, ignoreUnreplicatedKeyspaces, repairData, repairPaxos, dontPurgeTombstones, repairAccord);
|
RepairOption repairOption = new RepairOption(RepairParallelism.PARALLEL, primaryRange, incremental, trace, numJobThreads, intersectingRanges, pullRepair, forceRepair, PreviewKind.NONE, optimiseStreams, ignoreUnreplicatedKeyspaces, repairData, repairPaxos, dontPurgeTombstones, repairAccord, false);
|
||||||
tables.forEach(table -> repairOption.getColumnFamilies().add(table.tableName));
|
tables.forEach(table -> repairOption.getColumnFamilies().add(table.tableName));
|
||||||
return repairOption;
|
return repairOption;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,9 @@ public class Repair extends AbstractCommand
|
||||||
@Option(paramLabel = "accord-only", names = { "-accord-only", "--accord-only" }, description = "If the --accord-only flag is included, no table data is repaired, only accord operations..")
|
@Option(paramLabel = "accord-only", names = { "-accord-only", "--accord-only" }, description = "If the --accord-only flag is included, no table data is repaired, only accord operations..")
|
||||||
private boolean accordOnly = false;
|
private boolean accordOnly = false;
|
||||||
|
|
||||||
|
@Option(paramLabel = "permit-no-quorum", names = { "-permit-no-quorum", "--permit-no-quorum" }, description = "If the --permit-no-quorum flag is included, accord repair is permitted to proceed without a quorum, including only the specified nodes")
|
||||||
|
private boolean permitNoQuorum = false;
|
||||||
|
|
||||||
@Option(paramLabel = "skip-accord", names = { "-skip-accord", "--skip-accord" }, description = "If the --skip-accord flag is included, the Accord repair step is skipped. Accord repair is also skipped for preview repairs.")
|
@Option(paramLabel = "skip-accord", names = { "-skip-accord", "--skip-accord" }, description = "If the --skip-accord flag is included, the Accord repair step is skipped. Accord repair is also skipped for preview repairs.")
|
||||||
private boolean skipAccord = false;
|
private boolean skipAccord = false;
|
||||||
|
|
||||||
|
|
@ -202,6 +205,7 @@ public class Repair extends AbstractCommand
|
||||||
checkArgument(!(skipAccord && accordOnly), "Can't specify both skip-accord and accord-only");
|
checkArgument(!(skipAccord && accordOnly), "Can't specify both skip-accord and accord-only");
|
||||||
boolean repairAccord = !skipAccord && !paxosOnly && getPreviewKind() == PreviewKind.NONE;
|
boolean repairAccord = !skipAccord && !paxosOnly && getPreviewKind() == PreviewKind.NONE;
|
||||||
options.put(RepairOption.REPAIR_ACCORD_KEY, Boolean.toString(repairAccord));
|
options.put(RepairOption.REPAIR_ACCORD_KEY, Boolean.toString(repairAccord));
|
||||||
|
options.put(RepairOption.PERMIT_NO_QUORUM_KEY, Boolean.toString(permitNoQuorum));
|
||||||
boolean repairData = false;
|
boolean repairData = false;
|
||||||
if (getPreviewKind() == PreviewKind.NONE)
|
if (getPreviewKind() == PreviewKind.NONE)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import org.apache.cassandra.concurrent.Shutdownable;
|
import org.apache.cassandra.concurrent.Shutdownable;
|
||||||
|
import org.apache.cassandra.utils.concurrent.UncheckedInterruptedException;
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||||
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
|
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
|
||||||
|
|
@ -103,13 +104,49 @@ public class ExecutorUtils
|
||||||
else
|
else
|
||||||
throw new IllegalArgumentException(executor.toString());
|
throw new IllegalArgumentException(executor.toString());
|
||||||
}
|
}
|
||||||
catch (Throwable t)
|
catch (InterruptedException t)
|
||||||
|
{
|
||||||
|
throw new UncheckedInterruptedException(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return shutdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean shutdownThenWait(Iterable<?> executors, long timeout, TimeUnit unit)
|
||||||
|
{
|
||||||
|
long deadline = nanoTime() + unit.toNanos(timeout);
|
||||||
|
|
||||||
|
for (Object executor : executors)
|
||||||
|
{
|
||||||
|
if (executor instanceof ExecutorService) ((ExecutorService) executor).shutdown();
|
||||||
|
else if (executor instanceof Shutdownable) ((Shutdownable) executor).shutdown();
|
||||||
|
else throw new IllegalArgumentException(executor.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Object executor : executors)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (executor instanceof ExecutorService)
|
||||||
|
{
|
||||||
|
if (!((ExecutorService) executor).awaitTermination(Math.max(0, deadline - nanoTime()), NANOSECONDS))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (executor instanceof Shutdownable)
|
||||||
|
{
|
||||||
|
if (!((Shutdownable) executor).awaitTermination(Math.max(0, deadline - nanoTime()), NANOSECONDS))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else throw new IllegalArgumentException(executor.toString());
|
||||||
|
}
|
||||||
|
catch (InterruptedException t)
|
||||||
{
|
{
|
||||||
throw new IllegalStateException("Caught interrupt while shutting down " + executor, t);
|
throw new IllegalStateException("Caught interrupt while shutting down " + executor, t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return shutdown;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void shutdown(ExecutorService ... executors)
|
public static void shutdown(ExecutorService ... executors)
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.BiPredicate;
|
import java.util.function.BiPredicate;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
@ -197,7 +198,12 @@ public abstract class AbstractCluster<I extends IInstance> implements ICluster<I
|
||||||
CassandraRelevantProperties.TEST_FLUSH_LOCAL_SCHEMA_CHANGES.reset();
|
CassandraRelevantProperties.TEST_FLUSH_LOCAL_SCHEMA_CHANGES.reset();
|
||||||
CassandraRelevantProperties.NON_GRACEFUL_SHUTDOWN.reset();
|
CassandraRelevantProperties.NON_GRACEFUL_SHUTDOWN.reset();
|
||||||
CassandraRelevantProperties.IO_NETTY_TRANSPORT_NONATIVE.setBoolean(false);
|
CassandraRelevantProperties.IO_NETTY_TRANSPORT_NONATIVE.setBoolean(false);
|
||||||
withInstanceInitializer((classLoader, threadGroup, i, i1) -> {
|
withInstanceInitializer(defaultInitializer());
|
||||||
|
}
|
||||||
|
|
||||||
|
private IInstanceInitializer defaultInitializer()
|
||||||
|
{
|
||||||
|
return (classLoader, threadGroup, i, i1) -> {
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Class<?> ef = classLoader.loadClass(ExecutorFactory.class.getName());
|
Class<?> ef = classLoader.loadClass(ExecutorFactory.class.getName());
|
||||||
|
|
@ -215,10 +221,21 @@ public abstract class AbstractCluster<I extends IInstance> implements ICluster<I
|
||||||
else
|
else
|
||||||
logger.info("Unable to set ExecutorFactory for instance {}", i, e);
|
logger.info("Unable to set ExecutorFactory for instance {}", i, e);
|
||||||
}
|
}
|
||||||
catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e)
|
catch (NoSuchMethodException | InvocationTargetException | InstantiationException |
|
||||||
|
IllegalAccessException e)
|
||||||
{
|
{
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public B withInstanceInitializer(BiConsumer<ClassLoader, Integer> instanceInitializer)
|
||||||
|
{
|
||||||
|
IInstanceInitializer wrap = defaultInitializer();
|
||||||
|
return withInstanceInitializer((classLoader, threadGroup, num, v) -> {
|
||||||
|
wrap.initialise(classLoader, threadGroup, num, v);
|
||||||
|
instanceInitializer.accept(classLoader, num);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -573,6 +590,10 @@ public abstract class AbstractCluster<I extends IInstance> implements ICluster<I
|
||||||
|
|
||||||
protected AbstractCluster(AbstractBuilder<I, ? extends ICluster<I>, ?> builder)
|
protected AbstractCluster(AbstractBuilder<I, ? extends ICluster<I>, ?> builder)
|
||||||
{
|
{
|
||||||
|
// start the JNA cleaner on the system class loader to avoid pinning an instance
|
||||||
|
// (we do it here because startup() isn't always called)
|
||||||
|
com.sun.jna.internal.Cleaner.getCleaner();
|
||||||
|
|
||||||
this.root = builder.getRootPath();
|
this.root = builder.getRootPath();
|
||||||
this.sharedClassLoader = builder.getSharedClassLoader();
|
this.sharedClassLoader = builder.getSharedClassLoader();
|
||||||
this.sharedClassPredicate = builder.getSharedClasses();
|
this.sharedClassPredicate = builder.getSharedClasses();
|
||||||
|
|
@ -1068,8 +1089,6 @@ public abstract class AbstractCluster<I extends IInstance> implements ICluster<I
|
||||||
|
|
||||||
public void startup()
|
public void startup()
|
||||||
{
|
{
|
||||||
// start the JNA cleaner on the system class loader to avoid pinning an instance
|
|
||||||
com.sun.jna.internal.Cleaner.getCleaner();
|
|
||||||
try (AllMembersAliveMonitor monitor = new AllMembersAliveMonitor())
|
try (AllMembersAliveMonitor monitor = new AllMembersAliveMonitor())
|
||||||
{
|
{
|
||||||
monitor.startPolling();
|
monitor.startPolling();
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import accord.utils.Invariants;
|
||||||
import io.netty.util.concurrent.GlobalEventExecutor;
|
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||||
import org.apache.cassandra.Util;
|
import org.apache.cassandra.Util;
|
||||||
import org.apache.cassandra.audit.AuditLogManager;
|
import org.apache.cassandra.audit.AuditLogManager;
|
||||||
|
|
@ -64,6 +65,7 @@ import org.apache.cassandra.concurrent.NamedThreadFactory;
|
||||||
import org.apache.cassandra.concurrent.ScheduledExecutors;
|
import org.apache.cassandra.concurrent.ScheduledExecutors;
|
||||||
import org.apache.cassandra.concurrent.SharedExecutorPool;
|
import org.apache.cassandra.concurrent.SharedExecutorPool;
|
||||||
import org.apache.cassandra.concurrent.Stage;
|
import org.apache.cassandra.concurrent.Stage;
|
||||||
|
import org.apache.cassandra.config.CassandraRelevantProperties;
|
||||||
import org.apache.cassandra.config.Config;
|
import org.apache.cassandra.config.Config;
|
||||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
import org.apache.cassandra.config.DurationSpec;
|
import org.apache.cassandra.config.DurationSpec;
|
||||||
|
|
@ -822,6 +824,9 @@ public class Instance extends IsolatedExecutor implements IInvokableInstance
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CommitLog.instance.recoverSegmentsOnDisk();
|
CommitLog.instance.recoverSegmentsOnDisk();
|
||||||
|
NodeId self = ClusterMetadata.current().myNodeId();
|
||||||
|
if (self != null)
|
||||||
|
AccordService.localStartup(self);
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
|
|
@ -868,7 +873,10 @@ public class Instance extends IsolatedExecutor implements IInvokableInstance
|
||||||
ClusterMetadataService.instance().processor().fetchLogAndWait();
|
ClusterMetadataService.instance().processor().fetchLogAndWait();
|
||||||
NodeId self = Register.maybeRegister();
|
NodeId self = Register.maybeRegister();
|
||||||
RegistrationStatus.instance.onRegistration();
|
RegistrationStatus.instance.onRegistration();
|
||||||
AccordService.startup(self);
|
if (!AccordService.isSetupOrStarting())
|
||||||
|
AccordService.localStartup(self);
|
||||||
|
AccordService.distributedStartup();
|
||||||
|
|
||||||
boolean joinRing = config.get(Constants.KEY_DTEST_JOIN_RING) == null || (boolean) config.get(Constants.KEY_DTEST_JOIN_RING);
|
boolean joinRing = config.get(Constants.KEY_DTEST_JOIN_RING) == null || (boolean) config.get(Constants.KEY_DTEST_JOIN_RING);
|
||||||
if (ClusterMetadata.current().directory.peerState(self) != NodeState.JOINED && joinRing)
|
if (ClusterMetadata.current().directory.peerState(self) != NodeState.JOINED && joinRing)
|
||||||
{
|
{
|
||||||
|
|
@ -1028,8 +1036,8 @@ public class Instance extends IsolatedExecutor implements IInvokableInstance
|
||||||
);
|
);
|
||||||
|
|
||||||
error = parallelRun(error, executor, () -> {
|
error = parallelRun(error, executor, () -> {
|
||||||
if (!AccordService.isSetup()) return;
|
if (AccordService.isSetupOrStarting())
|
||||||
AccordService.instance().shutdownAndWait(1l, MINUTES);
|
AccordService.unsafeInstance().shutdownAndWait(1L, MINUTES);
|
||||||
});
|
});
|
||||||
|
|
||||||
// CommitLog must shut down after Stage, or threads from the latter may attempt to use the former.
|
// CommitLog must shut down after Stage, or threads from the latter may attempt to use the former.
|
||||||
|
|
@ -1064,6 +1072,10 @@ public class Instance extends IsolatedExecutor implements IInvokableInstance
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
future.get();
|
future.get();
|
||||||
|
ThreadGroup group = Thread.currentThread().getThreadGroup();
|
||||||
|
int active = group.activeCount();
|
||||||
|
Invariants.expect(group.getParent().activeCount() <= active
|
||||||
|
|| CassandraRelevantProperties.DTEST_IGNORE_SHUTDOWN_THREADCOUNT.getBoolean());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import net.bytebuddy.ByteBuddy;
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
|
@ -53,6 +54,12 @@ import static org.junit.Assert.fail;
|
||||||
|
|
||||||
public class DecommissionTest extends TestBaseImpl
|
public class DecommissionTest extends TestBaseImpl
|
||||||
{
|
{
|
||||||
|
@BeforeClass
|
||||||
|
public static void before()
|
||||||
|
{
|
||||||
|
CassandraRelevantProperties.DTEST_IGNORE_SHUTDOWN_THREADCOUNT.setBoolean(true);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDecommission() throws Throwable
|
public void testDecommission() throws Throwable
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import accord.primitives.Unseekables;
|
import accord.primitives.Unseekables;
|
||||||
import accord.topology.Topologies;
|
import accord.topology.Topologies;
|
||||||
|
import accord.topology.TopologyException;
|
||||||
import org.apache.cassandra.config.Config.PaxosVariant;
|
import org.apache.cassandra.config.Config.PaxosVariant;
|
||||||
import org.apache.cassandra.cql3.CQLTester;
|
import org.apache.cassandra.cql3.CQLTester;
|
||||||
import org.apache.cassandra.cql3.ast.Symbol;
|
import org.apache.cassandra.cql3.ast.Symbol;
|
||||||
|
|
@ -709,7 +710,9 @@ public abstract class AccordCQLTestBase extends AccordTestBase
|
||||||
|
|
||||||
Unseekables<?> routables = AccordTestUtils.createTxn(sb.toString()).keys().toParticipants();
|
Unseekables<?> routables = AccordTestUtils.createTxn(sb.toString()).keys().toParticipants();
|
||||||
long epoch = AccordService.instance().topology().epoch();
|
long epoch = AccordService.instance().topology().epoch();
|
||||||
Topologies topology = AccordService.instance().topology().active().withUnsyncedEpochs(routables, epoch, epoch);
|
Topologies topology;
|
||||||
|
try { topology = AccordService.instance().topology().active().withUnsyncedEpochs(routables, epoch, epoch); }
|
||||||
|
catch (TopologyException e) { throw new RuntimeException(e); }
|
||||||
// we don't detect out-of-bounds read/write yet, so use this to validate we reach different shards
|
// we don't detect out-of-bounds read/write yet, so use this to validate we reach different shards
|
||||||
Assertions.assertThat(topology.totalShards()).isEqualTo(2);
|
Assertions.assertThat(topology.totalShards()).isEqualTo(2);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import accord.primitives.SaveStatus;
|
||||||
import accord.primitives.Status;
|
import accord.primitives.Status;
|
||||||
import accord.primitives.Txn;
|
import accord.primitives.Txn;
|
||||||
import accord.primitives.TxnId;
|
import accord.primitives.TxnId;
|
||||||
|
import accord.topology.TopologyException;
|
||||||
import accord.utils.ImmutableBitSet;
|
import accord.utils.ImmutableBitSet;
|
||||||
import accord.utils.LargeBitSet;
|
import accord.utils.LargeBitSet;
|
||||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
|
|
@ -127,7 +128,9 @@ public class AccordCommandStoreTryExecuteListeningTest extends TestBaseImpl
|
||||||
PartitionKey key = pk(1, "ks", "tbl");
|
PartitionKey key = pk(1, "ks", "tbl");
|
||||||
Txn txn = node.agent().emptySystemTxn(Txn.Kind.ExclusiveSyncPoint, Routable.Domain.Range);
|
Txn txn = node.agent().emptySystemTxn(Txn.Kind.ExclusiveSyncPoint, Routable.Domain.Range);
|
||||||
TxnId txnId = node.nextTxnId(txn);
|
TxnId txnId = node.nextTxnId(txn);
|
||||||
FullRoute<?> route = node.computeRoute(txnId, Ranges.of(key.asRange()));
|
FullRoute<?> route;
|
||||||
|
try { route = node.computeRoute(txnId, Ranges.of(key.asRange())); }
|
||||||
|
catch (TopologyException e) { throw new RuntimeException(e); }
|
||||||
AccordCommandStore commandStore = (AccordCommandStore) node.commandStores().unsafeForKey(key.toUnseekable());
|
AccordCommandStore commandStore = (AccordCommandStore) node.commandStores().unsafeForKey(key.toUnseekable());
|
||||||
int[] rangesToTxnIds = new int[dependencies.length + 1];
|
int[] rangesToTxnIds = new int[dependencies.length + 1];
|
||||||
rangesToTxnIds[0] = rangesToTxnIds.length;
|
rangesToTxnIds[0] = rangesToTxnIds.length;
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,6 @@ public class AccordMetricsTest extends AccordTestBase
|
||||||
|
|
||||||
assertClientMetrics(0, "AccordRead", 1, 0);
|
assertClientMetrics(0, "AccordRead", 1, 0);
|
||||||
assertClientMetrics(1, "AccordRead", 0, 0);
|
assertClientMetrics(1, "AccordRead", 0, 0);
|
||||||
// TODO (required): reenable or remove metric
|
|
||||||
assertCoordinatorMetrics(0, "ro", 0, 0, 1, 0, 0);
|
assertCoordinatorMetrics(0, "ro", 0, 0, 1, 0, 0);
|
||||||
assertCoordinatorMetrics(1, "ro" , 0, 0, 0, 0, 1);
|
assertCoordinatorMetrics(1, "ro" , 0, 0, 0, 0, 1);
|
||||||
assertReplicaMetrics(0, "ro", 0, 0, 0);
|
assertReplicaMetrics(0, "ro", 0, 0, 0);
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ public class AccordSimpleFastPathTest extends TestBaseImpl
|
||||||
|
|
||||||
long epoch = cm.epoch.getEpoch();
|
long epoch = cm.epoch.getEpoch();
|
||||||
TopologyManager tm = AccordService.instance().topology();
|
TopologyManager tm = AccordService.instance().topology();
|
||||||
Topology topology = tm.active().globalForEpoch(epoch);
|
Topology topology = tm.active().getKnown(epoch).global();
|
||||||
Assert.assertFalse(topology.shards().isEmpty());
|
Assert.assertFalse(topology.shards().isEmpty());
|
||||||
topology.shards().forEach(shard -> Assert.assertEquals(idSet(1, 2, 3), shard.nodes.without(shard.notInFastPath)));
|
topology.shards().forEach(shard -> Assert.assertEquals(idSet(1, 2, 3), shard.nodes.without(shard.notInFastPath)));
|
||||||
return cm.epoch.getEpoch();
|
return cm.epoch.getEpoch();
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ import accord.primitives.SyncPoint;
|
||||||
import accord.primitives.Txn;
|
import accord.primitives.Txn;
|
||||||
import accord.primitives.TxnId;
|
import accord.primitives.TxnId;
|
||||||
import accord.primitives.Writes;
|
import accord.primitives.Writes;
|
||||||
|
import accord.topology.TopologyException;
|
||||||
import accord.utils.ImmutableBitSet;
|
import accord.utils.ImmutableBitSet;
|
||||||
import accord.utils.LargeBitSet;
|
import accord.utils.LargeBitSet;
|
||||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
|
|
@ -114,7 +115,9 @@ public class AccordJournalReplayTest extends TestBaseImpl
|
||||||
Txn syncPointTxn = node.agent().emptySystemTxn(Txn.Kind.ExclusiveSyncPoint, Routable.Domain.Range);
|
Txn syncPointTxn = node.agent().emptySystemTxn(Txn.Kind.ExclusiveSyncPoint, Routable.Domain.Range);
|
||||||
TxnId syncPointId = node.nextTxnId(syncPointTxn);
|
TxnId syncPointId = node.nextTxnId(syncPointTxn);
|
||||||
TxnId txnId = node.nextTxnId(txn);
|
TxnId txnId = node.nextTxnId(txn);
|
||||||
FullRoute<?> route = node.computeRoute(txnId, txn.keys());
|
FullRoute<?> route;
|
||||||
|
try { route = node.computeRoute(txnId, txn.keys()); }
|
||||||
|
catch (TopologyException e) { throw new RuntimeException(e); }
|
||||||
AccordCommandStore commandStore = (AccordCommandStore) node.commandStores().unsafeForKey(key.toUnseekable());
|
AccordCommandStore commandStore = (AccordCommandStore) node.commandStores().unsafeForKey(key.toUnseekable());
|
||||||
Deps deps = new Deps(KeyDeps.NONE, RangeDeps.SerializerSupport.create(new accord.primitives.Range[] { key.asRange() }, new TxnId[] { syncPointId }, new int[] { 2, 0 }, new int[] { 2, 0 }));
|
Deps deps = new Deps(KeyDeps.NONE, RangeDeps.SerializerSupport.create(new accord.primitives.Range[] { key.asRange() }, new TxnId[] { syncPointId }, new int[] { 2, 0 }, new int[] { 2, 0 }));
|
||||||
Command.WaitingOn waitingOn; {
|
Command.WaitingOn waitingOn; {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@ SYNOPSIS
|
||||||
[(-iuk | --ignore-unreplicated-keyspaces)]
|
[(-iuk | --ignore-unreplicated-keyspaces)]
|
||||||
[(-j <job_threads> | --job-threads <job_threads>)]
|
[(-j <job_threads> | --job-threads <job_threads>)]
|
||||||
[(-local | --in-local-dc)] [(-os | --optimise-streams)]
|
[(-local | --in-local-dc)] [(-os | --optimise-streams)]
|
||||||
[(-paxos-only | --paxos-only)] [(-pl | --pull)]
|
[(-paxos-only | --paxos-only)]
|
||||||
|
[(-permit-no-quorum | --permit-no-quorum)] [(-pl | --pull)]
|
||||||
[(-pr | --partitioner-range)] [(-prv | --preview)]
|
[(-pr | --partitioner-range)] [(-prv | --preview)]
|
||||||
[(-seq | --sequential)] [(-skip-accord | --skip-accord)]
|
[(-seq | --sequential)] [(-skip-accord | --skip-accord)]
|
||||||
[(-skip-paxos | --skip-paxos)]
|
[(-skip-paxos | --skip-paxos)]
|
||||||
|
|
@ -76,6 +77,11 @@ OPTIONS
|
||||||
If the --paxos-only flag is included, no table data is repaired,
|
If the --paxos-only flag is included, no table data is repaired,
|
||||||
only paxos operations..
|
only paxos operations..
|
||||||
|
|
||||||
|
-permit-no-quorum, --permit-no-quorum
|
||||||
|
If the --permit-no-quorum flag is included, accord repair is
|
||||||
|
permitted to proceed without a quorum, including only the specified
|
||||||
|
nodes
|
||||||
|
|
||||||
-pl, --pull
|
-pl, --pull
|
||||||
Use --pull to perform a one way repair where data is only streamed
|
Use --pull to perform a one way repair where data is only streamed
|
||||||
from a remote node to this node.
|
from a remote node to this node.
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ public class OnInstanceRepair extends ClusterAction
|
||||||
{
|
{
|
||||||
Collection<Range<Token>> ranges = rangesSupplier.call();
|
Collection<Range<Token>> ranges = rangesSupplier.call();
|
||||||
// no need to wait for completion, as we track all task submissions and message exchanges, and ensure they finish before continuing to next action
|
// no need to wait for completion, as we track all task submissions and message exchanges, and ensure they finish before continuing to next action
|
||||||
StorageService.instance.repair(keyspaceName, new RepairOption(RepairParallelism.SEQUENTIAL, isPrimaryRangeOnly, repairType.incremental, false, 1, ranges, false, force, PreviewKind.NONE, false, true, repairType.repairData, repairType.repairPaxos, false, repairType.repairAccord), singletonList((tag, event) -> {
|
StorageService.instance.repair(keyspaceName, new RepairOption(RepairParallelism.SEQUENTIAL, isPrimaryRangeOnly, repairType.incremental, false, 1, ranges, false, force, PreviewKind.NONE, false, true, repairType.repairData, repairType.repairPaxos, false, repairType.repairAccord, false), singletonList((tag, event) -> {
|
||||||
if (event.getType() == ProgressEventType.COMPLETE)
|
if (event.getType() == ProgressEventType.COMPLETE)
|
||||||
listener.run();
|
listener.run();
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -279,10 +279,14 @@ public class CompactionAccordIteratorsTest
|
||||||
|
|
||||||
private static void flush(AccordCommandStore commandStore)
|
private static void flush(AccordCommandStore commandStore)
|
||||||
{
|
{
|
||||||
|
long cacheSize;
|
||||||
try (AccordExecutor.ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();)
|
try (AccordExecutor.ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();)
|
||||||
{
|
{
|
||||||
long cacheSize = cache.global.capacity();
|
cacheSize = cache.global.capacity();
|
||||||
cache.global.setCapacity(0);
|
cache.global.setCapacity(0);
|
||||||
|
}
|
||||||
|
try (AccordExecutor.ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();)
|
||||||
|
{
|
||||||
cache.global.setCapacity(cacheSize);
|
cache.global.setCapacity(cacheSize);
|
||||||
}
|
}
|
||||||
commandsForKey.forceBlockingFlush(FlushReason.UNIT_TESTS);
|
commandsForKey.forceBlockingFlush(FlushReason.UNIT_TESTS);
|
||||||
|
|
@ -313,7 +317,7 @@ public class CompactionAccordIteratorsTest
|
||||||
Txn txn = txnId.kind().isWrite() ? writeTxn : readTxn;
|
Txn txn = txnId.kind().isWrite() ? writeTxn : readTxn;
|
||||||
PartialDeps partialDeps = Deps.NONE.intersecting(AccordTestUtils.fullRange(txn));
|
PartialDeps partialDeps = Deps.NONE.intersecting(AccordTestUtils.fullRange(txn));
|
||||||
PartialTxn partialTxn = txn.slice(commandStore.unsafeGetRangesForEpoch().currentRanges(), true);
|
PartialTxn partialTxn = txn.slice(commandStore.unsafeGetRangesForEpoch().currentRanges(), true);
|
||||||
Route<?> partialRoute = route.slice(commandStore.unsafeGetRangesForEpoch().currentRanges());
|
Route<?> partialRoute = route.overlapping(commandStore.unsafeGetRangesForEpoch().currentRanges());
|
||||||
getBlocking(commandStore.execute(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> {
|
getBlocking(commandStore.execute(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> {
|
||||||
CheckedCommands.preaccept(safe, txnId, partialTxn, route, (a, b) -> {});
|
CheckedCommands.preaccept(safe, txnId, partialTxn, route, (a, b) -> {});
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import org.junit.Test;
|
||||||
import accord.api.ProtocolModifiers;
|
import accord.api.ProtocolModifiers;
|
||||||
import accord.messages.NoWaitRequest;
|
import accord.messages.NoWaitRequest;
|
||||||
import accord.api.RoutingKey;
|
import accord.api.RoutingKey;
|
||||||
|
import accord.primitives.Keys;
|
||||||
import accord.primitives.Ranges;
|
import accord.primitives.Ranges;
|
||||||
import accord.primitives.Routable;
|
import accord.primitives.Routable;
|
||||||
import accord.primitives.SaveStatus;
|
import accord.primitives.SaveStatus;
|
||||||
|
|
@ -209,7 +210,8 @@ public class AccordDebugKeyspaceTest extends CQLTester
|
||||||
CQLTester.setUpClass();
|
CQLTester.setUpClass();
|
||||||
CassandraDaemon.getInstanceForTesting().setupVirtualKeyspaces();
|
CassandraDaemon.getInstanceForTesting().setupVirtualKeyspaces();
|
||||||
|
|
||||||
AccordService.startup(ClusterMetadata.current().myNodeId());
|
AccordService.localStartup(ClusterMetadata.current().myNodeId());
|
||||||
|
AccordService.distributedStartup();
|
||||||
requireNetwork();
|
requireNetwork();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -235,8 +237,8 @@ public class AccordDebugKeyspaceTest extends CQLTester
|
||||||
MessagingService.instance().outboundSink.add(filter);
|
MessagingService.instance().outboundSink.add(filter);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
TxnId id = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.Write, Routable.Domain.Key);
|
|
||||||
Txn txn = createTxn(wrapInTxn(String.format("INSERT INTO %s.%s(k, c, v) VALUES (?, ?, ?)", KEYSPACE, tableName)), 0, 0, 0);
|
Txn txn = createTxn(wrapInTxn(String.format("INSERT INTO %s.%s(k, c, v) VALUES (?, ?, ?)", KEYSPACE, tableName)), 0, 0, 0);
|
||||||
|
TxnId id = accord.node().nextTxnIdWithDefaultFlags(txn.keys(), Txn.Kind.Write, Routable.Domain.Key);
|
||||||
filter.appliesTo(id);
|
filter.appliesTo(id);
|
||||||
|
|
||||||
execute(SET_TRACE, 1, "{WaitProgress}", id.toString());
|
execute(SET_TRACE, 1, "{WaitProgress}", id.toString());
|
||||||
|
|
@ -276,8 +278,8 @@ public class AccordDebugKeyspaceTest extends CQLTester
|
||||||
MessagingService.instance().outboundSink.add(filter);
|
MessagingService.instance().outboundSink.add(filter);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
TxnId id = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.Write, Routable.Domain.Key);
|
|
||||||
Txn txn = createTxn(wrapInTxn(String.format("INSERT INTO %s.%s(k, c, v) VALUES (?, ?, ?)", KEYSPACE, tableName)), 1, 1, 1);
|
Txn txn = createTxn(wrapInTxn(String.format("INSERT INTO %s.%s(k, c, v) VALUES (?, ?, ?)", KEYSPACE, tableName)), 1, 1, 1);
|
||||||
|
TxnId id = accord.node().nextTxnIdWithDefaultFlags(txn.keys(), Txn.Kind.Write, Routable.Domain.Key);
|
||||||
filter.appliesTo(id);
|
filter.appliesTo(id);
|
||||||
|
|
||||||
execute(SET_TRACE_REMOTE, 1, "{WaitProgress}", nodeId, id.toString());
|
execute(SET_TRACE_REMOTE, 1, "{WaitProgress}", nodeId, id.toString());
|
||||||
|
|
@ -339,8 +341,8 @@ public class AccordDebugKeyspaceTest extends CQLTester
|
||||||
execute(SET_PATTERN_TRACE, "leaky", 0, count, 1.0f, matchKey.toString(), "*", "{}", "*", "leaky", 1, 1, "*", 1);
|
execute(SET_PATTERN_TRACE, "leaky", 0, count, 1.0f, matchKey.toString(), "*", "{}", "*", "leaky", 1, 1, "*", 1);
|
||||||
for (int i = 0 ; i < count + 1 ; ++i)
|
for (int i = 0 ; i < count + 1 ; ++i)
|
||||||
{
|
{
|
||||||
TxnId id = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.Write, Routable.Domain.Key);
|
|
||||||
Txn txn = createTxn(wrapInTxn(String.format("INSERT INTO %s.%s(k, c, v) VALUES (?, ?, ?)", KEYSPACE, tableName)), 0, i, 0);
|
Txn txn = createTxn(wrapInTxn(String.format("INSERT INTO %s.%s(k, c, v) VALUES (?, ?, ?)", KEYSPACE, tableName)), 0, i, 0);
|
||||||
|
TxnId id = accord.node().nextTxnIdWithDefaultFlags(txn.keys(), Txn.Kind.Write, Routable.Domain.Key);
|
||||||
getBlocking(accord.node().coordinate(id, txn));
|
getBlocking(accord.node().coordinate(id, txn));
|
||||||
if (i < count) assertRows(execute(QUERY_TRACE, id.toString()), row(id.toString(), 1, "*"));
|
if (i < count) assertRows(execute(QUERY_TRACE, id.toString()), row(id.toString(), 1, "*"));
|
||||||
else assertRows(execute(QUERY_TRACE, id.toString()));
|
else assertRows(execute(QUERY_TRACE, id.toString()));
|
||||||
|
|
@ -356,8 +358,8 @@ public class AccordDebugKeyspaceTest extends CQLTester
|
||||||
execute(SET_PATTERN_TRACE, "leaky", 0, count, 1.0f, matchKey.asRange().toString(), "{KE}", "{}", "{PreAccept}", "leaky", 1, 1, "*", 1);
|
execute(SET_PATTERN_TRACE, "leaky", 0, count, 1.0f, matchKey.asRange().toString(), "{KE}", "{}", "{PreAccept}", "leaky", 1, 1, "*", 1);
|
||||||
for (int i = 0 ; i < count ; ++i)
|
for (int i = 0 ; i < count ; ++i)
|
||||||
{
|
{
|
||||||
TxnId id = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.Write, Routable.Domain.Key);
|
|
||||||
Txn txn = createTxn(wrapInTxn(String.format("INSERT INTO %s.%s(k, c, v) VALUES (?, ?, ?)", KEYSPACE, tableName)), 0, i, 0);
|
Txn txn = createTxn(wrapInTxn(String.format("INSERT INTO %s.%s(k, c, v) VALUES (?, ?, ?)", KEYSPACE, tableName)), 0, i, 0);
|
||||||
|
TxnId id = accord.node().nextTxnIdWithDefaultFlags(txn.keys(), Txn.Kind.Write, Routable.Domain.Key);
|
||||||
getBlocking(accord.node().coordinate(id, txn));
|
getBlocking(accord.node().coordinate(id, txn));
|
||||||
assertRows(execute(QUERY_TRACE, id.toString()));
|
assertRows(execute(QUERY_TRACE, id.toString()));
|
||||||
}
|
}
|
||||||
|
|
@ -365,8 +367,8 @@ public class AccordDebugKeyspaceTest extends CQLTester
|
||||||
List<TxnId> txnIds = new ArrayList<>();
|
List<TxnId> txnIds = new ArrayList<>();
|
||||||
for (int i = 0 ; i < count + 1 ; ++i)
|
for (int i = 0 ; i < count + 1 ; ++i)
|
||||||
{
|
{
|
||||||
TxnId id = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.EphemeralRead, Routable.Domain.Key);
|
|
||||||
Txn txn = createTxn(wrapInTxn(String.format("SELECT * FROM %s.%s WHERE k = ? AND c = ?", KEYSPACE, tableName)), 0, i);
|
Txn txn = createTxn(wrapInTxn(String.format("SELECT * FROM %s.%s WHERE k = ? AND c = ?", KEYSPACE, tableName)), 0, i);
|
||||||
|
TxnId id = accord.node().nextTxnIdWithDefaultFlags(txn.keys(), Txn.Kind.EphemeralRead, Routable.Domain.Key);
|
||||||
getBlocking(accord.node().coordinate(id, txn));
|
getBlocking(accord.node().coordinate(id, txn));
|
||||||
if (i < count) assertRows(execute(QUERY_TRACE, id.toString()), row(id.toString(), 1, "*"));
|
if (i < count) assertRows(execute(QUERY_TRACE, id.toString()), row(id.toString(), 1, "*"));
|
||||||
else assertRows(execute(QUERY_TRACE, id.toString()));
|
else assertRows(execute(QUERY_TRACE, id.toString()));
|
||||||
|
|
@ -379,8 +381,8 @@ public class AccordDebugKeyspaceTest extends CQLTester
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
TxnId id = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.Write, Routable.Domain.Key);
|
|
||||||
Txn txn = createTxn(wrapInTxn(String.format("INSERT INTO %s.%s(k, c, v) VALUES (?, ?, ?)", KEYSPACE, tableName)), 1, 1, 1);
|
Txn txn = createTxn(wrapInTxn(String.format("INSERT INTO %s.%s(k, c, v) VALUES (?, ?, ?)", KEYSPACE, tableName)), 1, 1, 1);
|
||||||
|
TxnId id = accord.node().nextTxnIdWithDefaultFlags(txn.keys(), Txn.Kind.Write, Routable.Domain.Key);
|
||||||
execute(SET_PATTERN_TRACE, "leaky", 0, count, 1.0f, "" + txn.keys().get(0).toUnseekable(), "{KW}", "*", "{}", "leaky", 1, 1, "{}", 1);
|
execute(SET_PATTERN_TRACE, "leaky", 0, count, 1.0f, "" + txn.keys().get(0).toUnseekable(), "{KW}", "*", "{}", "leaky", 1, 1, "{}", 1);
|
||||||
|
|
||||||
AccordMsgFilter filter = new AccordMsgFilter();
|
AccordMsgFilter filter = new AccordMsgFilter();
|
||||||
|
|
@ -461,7 +463,7 @@ public class AccordDebugKeyspaceTest extends CQLTester
|
||||||
{
|
{
|
||||||
AccordService accord = accord();
|
AccordService accord = accord();
|
||||||
int nodeId = accord.nodeId().id;
|
int nodeId = accord.nodeId().id;
|
||||||
TxnId id = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.Write, Routable.Domain.Key);
|
TxnId id = accord.node().nextTxnIdWithDefaultFlags(Keys.of(), Txn.Kind.Write, Routable.Domain.Key);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
execute(ERASE_JOURNAL_REMOTE, nodeId, 1, id.toString());
|
execute(ERASE_JOURNAL_REMOTE, nodeId, 1, id.toString());
|
||||||
|
|
@ -479,8 +481,8 @@ public class AccordDebugKeyspaceTest extends CQLTester
|
||||||
AccordService accord = accord();
|
AccordService accord = accord();
|
||||||
int nodeId = accord.nodeId().id;
|
int nodeId = accord.nodeId().id;
|
||||||
AccordMsgFilter filter = new AccordMsgFilter();
|
AccordMsgFilter filter = new AccordMsgFilter();
|
||||||
TxnId id = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.Write, Routable.Domain.Key);
|
|
||||||
Txn txn = createTxn(wrapInTxn(String.format("INSERT INTO %s.%s(k, c, v) VALUES (?, ?, ?)", KEYSPACE, tableName)), 0, 0, 0);
|
Txn txn = createTxn(wrapInTxn(String.format("INSERT INTO %s.%s(k, c, v) VALUES (?, ?, ?)", KEYSPACE, tableName)), 0, 0, 0);
|
||||||
|
TxnId id = accord.node().nextTxnIdWithDefaultFlags(txn.keys(), Txn.Kind.Write, Routable.Domain.Key);
|
||||||
filter.appliesTo(id);
|
filter.appliesTo(id);
|
||||||
filter.dropVerbs = Set.of();
|
filter.dropVerbs = Set.of();
|
||||||
MessagingService.instance().outboundSink.add(filter);
|
MessagingService.instance().outboundSink.add(filter);
|
||||||
|
|
@ -562,7 +564,6 @@ public class AccordDebugKeyspaceTest extends CQLTester
|
||||||
String tableName = createTable("CREATE TABLE %s (k int, c int, v int, PRIMARY KEY (k, c)) WITH transactional_mode = 'full'");
|
String tableName = createTable("CREATE TABLE %s (k int, c int, v int, PRIMARY KEY (k, c)) WITH transactional_mode = 'full'");
|
||||||
AccordService accord = accord();
|
AccordService accord = accord();
|
||||||
int nodeId = accord.nodeId().id;
|
int nodeId = accord.nodeId().id;
|
||||||
TxnId id = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.Write, Routable.Domain.Key);
|
|
||||||
String insertTxn = String.format("BEGIN TRANSACTION\n" +
|
String insertTxn = String.format("BEGIN TRANSACTION\n" +
|
||||||
" LET r = (SELECT * FROM %s.%s WHERE k = ? AND c = ?);\n" +
|
" LET r = (SELECT * FROM %s.%s WHERE k = ? AND c = ?);\n" +
|
||||||
" IF r IS NULL THEN\n " +
|
" IF r IS NULL THEN\n " +
|
||||||
|
|
@ -570,6 +571,7 @@ public class AccordDebugKeyspaceTest extends CQLTester
|
||||||
" END IF\n" +
|
" END IF\n" +
|
||||||
"COMMIT TRANSACTION", KEYSPACE, tableName, KEYSPACE, tableName);
|
"COMMIT TRANSACTION", KEYSPACE, tableName, KEYSPACE, tableName);
|
||||||
Txn txn = createTxn(insertTxn, 0, 0, 0, 0, 0);
|
Txn txn = createTxn(insertTxn, 0, 0, 0, 0, 0);
|
||||||
|
TxnId id = accord.node().nextTxnIdWithDefaultFlags(txn.keys(), Txn.Kind.Write, Routable.Domain.Key);
|
||||||
filter.appliesTo(id);
|
filter.appliesTo(id);
|
||||||
accord.node().coordinate(id, txn).beginAsResult();
|
accord.node().coordinate(id, txn).beginAsResult();
|
||||||
|
|
||||||
|
|
@ -603,16 +605,17 @@ public class AccordDebugKeyspaceTest extends CQLTester
|
||||||
String tableName = createTable("CREATE TABLE %s (k int, c int, v int, PRIMARY KEY (k, c)) WITH transactional_mode = 'full'");
|
String tableName = createTable("CREATE TABLE %s (k int, c int, v int, PRIMARY KEY (k, c)) WITH transactional_mode = 'full'");
|
||||||
AccordService accord = accord();
|
AccordService accord = accord();
|
||||||
int nodeId = accord.nodeId().id;
|
int nodeId = accord.nodeId().id;
|
||||||
TxnId first = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.Write, Routable.Domain.Key);
|
|
||||||
String insertTxn = String.format("BEGIN TRANSACTION\n" +
|
String insertTxn = String.format("BEGIN TRANSACTION\n" +
|
||||||
" LET r = (SELECT * FROM %s.%s WHERE k = ? AND c = ?);\n" +
|
" LET r = (SELECT * FROM %s.%s WHERE k = ? AND c = ?);\n" +
|
||||||
" IF r IS NULL THEN\n " +
|
" IF r IS NULL THEN\n " +
|
||||||
" INSERT INTO %s.%s (k, c, v) VALUES (?, ?, ?);\n" +
|
" INSERT INTO %s.%s (k, c, v) VALUES (?, ?, ?);\n" +
|
||||||
" END IF\n" +
|
" END IF\n" +
|
||||||
"COMMIT TRANSACTION", KEYSPACE, tableName, KEYSPACE, tableName);
|
"COMMIT TRANSACTION", KEYSPACE, tableName, KEYSPACE, tableName);
|
||||||
|
Txn txn = createTxn(insertTxn, 0, 0, 0, 0, 0);
|
||||||
|
TxnId first = accord.node().nextTxnIdWithDefaultFlags(txn.keys(), Txn.Kind.Write, Routable.Domain.Key);
|
||||||
|
|
||||||
filter.appliesTo(first);
|
filter.appliesTo(first);
|
||||||
accord.node().coordinate(first, createTxn(insertTxn, 0, 0, 0, 0, 0)).beginAsResult();
|
accord.node().coordinate(first, txn).beginAsResult();
|
||||||
|
|
||||||
filter.preAccept.awaitThrowUncheckedOnInterrupt();
|
filter.preAccept.awaitThrowUncheckedOnInterrupt();
|
||||||
assertRows(execute(QUERY_TXN_BLOCKED_BY, first.toString()),
|
assertRows(execute(QUERY_TXN_BLOCKED_BY, first.toString()),
|
||||||
|
|
@ -627,10 +630,10 @@ public class AccordDebugKeyspaceTest extends CQLTester
|
||||||
|
|
||||||
filter.reset();
|
filter.reset();
|
||||||
|
|
||||||
TxnId second = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.Write, Routable.Domain.Key);
|
TxnId second = accord.node().nextTxnIdWithDefaultFlags(txn.keys(), Txn.Kind.Write, Routable.Domain.Key);
|
||||||
filter.reset();
|
filter.reset();
|
||||||
filter.appliesTo(second);
|
filter.appliesTo(second);
|
||||||
accord.node().coordinate(second, createTxn(insertTxn, 0, 0, 0, 0, 0)).beginAsResult();
|
accord.node().coordinate(second, txn).beginAsResult();
|
||||||
|
|
||||||
filter.commit.awaitThrowUncheckedOnInterrupt();
|
filter.commit.awaitThrowUncheckedOnInterrupt();
|
||||||
|
|
||||||
|
|
@ -695,9 +698,10 @@ public class AccordDebugKeyspaceTest extends CQLTester
|
||||||
KEYSPACE,
|
KEYSPACE,
|
||||||
tableName);
|
tableName);
|
||||||
AccordService accord = accord();
|
AccordService accord = accord();
|
||||||
TxnId id = accord.node().nextTxnIdWithDefaultFlags(Txn.Kind.Write, Routable.Domain.Key);
|
Txn txn = createTxn(insertTxn, 0, 0, 0);
|
||||||
|
TxnId id = accord.node().nextTxnIdWithDefaultFlags(txn.keys(), Txn.Kind.Write, Routable.Domain.Key);
|
||||||
filter.appliesTo(id);
|
filter.appliesTo(id);
|
||||||
accord.node().coordinate(id, createTxn(insertTxn, 0, 0, 0)).beginAsResult();
|
accord.node().coordinate(id, txn).beginAsResult();
|
||||||
|
|
||||||
filter.preAccept.awaitThrowUncheckedOnInterrupt();
|
filter.preAccept.awaitThrowUncheckedOnInterrupt();
|
||||||
String QUERY_JOURNAL = String.format("SELECT txn_id, save_status, command_store_id FROM %s.%s WHERE txn_id=?", SchemaConstants.VIRTUAL_ACCORD_DEBUG, AccordDebugKeyspace.JOURNAL);
|
String QUERY_JOURNAL = String.format("SELECT txn_id, save_status, command_store_id FROM %s.%s WHERE txn_id=?", SchemaConstants.VIRTUAL_ACCORD_DEBUG, AccordDebugKeyspace.JOURNAL);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,351 @@
|
||||||
|
/*
|
||||||
|
* 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.db.virtual;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.IntUnaryOperator;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import accord.utils.Invariants;
|
||||||
|
import accord.utils.LargeBitSet;
|
||||||
|
import accord.utils.RandomSource;
|
||||||
|
import accord.utils.RandomTestRunner;
|
||||||
|
import accord.utils.UnhandledEnum;
|
||||||
|
import net.openhft.chronicle.core.util.IntBiPredicate;
|
||||||
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
|
import org.apache.cassandra.cql3.CQLTester;
|
||||||
|
import org.apache.cassandra.cql3.QueryProcessor;
|
||||||
|
import org.apache.cassandra.cql3.UntypedResultSet;
|
||||||
|
import org.apache.cassandra.cql3.statements.schema.CreateTableStatement;
|
||||||
|
import org.apache.cassandra.db.marshal.Int32Type;
|
||||||
|
import org.apache.cassandra.dht.LocalPartitioner;
|
||||||
|
import org.apache.cassandra.schema.TableMetadata;
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
|
|
||||||
|
import static org.apache.cassandra.db.virtual.AbstractLazyVirtualTable.OnTimeout.FAIL;
|
||||||
|
import static org.apache.cassandra.db.virtual.LazyVirtualTableTest.Cmp.CMPS;
|
||||||
|
import static org.apache.cassandra.db.virtual.VirtualTable.Sorted.ASC;
|
||||||
|
import static org.apache.cassandra.db.virtual.VirtualTable.Sorted.DESC;
|
||||||
|
import static org.apache.cassandra.db.virtual.VirtualTable.Sorted.UNSORTED;
|
||||||
|
|
||||||
|
public class LazyVirtualTableTest extends CQLTester
|
||||||
|
{
|
||||||
|
private static final String KEYSPACE = "system_accord_not_really";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test()
|
||||||
|
{
|
||||||
|
DatabaseDescriptor.setRpcTimeout(TimeUnit.MINUTES.toMillis(5L));
|
||||||
|
RandomTestRunner.test().withSeed(3606318398900145565L).check(this::testOne);
|
||||||
|
for (int i = 0 ; i < 10 ; ++i)
|
||||||
|
RandomTestRunner.test().check(this::testOne);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testOne(RandomSource rnd)
|
||||||
|
{
|
||||||
|
QueryProcessor.clearInternalStatementsCache();
|
||||||
|
QueryProcessor.clearPreparedStatementsCache();
|
||||||
|
int keyCount = rnd.nextInt(1, 5);
|
||||||
|
int valCount = rnd.nextInt(0, 3);
|
||||||
|
int rowCount = rnd.nextInt(0, 12) - 1;
|
||||||
|
rowCount = rowCount < 0 ? 0 : rnd.nextInt(1 << rowCount, 2 << rowCount);
|
||||||
|
int[] keyDomains = new int[keyCount];
|
||||||
|
Arrays.fill(keyDomains, 1);
|
||||||
|
int uniqueKeys = Math.max(1, rowCount * rnd.nextInt(2, 5));
|
||||||
|
for (int k = keyDomains.length - 1 ; k > 0 && uniqueKeys > 1 ; --k)
|
||||||
|
{
|
||||||
|
int maxScale = 31 - Integer.numberOfLeadingZeros(uniqueKeys);
|
||||||
|
if (1 << maxScale != uniqueKeys)
|
||||||
|
++maxScale;
|
||||||
|
int avgScale = maxScale / (1 + k);
|
||||||
|
int scale = rnd.nextBiasedInt(0, avgScale, maxScale);
|
||||||
|
keyDomains[k] = rnd.nextInt(1 << scale, Math.min(uniqueKeys, 2 << scale));
|
||||||
|
uniqueKeys = (uniqueKeys + keyDomains[k] - 1) / keyDomains[k];
|
||||||
|
}
|
||||||
|
keyDomains[0] = uniqueKeys;
|
||||||
|
int[] valDomains = new int[valCount];
|
||||||
|
Arrays.fill(valDomains, Integer.MAX_VALUE);
|
||||||
|
int queryCount = rnd.nextInt(1, Math.max(2, Math.min(1000, rowCount)));
|
||||||
|
testOne(rnd, keyDomains, valDomains, rowCount, queryCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testOne(RandomSource rnd, int[] keyDomains, int[] valDomains, int rowCount, int queryLoops)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
int uniqueKeys = 1;
|
||||||
|
for (int keyDomain : keyDomains)
|
||||||
|
uniqueKeys *= keyDomain;
|
||||||
|
Invariants.require(uniqueKeys >= 2 * rowCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
int keyCount = keyDomains.length;
|
||||||
|
int valCount = valDomains.length;
|
||||||
|
String[] keyNames = IntStream.rangeClosed(1, keyCount).mapToObj(i -> "k" + i).toArray(String[]::new);
|
||||||
|
String[] valNames = IntStream.rangeClosed(1, valCount).mapToObj(i -> "v" + i).toArray(String[]::new);
|
||||||
|
|
||||||
|
int[][] allKeys = new int[rowCount][], allVals = new int[rowCount][];
|
||||||
|
{
|
||||||
|
TreeMap<int[], int[]> unique = new TreeMap<>(Arrays::compare);
|
||||||
|
for (int r = 0 ; r < rowCount ; ++r)
|
||||||
|
{
|
||||||
|
int[] vals = new int[valCount];
|
||||||
|
for (int v = 0 ; v < valCount ; ++v)
|
||||||
|
vals[v] = rnd.nextInt(valDomains[v]);
|
||||||
|
|
||||||
|
int[] keys = new int[keyCount];
|
||||||
|
do
|
||||||
|
{
|
||||||
|
for (int k = 0 ; k < keyCount ; ++k)
|
||||||
|
keys[k] = rnd.nextInt(keyDomains[k]);
|
||||||
|
}
|
||||||
|
while (null != unique.putIfAbsent(keys, vals));
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
for (Map.Entry<int[], int[]> e : unique.entrySet())
|
||||||
|
{
|
||||||
|
allKeys[count] = e.getKey();
|
||||||
|
allVals[count] = e.getValue();
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestTable extends AbstractLazyVirtualTable
|
||||||
|
{
|
||||||
|
final int[][] copyOfKeys = new int[rowCount][], copyOfVals = new int[rowCount][];
|
||||||
|
TestTable(Sorted sorted, Sorted sortedByPartitionKey)
|
||||||
|
{
|
||||||
|
super(LazyVirtualTableTest.metadata(keyCount, valCount, sorted + "_" + sortedByPartitionKey), FAIL, sorted, sortedByPartitionKey);
|
||||||
|
IntUnaryOperator index;
|
||||||
|
switch (sorted)
|
||||||
|
{
|
||||||
|
default: throw new UnhandledEnum(sorted);
|
||||||
|
case SORTED: throw new UnsupportedOperationException(); // TODO (desired): test this
|
||||||
|
case ASC:
|
||||||
|
index = i -> i;
|
||||||
|
break;
|
||||||
|
case UNSORTED:
|
||||||
|
int[] indexes = IntStream.range(0, rowCount).toArray();
|
||||||
|
index = i -> indexes[i];
|
||||||
|
switch (sortedByPartitionKey)
|
||||||
|
{
|
||||||
|
default: throw new UnhandledEnum(sortedByPartitionKey);
|
||||||
|
case DESC: case SORTED: throw new UnsupportedOperationException(); // TODO (desired): test this
|
||||||
|
case UNSORTED:
|
||||||
|
shuffle(indexes, 0, indexes.length, rnd);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASC:
|
||||||
|
int prev = 0;
|
||||||
|
for (int i = 1; i < rowCount ; ++i)
|
||||||
|
{
|
||||||
|
if (allKeys[prev][0] != allKeys[i][0])
|
||||||
|
{
|
||||||
|
shuffle(indexes, prev, i, rnd);
|
||||||
|
prev = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shuffle(indexes, prev, indexes.length, rnd);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DESC:
|
||||||
|
index = i -> rowCount - (i + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (int i = 0 ; i < rowCount ; ++i)
|
||||||
|
{
|
||||||
|
copyOfKeys[i] = allKeys[index.applyAsInt(i)];
|
||||||
|
copyOfVals[i] = allVals[index.applyAsInt(i)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void collect(PartitionsCollector collector)
|
||||||
|
{
|
||||||
|
for (int i = 0 ; i < copyOfKeys.length ; ++i)
|
||||||
|
{
|
||||||
|
int[] keys = copyOfKeys[i];
|
||||||
|
int[] vals = copyOfVals[i];
|
||||||
|
Object[] ckeys = new Object[keyCount - 1];
|
||||||
|
for (int k = 1; k < keyCount ; ++k)
|
||||||
|
ckeys[k - 1] = keys[k];
|
||||||
|
collector.partition(keys[0])
|
||||||
|
.collect(rows -> rows.add(ckeys).lazyCollect(cols -> {
|
||||||
|
for (int j = 0 ; j < vals.length ; ++j)
|
||||||
|
cols.add(valNames[j], vals[j]);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<VirtualTable> tables = new ArrayList<>();
|
||||||
|
tables.add(new TestTable(ASC, ASC));
|
||||||
|
tables.add(new TestTable(DESC, DESC));
|
||||||
|
tables.add(new TestTable(UNSORTED, ASC));
|
||||||
|
tables.add(new TestTable(UNSORTED, UNSORTED));
|
||||||
|
VirtualKeyspaceRegistry.instance.register(new VirtualKeyspace(KEYSPACE, tables));
|
||||||
|
|
||||||
|
LargeBitSet matchingRows = new LargeBitSet(rowCount);
|
||||||
|
int fieldCount = keyCount + valCount;
|
||||||
|
LargeBitSet filteringKeys = new LargeBitSet(fieldCount);
|
||||||
|
StringBuilder where = new StringBuilder();
|
||||||
|
for (int q = 0 ; q < queryLoops ; q++)
|
||||||
|
{
|
||||||
|
matchingRows.setRange(0, rowCount);
|
||||||
|
filteringKeys.setRange(0, fieldCount);
|
||||||
|
where.setLength(0);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
int f;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
int min = filteringKeys.nextSetBit(0);
|
||||||
|
f = rnd.nextInt(min, fieldCount);
|
||||||
|
} while (!filteringKeys.unset(f));
|
||||||
|
|
||||||
|
int val;
|
||||||
|
if (rowCount == 0 || rnd.nextBoolean())
|
||||||
|
{
|
||||||
|
val = rnd.nextInt(f < keyCount ? keyDomains[f] : valDomains[f - keyCount]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int i = rnd.nextInt(rowCount);
|
||||||
|
val = f < keyCount ? allKeys[i][f] : allVals[i][f - keyCount];
|
||||||
|
}
|
||||||
|
Cmp cmp = rnd.pick(CMPS);
|
||||||
|
if (where.length() > 0)
|
||||||
|
where.append(" AND ");
|
||||||
|
|
||||||
|
where.append(f < keyCount ? keyNames[f] : valNames[f - keyCount]);
|
||||||
|
where.append(' ');
|
||||||
|
where.append(cmp.str);
|
||||||
|
where.append(' ');
|
||||||
|
where.append(val);
|
||||||
|
|
||||||
|
IntBiPredicate pred = cmp.predicate();
|
||||||
|
for (int i = matchingRows.nextSetBit(0); i >= 0; i = matchingRows.nextSetBit(i + 1, -1))
|
||||||
|
{
|
||||||
|
int rowVal = f < keyCount ? allKeys[i][f] : allVals[i][f - keyCount];
|
||||||
|
if (!pred.test(rowVal, val))
|
||||||
|
matchingRows.unset(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
int matchCount = matchingRows.getSetBitCount();
|
||||||
|
int limit = rnd.nextBoolean() ? 0 : rnd.nextInt(1, Math.max(2, 2 * matchCount));
|
||||||
|
for (VirtualTable table : tables)
|
||||||
|
{
|
||||||
|
UntypedResultSet results = execute("select * from " + table.metadata() + " where " + where + (limit <= 0 ? "" : " LIMIT " + limit));
|
||||||
|
|
||||||
|
int i = -1;
|
||||||
|
for (UntypedResultSet.Row row : results)
|
||||||
|
{
|
||||||
|
i = matchingRows.nextSetBit(i + 1);
|
||||||
|
|
||||||
|
for (int k = 0 ; k < keyCount ; ++k)
|
||||||
|
Assertions.assertThat(allKeys[i][k]).isEqualTo(row.getInt(keyNames[k]));
|
||||||
|
|
||||||
|
for (int v = 0 ; v < valCount ; ++v)
|
||||||
|
Assertions.assertThat(allVals[i][v]).isEqualTo(row.getInt(valNames[v]));
|
||||||
|
}
|
||||||
|
|
||||||
|
i = matchingRows.nextSetBit(i + 1);
|
||||||
|
if (limit >= matchCount || limit == 0) Assertions.assertThat(-1).isEqualTo(i);
|
||||||
|
else Assertions.assertThat(i).isNotEqualTo(-1);
|
||||||
|
Assertions.assertThat(results.size()).isEqualTo(limit == 0 ? matchCount : Math.min(matchCount, limit));
|
||||||
|
}
|
||||||
|
} while (matchingRows.getSetBitCount() > 0 && filteringKeys.getSetBitCount() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualKeyspaceRegistry.instance.register(new VirtualKeyspace(KEYSPACE, tables));
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Cmp
|
||||||
|
{
|
||||||
|
LT("<"), LE("<="), NE("!="), EQ("="), GE(">="), GT(">");
|
||||||
|
|
||||||
|
static final Cmp[] CMPS = values();
|
||||||
|
final String str;
|
||||||
|
|
||||||
|
Cmp(String str)
|
||||||
|
{
|
||||||
|
this.str = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntBiPredicate predicate()
|
||||||
|
{
|
||||||
|
switch (this)
|
||||||
|
{
|
||||||
|
default: throw new UnhandledEnum(this);
|
||||||
|
case LT: return (a, b) -> a < b;
|
||||||
|
case LE: return (a, b) -> a <= b;
|
||||||
|
case NE: return (a, b) -> a != b;
|
||||||
|
case EQ: return (a, b) -> a == b;
|
||||||
|
case GE: return (a, b) -> a >= b;
|
||||||
|
case GT: return (a, b) -> a > b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TableMetadata metadata(int keys, int vals, String name)
|
||||||
|
{
|
||||||
|
return CreateTableStatement.parse(createTable(keys, vals, name), KEYSPACE)
|
||||||
|
.comment("")
|
||||||
|
.kind(TableMetadata.Kind.VIRTUAL)
|
||||||
|
.partitioner(new LocalPartitioner(Int32Type.instance))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String createTable(int keys, int vals, String name)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder("CREATE TABLE ");
|
||||||
|
sb.append(name).append(" (");
|
||||||
|
for (int i = 1 ; i <= keys ; ++i)
|
||||||
|
sb.append('k').append(i).append(" int, ");
|
||||||
|
for (int i = 1 ; i <= vals ; ++i)
|
||||||
|
sb.append('v').append(i).append(" int, ");
|
||||||
|
sb.append(" PRIMARY KEY (");
|
||||||
|
for (int i = 1 ; i <= keys ; ++i)
|
||||||
|
{
|
||||||
|
if (i > 1) sb.append(", ");
|
||||||
|
sb.append('k').append(i);
|
||||||
|
}
|
||||||
|
sb.append("))");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void shuffle(int[] array, int start, int end, RandomSource rnd)
|
||||||
|
{
|
||||||
|
while (end - start > 1)
|
||||||
|
{
|
||||||
|
int i = start + rnd.nextInt(end - start);
|
||||||
|
int tmp = array[start];
|
||||||
|
array[start] = array[i];
|
||||||
|
array[i] = tmp;
|
||||||
|
++start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -269,7 +269,7 @@ public class LocalRepairTablesTest extends CQLTester
|
||||||
|
|
||||||
private static RepairCoordinator.NeighborsAndRanges neighbors()
|
private static RepairCoordinator.NeighborsAndRanges neighbors()
|
||||||
{
|
{
|
||||||
return new RepairCoordinator.NeighborsAndRanges(false, ADDRESSES, ImmutableList.of(COMMON_RANGE));
|
return new RepairCoordinator.NeighborsAndRanges(false, false, ADDRESSES, ImmutableList.of(COMMON_RANGE));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Range<Token> range(long a, long b)
|
private static Range<Token> range(long a, long b)
|
||||||
|
|
|
||||||
|
|
@ -521,7 +521,8 @@ public class RouteIndexTest extends CQLTester
|
||||||
AccordService.MetadataChangeListener.instance.resetForTesting(metadata);
|
AccordService.MetadataChangeListener.instance.resetForTesting(metadata);
|
||||||
NodeId tcmNodeId = metadata.myNodeId();
|
NodeId tcmNodeId = metadata.myNodeId();
|
||||||
AccordService.unsafeSetNewAccordService(null);
|
AccordService.unsafeSetNewAccordService(null);
|
||||||
return AccordService.startup(tcmNodeId);
|
AccordService.localStartup(tcmNodeId);
|
||||||
|
return AccordService.distributedStartup();
|
||||||
}
|
}
|
||||||
|
|
||||||
TxnId nextTxnId(Domain domain)
|
TxnId nextTxnId(Domain domain)
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,9 @@ public class NeighborsAndRangesTest extends AbstractRepairTest
|
||||||
public void filterCommonIncrementalRangesNotForced()
|
public void filterCommonIncrementalRangesNotForced()
|
||||||
{
|
{
|
||||||
CommonRange cr = new CommonRange(PARTICIPANTS, Collections.emptySet(), ALL_RANGES);
|
CommonRange cr = new CommonRange(PARTICIPANTS, Collections.emptySet(), ALL_RANGES);
|
||||||
NeighborsAndRanges nr = new NeighborsAndRanges(false, PARTICIPANTS, Collections.singletonList(cr));
|
NeighborsAndRanges nr = new NeighborsAndRanges(false, false, PARTICIPANTS, Collections.singletonList(cr));
|
||||||
List<CommonRange> expected = Lists.newArrayList(cr);
|
List<CommonRange> expected = Lists.newArrayList(cr);
|
||||||
List<CommonRange> actual = nr.filterCommonRanges(null, null);
|
List<CommonRange> actual = nr.filterCommonRanges(null, null).commonRanges;
|
||||||
|
|
||||||
Assert.assertEquals(expected, actual);
|
Assert.assertEquals(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
@ -59,8 +59,8 @@ public class NeighborsAndRangesTest extends AbstractRepairTest
|
||||||
new CommonRange(Sets.newHashSet(PARTICIPANT2, PARTICIPANT3), Collections.emptySet(), Sets.newHashSet(RANGE3), true),
|
new CommonRange(Sets.newHashSet(PARTICIPANT2, PARTICIPANT3), Collections.emptySet(), Sets.newHashSet(RANGE3), true),
|
||||||
new CommonRange(Sets.newHashSet(PARTICIPANT2, PARTICIPANT3), Collections.emptySet(), Sets.newHashSet(RANGE2), false));
|
new CommonRange(Sets.newHashSet(PARTICIPANT2, PARTICIPANT3), Collections.emptySet(), Sets.newHashSet(RANGE2), false));
|
||||||
|
|
||||||
NeighborsAndRanges nr = new NeighborsAndRanges(true, liveEndpoints, initial);
|
NeighborsAndRanges nr = new NeighborsAndRanges(true, false, liveEndpoints, initial);
|
||||||
List<CommonRange> actual = nr.filterCommonRanges(null, null);
|
List<CommonRange> actual = nr.filterCommonRanges(null, null).commonRanges;
|
||||||
|
|
||||||
Assert.assertEquals(expected, actual);
|
Assert.assertEquals(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -128,8 +128,8 @@ public class RepairJobTest
|
||||||
boolean dontPurgeTombstones, boolean repairAccord, String... cfnames)
|
boolean dontPurgeTombstones, boolean repairAccord, String... cfnames)
|
||||||
{
|
{
|
||||||
super(SharedContext.Global.instance, new Scheduler.NoopScheduler(),
|
super(SharedContext.Global.instance, new Scheduler.NoopScheduler(),
|
||||||
parentRepairSession, commonRange, excludedDeadNodes, keyspace, parallelismDegree, isIncremental, pullRepair,
|
parentRepairSession, commonRange, excludedDeadNodes, keyspace, parallelismDegree, false, isIncremental, pullRepair,
|
||||||
previewKind, optimiseStreams, repairData, repairPaxos, dontPurgeTombstones, repairAccord, cfnames);
|
previewKind, optimiseStreams, repairData, repairPaxos, dontPurgeTombstones, repairAccord, false, cfnames);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,8 @@ public class RepairSessionTest
|
||||||
RepairSession session = new RepairSession(SharedContext.Global.instance, new Scheduler.NoopScheduler(), parentSessionId,
|
RepairSession session = new RepairSession(SharedContext.Global.instance, new Scheduler.NoopScheduler(), parentSessionId,
|
||||||
new CommonRange(endpoints, Collections.emptySet(), Arrays.asList(repairRange)),
|
new CommonRange(endpoints, Collections.emptySet(), Arrays.asList(repairRange)),
|
||||||
false, "Keyspace1", RepairParallelism.SEQUENTIAL,
|
false, "Keyspace1", RepairParallelism.SEQUENTIAL,
|
||||||
false, false, PreviewKind.NONE, false,
|
false, false, false, PreviewKind.NONE, false,
|
||||||
false, false, false, false, "Standard1");
|
false, false, false, false, false, "Standard1");
|
||||||
|
|
||||||
// perform convict
|
// perform convict
|
||||||
session.convict(remote, Double.MAX_VALUE);
|
session.convict(remote, Double.MAX_VALUE);
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,6 @@ import org.apache.cassandra.cql3.statements.schema.TableAttributes;
|
||||||
import org.apache.cassandra.repair.RepairCoordinator;
|
import org.apache.cassandra.repair.RepairCoordinator;
|
||||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||||
import org.apache.cassandra.locator.LocalStrategy;
|
import org.apache.cassandra.locator.LocalStrategy;
|
||||||
import org.apache.cassandra.repair.RepairParallelism;
|
|
||||||
import org.apache.cassandra.repair.messages.RepairOption;
|
|
||||||
import org.apache.cassandra.schema.SystemDistributedKeyspace;
|
import org.apache.cassandra.schema.SystemDistributedKeyspace;
|
||||||
import org.apache.cassandra.service.StorageService;
|
import org.apache.cassandra.service.StorageService;
|
||||||
|
|
||||||
|
|
@ -62,7 +60,6 @@ import org.apache.cassandra.metrics.AutoRepairMetrics;
|
||||||
import org.apache.cassandra.schema.SchemaConstants;
|
import org.apache.cassandra.schema.SchemaConstants;
|
||||||
import org.apache.cassandra.schema.TableMetadata;
|
import org.apache.cassandra.schema.TableMetadata;
|
||||||
import org.apache.cassandra.service.AutoRepairService;
|
import org.apache.cassandra.service.AutoRepairService;
|
||||||
import org.apache.cassandra.streaming.PreviewKind;
|
|
||||||
import org.apache.cassandra.utils.FBUtilities;
|
import org.apache.cassandra.utils.FBUtilities;
|
||||||
import org.apache.cassandra.utils.progress.ProgressEvent;
|
import org.apache.cassandra.utils.progress.ProgressEvent;
|
||||||
import org.apache.cassandra.utils.progress.ProgressEventType;
|
import org.apache.cassandra.utils.progress.ProgressEventType;
|
||||||
|
|
@ -811,9 +808,6 @@ public class AutoRepairParameterizedTest extends CQLTester
|
||||||
@Test
|
@Test
|
||||||
public void testSchedulerIgnoresErrorsFromUnrelatedRepairRunables()
|
public void testSchedulerIgnoresErrorsFromUnrelatedRepairRunables()
|
||||||
{
|
{
|
||||||
RepairOption options = new RepairOption(RepairParallelism.PARALLEL, true, repairType == AutoRepairConfig.RepairType.INCREMENTAL, false,
|
|
||||||
AutoRepairService.instance.getAutoRepairConfig().getRepairThreads(repairType), Collections.emptySet(),
|
|
||||||
false, false, PreviewKind.NONE, false, true, true, false, false, false);
|
|
||||||
AutoRepairState repairState = AutoRepair.instance.repairStates.get(repairType);
|
AutoRepairState repairState = AutoRepair.instance.repairStates.get(repairType);
|
||||||
AutoRepairState spyState = spy(repairState);
|
AutoRepairState spyState = spy(repairState);
|
||||||
AtomicReference<AutoRepair.RepairProgressListener> failingListener = new AtomicReference<>();
|
AtomicReference<AutoRepair.RepairProgressListener> failingListener = new AtomicReference<>();
|
||||||
|
|
|
||||||
|
|
@ -280,7 +280,8 @@ public class AccordCacheTest
|
||||||
public void testEvictionOnAcquire()
|
public void testEvictionOnAcquire()
|
||||||
{
|
{
|
||||||
AccordCacheMetrics cacheMetrics = new AccordCacheMetrics(nextMetricId());
|
AccordCacheMetrics cacheMetrics = new AccordCacheMetrics(nextMetricId());
|
||||||
AccordCacheMetrics.Shard shard = cacheMetrics.newShard(new NoOpLock());
|
NoOpLock lock = new NoOpLock();
|
||||||
|
AccordCacheMetrics.Shard shard = cacheMetrics.newShard(lock);
|
||||||
|
|
||||||
ManualExecutor executor = new ManualExecutor();
|
ManualExecutor executor = new ManualExecutor();
|
||||||
AccordCache cache = new AccordCache(saveExecutor(executor), nodeSize(1) * 5);
|
AccordCache cache = new AccordCache(saveExecutor(executor), nodeSize(1) * 5);
|
||||||
|
|
@ -297,6 +298,7 @@ public class AccordCacheTest
|
||||||
testLoad(executor, safeString, Integer.toString(i));
|
testLoad(executor, safeString, Integer.toString(i));
|
||||||
Assert.assertTrue(instance.isReferenced(safeString.key()));
|
Assert.assertTrue(instance.isReferenced(safeString.key()));
|
||||||
instance.release(safeString, null);
|
instance.release(safeString, null);
|
||||||
|
cache.tryShrinkOrEvict(lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
assertCacheState(cache, 0, 5, nodeSize(1) * 5);
|
assertCacheState(cache, 0, 5, nodeSize(1) * 5);
|
||||||
|
|
@ -305,6 +307,7 @@ public class AccordCacheTest
|
||||||
assertCacheMetrics(cacheMetrics, 0, 5, 5, 5);
|
assertCacheMetrics(cacheMetrics, 0, 5, 5, 5);
|
||||||
|
|
||||||
SafeString safeString = instance.acquire("5");
|
SafeString safeString = instance.acquire("5");
|
||||||
|
cache.tryShrinkOrEvict(lock);
|
||||||
Assert.assertTrue(instance.isReferenced(safeString.key()));
|
Assert.assertTrue(instance.isReferenced(safeString.key()));
|
||||||
|
|
||||||
// since it's not loaded, only the node size is counted here
|
// since it's not loaded, only the node size is counted here
|
||||||
|
|
@ -327,7 +330,8 @@ public class AccordCacheTest
|
||||||
public void testEvictionOnRelease()
|
public void testEvictionOnRelease()
|
||||||
{
|
{
|
||||||
AccordCacheMetrics cacheMetrics = new AccordCacheMetrics(nextMetricId());
|
AccordCacheMetrics cacheMetrics = new AccordCacheMetrics(nextMetricId());
|
||||||
AccordCacheMetrics.Shard shard = cacheMetrics.newShard(new NoOpLock());
|
NoOpLock lock = new NoOpLock();
|
||||||
|
AccordCacheMetrics.Shard shard = cacheMetrics.newShard(lock);
|
||||||
|
|
||||||
ManualExecutor executor = new ManualExecutor();
|
ManualExecutor executor = new ManualExecutor();
|
||||||
AccordCache cache = new AccordCache(saveExecutor(executor), nodeSize(1) * 4);
|
AccordCache cache = new AccordCache(saveExecutor(executor), nodeSize(1) * 4);
|
||||||
|
|
@ -343,6 +347,7 @@ public class AccordCacheTest
|
||||||
items[i] = safeString;
|
items[i] = safeString;
|
||||||
testLoad(executor, safeString, Integer.toString(i));
|
testLoad(executor, safeString, Integer.toString(i));
|
||||||
Assert.assertTrue(instance.isReferenced(safeString.key()));
|
Assert.assertTrue(instance.isReferenced(safeString.key()));
|
||||||
|
cache.tryShrinkOrEvict(lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
assertCacheState(cache, 5, 5, nodeSize(1) * 5);
|
assertCacheState(cache, 5, 5, nodeSize(1) * 5);
|
||||||
|
|
@ -351,12 +356,14 @@ public class AccordCacheTest
|
||||||
Assert.assertNull(cache.tail());
|
Assert.assertNull(cache.tail());
|
||||||
|
|
||||||
instance.release(items[2], null);
|
instance.release(items[2], null);
|
||||||
|
cache.tryShrinkOrEvict(lock);
|
||||||
assertCacheState(cache, 4, 4, nodeSize(1) * 4);
|
assertCacheState(cache, 4, 4, nodeSize(1) * 4);
|
||||||
assertCacheMetrics(cacheMetrics, 0, 5, 5, 5);
|
assertCacheMetrics(cacheMetrics, 0, 5, 5, 5);
|
||||||
Assert.assertNull(cache.head());
|
Assert.assertNull(cache.head());
|
||||||
Assert.assertNull(cache.tail());
|
Assert.assertNull(cache.tail());
|
||||||
|
|
||||||
instance.release(items[4], null);
|
instance.release(items[4], null);
|
||||||
|
cache.tryShrinkOrEvict(lock);
|
||||||
assertCacheState(cache, 3, 4, nodeSize(1) * 4);
|
assertCacheState(cache, 3, 4, nodeSize(1) * 4);
|
||||||
assertCacheMetrics(cacheMetrics, 0, 5, 5, 5);
|
assertCacheMetrics(cacheMetrics, 0, 5, 5, 5);
|
||||||
Assert.assertSame(items[4].global, cache.head());
|
Assert.assertSame(items[4].global, cache.head());
|
||||||
|
|
@ -401,7 +408,8 @@ public class AccordCacheTest
|
||||||
public void evictionBlockedOnSaving()
|
public void evictionBlockedOnSaving()
|
||||||
{
|
{
|
||||||
AccordCacheMetrics cacheMetrics = new AccordCacheMetrics(nextMetricId());
|
AccordCacheMetrics cacheMetrics = new AccordCacheMetrics(nextMetricId());
|
||||||
AccordCacheMetrics.Shard shard = cacheMetrics.newShard(new NoOpLock());
|
NoOpLock lock = new NoOpLock();
|
||||||
|
AccordCacheMetrics.Shard shard = cacheMetrics.newShard(lock);
|
||||||
|
|
||||||
ManualExecutor executor = new ManualExecutor();
|
ManualExecutor executor = new ManualExecutor();
|
||||||
AccordCache cache = new AccordCache(saveExecutor(executor), nodeSize(1) * 3 + nodeSize(3));
|
AccordCache cache = new AccordCache(saveExecutor(executor), nodeSize(1) * 3 + nodeSize(3));
|
||||||
|
|
@ -422,6 +430,7 @@ public class AccordCacheTest
|
||||||
testLoad(executor, item, Integer.toString(i));
|
testLoad(executor, item, Integer.toString(i));
|
||||||
Assert.assertTrue(instance.isReferenced(item.key()));
|
Assert.assertTrue(instance.isReferenced(item.key()));
|
||||||
instance.release(item, null);
|
instance.release(item, null);
|
||||||
|
cache.tryShrinkOrEvict(lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
assertCacheState(cache, 0, 4, nodeSize(1) * 3 + nodeSize(2));
|
assertCacheState(cache, 0, 4, nodeSize(1) * 3 + nodeSize(2));
|
||||||
|
|
@ -430,6 +439,7 @@ public class AccordCacheTest
|
||||||
// force cache eviction
|
// force cache eviction
|
||||||
instance.acquire(Integer.toString(0));
|
instance.acquire(Integer.toString(0));
|
||||||
cache.setCapacity(0);
|
cache.setCapacity(0);
|
||||||
|
cache.tryShrinkOrEvict(lock);
|
||||||
|
|
||||||
// all should have been evicted except 0
|
// all should have been evicted except 0
|
||||||
assertCacheState(cache, 1, 1, nodeSize(2));
|
assertCacheState(cache, 1, 1, nodeSize(2));
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ public class AccordCommandTest
|
||||||
Key key = (Key)txn.keys().get(0);
|
Key key = (Key)txn.keys().get(0);
|
||||||
RoutingKey homeKey = key.toUnseekable();
|
RoutingKey homeKey = key.toUnseekable();
|
||||||
FullRoute<?> fullRoute = txn.keys().toRoute(homeKey);
|
FullRoute<?> fullRoute = txn.keys().toRoute(homeKey);
|
||||||
Route<?> route = fullRoute.slice(fullRange(txn));
|
Route<?> route = fullRoute.overlapping(fullRange(txn));
|
||||||
PartialTxn partialTxn = txn.intersecting(route, true);
|
PartialTxn partialTxn = txn.intersecting(route, true);
|
||||||
PreAccept preAccept = PreAccept.SerializerSupport.create(txnId, route, 1, 1, 1, partialTxn, null, false, fullRoute);
|
PreAccept preAccept = PreAccept.SerializerSupport.create(txnId, route, 1, 1, 1, partialTxn, null, false, fullRoute);
|
||||||
|
|
||||||
|
|
@ -195,7 +195,7 @@ public class AccordCommandTest
|
||||||
Key key = (Key)txn.keys().get(0);
|
Key key = (Key)txn.keys().get(0);
|
||||||
RoutingKey homeKey = key.toUnseekable();
|
RoutingKey homeKey = key.toUnseekable();
|
||||||
FullRoute<?> fullRoute = txn.keys().toRoute(homeKey);
|
FullRoute<?> fullRoute = txn.keys().toRoute(homeKey);
|
||||||
Route<?> route = fullRoute.slice(fullRange(txn));
|
Route<?> route = fullRoute.overlapping(fullRange(txn));
|
||||||
PartialTxn partialTxn = txn.intersecting(route, true);
|
PartialTxn partialTxn = txn.intersecting(route, true);
|
||||||
PreAccept preAccept1 = PreAccept.SerializerSupport.create(txnId1, route, 1, 1, 1, partialTxn, null, false, fullRoute);
|
PreAccept preAccept1 = PreAccept.SerializerSupport.create(txnId1, route, 1, 1, 1, partialTxn, null, false, fullRoute);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ import accord.messages.ReadData;
|
||||||
import accord.messages.ReadData.CommitOrReadNack;
|
import accord.messages.ReadData.CommitOrReadNack;
|
||||||
import accord.topology.TopologyUtils;
|
import accord.topology.TopologyUtils;
|
||||||
import org.apache.cassandra.service.accord.AccordFetchCoordinator.AccordFetchRequest;
|
import org.apache.cassandra.service.accord.AccordFetchCoordinator.AccordFetchRequest;
|
||||||
import org.apache.cassandra.service.accord.api.AccordAgent;
|
|
||||||
import org.apache.cassandra.service.accord.api.AccordTimeService;
|
import org.apache.cassandra.service.accord.api.AccordTimeService;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
|
@ -72,7 +71,7 @@ public class AccordMessageSinkTest
|
||||||
DatabaseDescriptor.clientInitialization();
|
DatabaseDescriptor.clientInitialization();
|
||||||
DatabaseDescriptor.setPartitionerUnsafe(Murmur3Partitioner.instance);
|
DatabaseDescriptor.setPartitionerUnsafe(Murmur3Partitioner.instance);
|
||||||
ClusterMetadataService.initializeForClients();
|
ClusterMetadataService.initializeForClients();
|
||||||
sink = new AccordMessageSink(Mockito.mock(AccordAgent.class), messaging, mapping, new RequestCallbacks(new AccordTimeService()));
|
sink = new AccordMessageSink(messaging, mapping, new RequestCallbacks(new AccordTimeService()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,7 @@ public class AccordTaskTest
|
||||||
RoutingKey routingKey = partialTxn.keys().get(0).asKey().toUnseekable();
|
RoutingKey routingKey = partialTxn.keys().get(0).asKey().toUnseekable();
|
||||||
FullRoute<?> route = partialTxn.keys().toRoute(routingKey);
|
FullRoute<?> route = partialTxn.keys().toRoute(routingKey);
|
||||||
Ranges ranges = AccordTestUtils.fullRange(partialTxn.keys());
|
Ranges ranges = AccordTestUtils.fullRange(partialTxn.keys());
|
||||||
route.slice(ranges);
|
route.overlapping(ranges);
|
||||||
PartialDeps deps = PartialDeps.builder(ranges, true).build();
|
PartialDeps deps = PartialDeps.builder(ranges, true).build();
|
||||||
|
|
||||||
Command command = getBlocking(commandStore.submit(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> {
|
Command command = getBlocking(commandStore.submit(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> {
|
||||||
|
|
@ -205,10 +205,15 @@ public class AccordTaskTest
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// clear cache
|
// clear cache
|
||||||
try (ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();)
|
long cacheSize;
|
||||||
|
try (ExclusiveGlobalCaches cache = commandStore.executor().lockCaches())
|
||||||
{
|
{
|
||||||
long cacheSize = cache.global.capacity();
|
cacheSize = cache.global.capacity();
|
||||||
cache.global.setCapacity(0);
|
cache.global.setCapacity(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (ExclusiveGlobalCaches cache = commandStore.executor().lockCaches())
|
||||||
|
{
|
||||||
cache.global.setCapacity(cacheSize);
|
cache.global.setCapacity(cacheSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,7 +243,7 @@ public class AccordTaskTest
|
||||||
RoutingKey routingKey = partialTxn.keys().get(0).asKey().toUnseekable();
|
RoutingKey routingKey = partialTxn.keys().get(0).asKey().toUnseekable();
|
||||||
FullRoute<?> route = partialTxn.keys().toRoute(routingKey);
|
FullRoute<?> route = partialTxn.keys().toRoute(routingKey);
|
||||||
Ranges ranges = AccordTestUtils.fullRange(partialTxn.keys());
|
Ranges ranges = AccordTestUtils.fullRange(partialTxn.keys());
|
||||||
Route<?> partialRoute = route.slice(ranges);
|
Route<?> partialRoute = route.overlapping(ranges);
|
||||||
PartialDeps deps = PartialDeps.builder(ranges, true).build();
|
PartialDeps deps = PartialDeps.builder(ranges, true).build();
|
||||||
|
|
||||||
Command command = getBlocking(commandStore.submit(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> {
|
Command command = getBlocking(commandStore.submit(contextFor(txnId, route, SYNC, READ_WRITE, "Test"), safe -> {
|
||||||
|
|
@ -250,10 +255,14 @@ public class AccordTaskTest
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// clear cache
|
// clear cache
|
||||||
|
long cacheSize;
|
||||||
try (ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();)
|
try (ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();)
|
||||||
{
|
{
|
||||||
long cacheSize = cache.global.capacity();
|
cacheSize = cache.global.capacity();
|
||||||
cache.global.setCapacity(0);
|
cache.global.setCapacity(0);
|
||||||
|
}
|
||||||
|
try (ExclusiveGlobalCaches cache = commandStore.executor().lockCaches();)
|
||||||
|
{
|
||||||
cache.global.setCapacity(cacheSize);
|
cache.global.setCapacity(cacheSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -280,57 +289,58 @@ public class AccordTaskTest
|
||||||
.withExamples(50)
|
.withExamples(50)
|
||||||
.forAll(Gens.random(), Gens.lists(txnIdGen).ofSizeBetween(1, 2))
|
.forAll(Gens.random(), Gens.lists(txnIdGen).ofSizeBetween(1, 2))
|
||||||
.check((rs, ids) -> {
|
.check((rs, ids) -> {
|
||||||
before(); // truncate tables
|
|
||||||
|
|
||||||
Participants<RoutingKey> participants = keys.toParticipants();
|
before(); // truncate tables
|
||||||
assertNoReferences(commandStore, ids, participants);
|
|
||||||
createCommand(commandStore, rs, ids);
|
|
||||||
awaitDone(commandStore, ids, participants);
|
|
||||||
assertNoReferences(commandStore, ids, participants);
|
|
||||||
|
|
||||||
PreLoadContext ctx = contextFor(ids.get(0), ids.size() == 1 ? null : ids.get(1), participants, SYNC, READ_WRITE, "Test");
|
Participants<RoutingKey> participants = keys.toParticipants();
|
||||||
Consumer<SafeCommandStore> consumer = Mockito.mock(Consumer.class);
|
assertNoReferences(commandStore, ids, participants);
|
||||||
|
createCommand(commandStore, rs, ids);
|
||||||
|
awaitDone(commandStore, ids, participants);
|
||||||
|
assertNoReferences(commandStore, ids, participants);
|
||||||
|
|
||||||
Map<TxnId, Boolean> failed = selectFailedTxn(rs, ids);
|
PreLoadContext ctx = contextFor(ids.get(0), ids.size() == 1 ? null : ids.get(1), participants, SYNC, READ_WRITE, "Test");
|
||||||
try (ExclusiveGlobalCaches caches = commandStore.executor().lockCaches())
|
Consumer<SafeCommandStore> consumer = Mockito.mock(Consumer.class);
|
||||||
{
|
|
||||||
caches.commands.unsafeSetLoadFunction((s, txnId) ->
|
Map<TxnId, Boolean> failed = selectFailedTxn(rs, ids);
|
||||||
|
try (ExclusiveGlobalCaches caches = commandStore.executor().lockCaches())
|
||||||
{
|
{
|
||||||
logger.info("Attempting to load {}; expected to fail? {}", txnId, failed.get(txnId));
|
caches.commands.unsafeSetLoadFunction((s, txnId) ->
|
||||||
if (!failed.get(txnId))
|
{
|
||||||
return commandStore.loadCommand(txnId);
|
logger.info("Attempting to load {}; expected to fail? {}", txnId, failed.get(txnId));
|
||||||
throw new NullPointerException("txn_id " + txnId);
|
if (!failed.get(txnId))
|
||||||
});
|
return commandStore.loadCommand(txnId);
|
||||||
}
|
throw new NullPointerException("txn_id " + txnId);
|
||||||
AccordTask<Void> o1 = AccordTask.create(commandStore, ctx, consumer);
|
});
|
||||||
AssertionUtils.assertThatThrownBy(() -> getBlocking(o1.chain()))
|
}
|
||||||
.hasRootCause()
|
AccordTask<Void> o1 = AccordTask.create(commandStore, ctx, consumer);
|
||||||
.isInstanceOf(NullPointerException.class)
|
AssertionUtils.assertThatThrownBy(() -> getBlocking(o1.chain()))
|
||||||
.hasNoSuppressedExceptions();
|
.hasRootCause()
|
||||||
|
.isInstanceOf(NullPointerException.class)
|
||||||
|
.hasNoSuppressedExceptions();
|
||||||
|
|
||||||
Mockito.verifyNoInteractions(consumer);
|
Mockito.verifyNoInteractions(consumer);
|
||||||
|
|
||||||
assertNoReferences(commandStore, ids, participants);
|
assertNoReferences(commandStore, ids, participants);
|
||||||
// the first failed load causes the whole operation to fail, so some ids may still be pending
|
// the first failed load causes the whole operation to fail, so some ids may still be pending
|
||||||
// to make sure the next operation does not see a PENDING that will fail, wait for all loads to complete
|
// to make sure the next operation does not see a PENDING that will fail, wait for all loads to complete
|
||||||
awaitDone(commandStore, ids, participants);
|
awaitDone(commandStore, ids, participants);
|
||||||
|
|
||||||
// can we recover?
|
// can we recover?
|
||||||
try (ExclusiveGlobalCaches caches = commandStore.executor().lockCaches())
|
try (ExclusiveGlobalCaches caches = commandStore.executor().lockCaches())
|
||||||
{
|
{
|
||||||
caches.commands.unsafeSetLoadFunction((s, txnId) -> {
|
caches.commands.unsafeSetLoadFunction((s, txnId) -> {
|
||||||
Command cmd = commandStore.loadCommand(txnId);
|
Command cmd = commandStore.loadCommand(txnId);
|
||||||
return cmd;
|
return cmd;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
AccordTask<Void> o2 = AccordTask.create(commandStore, ctx, store -> {
|
||||||
|
ids.forEach(id -> {
|
||||||
|
store.ifInitialised(id).readyToExecute(store);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
getBlocking(o2.chain());
|
||||||
AccordTask<Void> o2 = AccordTask.create(commandStore, ctx, store -> {
|
awaitDone(commandStore, ids, participants);
|
||||||
ids.forEach(id -> {
|
assertNoReferences(commandStore, ids, participants);
|
||||||
store.ifInitialised(id).readyToExecute(store);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
getBlocking(o2.chain());
|
|
||||||
awaitDone(commandStore, ids, participants);
|
|
||||||
assertNoReferences(commandStore, ids, participants);
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -392,7 +392,7 @@ public class EpochSyncTest
|
||||||
.isFalse();
|
.isFalse();
|
||||||
|
|
||||||
// validate topology manager
|
// validate topology manager
|
||||||
Ranges ranges = tm.active().globalForEpoch(epoch).ranges().mergeTouching();
|
Ranges ranges = tm.active().getKnown(epoch).global().ranges().mergeTouching();
|
||||||
Ranges actual = tm.unsafeQuorumReady(epoch).mergeTouching();
|
Ranges actual = tm.unsafeQuorumReady(epoch).mergeTouching();
|
||||||
Assertions.assertThat(actual)
|
Assertions.assertThat(actual)
|
||||||
.describedAs("node%s does not have all expected sync ranges for epoch %d; missing %s", id, epoch, ranges.without(actual))
|
.describedAs("node%s does not have all expected sync ranges for epoch %d; missing %s", id, epoch, ranges.without(actual))
|
||||||
|
|
@ -404,7 +404,7 @@ public class EpochSyncTest
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Assertions.assertThat(tm.active().hasEpoch(epoch)).describedAs("node%s does not have epoch %d", id, epoch).isTrue();
|
Assertions.assertThat(tm.active().hasEpoch(epoch)).describedAs("node%s does not have epoch %d", id, epoch).isTrue();
|
||||||
Topology topology = tm.active().globalForEpoch(epoch);
|
Topology topology = tm.active().getKnown(epoch).global();
|
||||||
Ranges ranges = topology.ranges().mergeTouching();
|
Ranges ranges = topology.ranges().mergeTouching();
|
||||||
Ranges actual = tm.unsafeQuorumReady(epoch).mergeTouching();
|
Ranges actual = tm.unsafeQuorumReady(epoch).mergeTouching();
|
||||||
// TopologyManager defines syncComplete for an epoch as (epoch - 1).syncComplete. This means that an epoch has reached quorum, but will still miss ranges as previous epochs have not
|
// TopologyManager defines syncComplete for an epoch as (epoch - 1).syncComplete. This means that an epoch has reached quorum, but will still miss ranges as previous epochs have not
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ public class SimulatedAccordCommandStore implements AutoCloseable
|
||||||
this.topologies = new Topologies.Single(SizeOfIntersectionSorter.SUPPLIER, topology);
|
this.topologies = new Topologies.Single(SizeOfIntersectionSorter.SUPPLIER, topology);
|
||||||
Ranges ranges = topology.ranges();
|
Ranges ranges = topology.ranges();
|
||||||
if (tableId != null)
|
if (tableId != null)
|
||||||
ranges = ranges.slice(Ranges.of(TokenRange.create(TokenKey.min(tableId, getPartitioner()), TokenKey.max(tableId, getPartitioner()))));
|
ranges = ranges.overlapping(Ranges.of(TokenRange.create(TokenKey.min(tableId, getPartitioner()), TokenKey.max(tableId, getPartitioner()))));
|
||||||
CommandStores.RangesForEpoch rangesForEpoch = new CommandStores.RangesForEpoch(topology.epoch(), ranges);
|
CommandStores.RangesForEpoch rangesForEpoch = new CommandStores.RangesForEpoch(topology.epoch(), ranges);
|
||||||
updateHolder.add(topology.epoch(), rangesForEpoch, ranges);
|
updateHolder.add(topology.epoch(), rangesForEpoch, ranges);
|
||||||
|
|
||||||
|
|
@ -258,10 +258,10 @@ public class SimulatedAccordCommandStore implements AutoCloseable
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUncaughtException(Throwable t)
|
public void onException(Throwable t)
|
||||||
{
|
{
|
||||||
if (ignoreExceptions.test(t)) return;
|
if (ignoreExceptions.test(t)) return;
|
||||||
super.onUncaughtException(t);
|
super.onException(t);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -662,8 +662,8 @@ public class CommandsForKeySerializerTest
|
||||||
@Override public void shutdown() { }
|
@Override public void shutdown() { }
|
||||||
@Override public <T> AsyncChain<T> chain(Callable<T> call) { throw new UnsupportedOperationException(); }
|
@Override public <T> AsyncChain<T> chain(Callable<T> call) { throw new UnsupportedOperationException(); }
|
||||||
@Override public OwnershipEventListener ownershipEvents() { return null; }
|
@Override public OwnershipEventListener ownershipEvents() { return null; }
|
||||||
@Override public void onUncaughtException(Throwable t) { throw new UnsupportedOperationException(); }
|
@Override public void onException(Throwable t) { throw new UnsupportedOperationException(); }
|
||||||
@Override public void onCaughtException(Throwable t, String context) { throw new UnsupportedOperationException(); }
|
@Override public void onException(Throwable t, String context) { throw new UnsupportedOperationException(); }
|
||||||
@Override public boolean rejectPreAccept(TimeService time, TxnId txnId) { throw new UnsupportedOperationException(); }
|
@Override public boolean rejectPreAccept(TimeService time, TxnId txnId) { throw new UnsupportedOperationException(); }
|
||||||
@Override public long cfkHlcPruneDelta() { return 0; }
|
@Override public long cfkHlcPruneDelta() { return 0; }
|
||||||
@Override public int cfkPruneInterval() { return 0; }
|
@Override public int cfkPruneInterval() { return 0; }
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ public class KeySerializersTest
|
||||||
int count = superset.isEmpty() ? 0 : rs.nextInt(superset.size());
|
int count = superset.isEmpty() ? 0 : rs.nextInt(superset.size());
|
||||||
Participants<?> subset = selectSubset(rs, count, superset);
|
Participants<?> subset = selectSubset(rs, count, superset);
|
||||||
if (superset instanceof Route<?> && (!changeType || rs.nextBoolean()))
|
if (superset instanceof Route<?> && (!changeType || rs.nextBoolean()))
|
||||||
return superset.intersecting(subset);
|
return superset.overlapping(subset);
|
||||||
return subset;
|
return subset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ public class TxnUpdateTest
|
||||||
}
|
}
|
||||||
|
|
||||||
// slice the same key should return the same block
|
// slice the same key should return the same block
|
||||||
TxnUpdate noUpdate = update.getTxnUpdate(k -> k.intersecting(update.keys()));
|
TxnUpdate noUpdate = update.getTxnUpdate(k -> k.overlapping(update.keys()));
|
||||||
for (int i = 0; i < update.blocks.size(); i++)
|
for (int i = 0; i < update.blocks.size(); i++)
|
||||||
assertThat(noUpdate.blocks.get(i)).isSameAs(update.blocks.get(i));
|
assertThat(noUpdate.blocks.get(i)).isSameAs(update.blocks.get(i));
|
||||||
|
|
||||||
|
|
@ -131,7 +131,7 @@ public class TxnUpdateTest
|
||||||
int keyIndex = rs.nextInt(0, update.keys().size());
|
int keyIndex = rs.nextInt(0, update.keys().size());
|
||||||
Key key = update.keys().get(keyIndex);
|
Key key = update.keys().get(keyIndex);
|
||||||
Keys singleKey = Keys.of(key);
|
Keys singleKey = Keys.of(key);
|
||||||
TxnUpdate singleKeyUpdate = update.getTxnUpdate(k -> k.intersecting(singleKey));
|
TxnUpdate singleKeyUpdate = update.getTxnUpdate(k -> k.overlapping(singleKey));
|
||||||
for (int i = 0; i < update.blocks.size(); i++)
|
for (int i = 0; i < update.blocks.size(); i++)
|
||||||
{
|
{
|
||||||
var block = singleKeyUpdate.blocks.get(i);
|
var block = singleKeyUpdate.blocks.get(i);
|
||||||
|
|
@ -162,7 +162,7 @@ public class TxnUpdateTest
|
||||||
for (int i = 0; i < update.keys().size(); i++)
|
for (int i = 0; i < update.keys().size(); i++)
|
||||||
{
|
{
|
||||||
int finalI = i;
|
int finalI = i;
|
||||||
perKeyUpdate.add(update.getTxnUpdate(k -> k.intersecting(Keys.of(update.keys().get(finalI)))));
|
perKeyUpdate.add(update.getTxnUpdate(k -> k.overlapping(Keys.of(update.keys().get(finalI)))));
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThat(update.merge(update)).isEqualTo(update); // merge with self produces self
|
assertThat(update.merge(update)).isEqualTo(update); // merge with self produces self
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ public class PaxosStateTrackerTest
|
||||||
{
|
{
|
||||||
SchemaLoader.prepareServer();
|
SchemaLoader.prepareServer();
|
||||||
DatabaseDescriptor.setAccordTransactionsEnabled(false);
|
DatabaseDescriptor.setAccordTransactionsEnabled(false);
|
||||||
AccordService.startup(null);
|
AccordService.localStartup(null);
|
||||||
|
|
||||||
ks = "coordinatorsessiontest";
|
ks = "coordinatorsessiontest";
|
||||||
cfm1 = TableMetadata.builder(ks, "tbl1").addPartitionKeyColumn("k", Int32Type.instance).addRegularColumn("v", Int32Type.instance).build();
|
cfm1 = TableMetadata.builder(ks, "tbl1").addPartitionKeyColumn("k", Int32Type.instance).addRegularColumn("v", Int32Type.instance).build();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue