CEP-14: Paxos Improvements

This work encompasses a range of improvements to Paxos, summarised as:

 - The introduction of Paxos Repair for ensuring consistency during range movements
 - The reduction of network round-trips by a factor of 2x for reads and writes

patch by Benedict Elliott Smith and Blake Eggleston; reviewed by Alex Petrov, Aleksey Yeschenko and Sam Tunnicliffe for CASSANDRA-17164
This commit is contained in:
Benedict Elliott Smith 2020-03-30 15:24:12 +01:00 committed by Benedict Elliott Smith
parent 8960b4d851
commit d2923275e3
379 changed files with 27090 additions and 2728 deletions

View File

@ -74,6 +74,7 @@
<exclude name="**/tools/cqlstress-lwt-example.yaml"/>
<!-- Documentation files -->
<exclude NAME="**/doc/modules/**/*"/>
<exclude NAME="**/src/java/**/Paxos.md"/>
<!-- NOTICE files -->
<exclude NAME="**/NOTICE.md"/>
<!-- LICENSE files -->

View File

@ -39,7 +39,7 @@ jobs:
echo "***java tests***"
# get all of our unit test filenames
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" > /tmp/all_java_unit_tests.txt
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" "$HOME/cassandra/test/simulator/test/**/*.java" > /tmp/all_java_unit_tests.txt
# split up the unit tests into groups based on the number of containers we have
set -eo pipefail && circleci tests split --split-by=timings --timings-type=filename --index=${CIRCLE_NODE_INDEX} --total=${CIRCLE_NODE_TOTAL} /tmp/all_java_unit_tests.txt > /tmp/java_tests_${CIRCLE_NODE_INDEX}.txt
@ -1723,7 +1723,7 @@ jobs:
echo "***java tests***"
# get all of our unit test filenames
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" > /tmp/all_java_unit_tests.txt
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" "$HOME/cassandra/test/simulator/test/**/*.java" > /tmp/all_java_unit_tests.txt
# split up the unit tests into groups based on the number of containers we have
set -eo pipefail && circleci tests split --split-by=timings --timings-type=filename --index=${CIRCLE_NODE_INDEX} --total=${CIRCLE_NODE_TOTAL} /tmp/all_java_unit_tests.txt > /tmp/java_tests_${CIRCLE_NODE_INDEX}.txt
@ -2335,7 +2335,7 @@ jobs:
echo "***java tests***"
# get all of our unit test filenames
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" > /tmp/all_java_unit_tests.txt
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" "$HOME/cassandra/test/simulator/test/**/*.java" > /tmp/all_java_unit_tests.txt
# split up the unit tests into groups based on the number of containers we have
set -eo pipefail && circleci tests split --split-by=timings --timings-type=filename --index=${CIRCLE_NODE_INDEX} --total=${CIRCLE_NODE_TOTAL} /tmp/all_java_unit_tests.txt > /tmp/java_tests_${CIRCLE_NODE_INDEX}.txt

View File

@ -39,7 +39,7 @@ jobs:
echo "***java tests***"
# get all of our unit test filenames
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" > /tmp/all_java_unit_tests.txt
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" "$HOME/cassandra/test/simulator/test/**/*.java" > /tmp/all_java_unit_tests.txt
# split up the unit tests into groups based on the number of containers we have
set -eo pipefail && circleci tests split --split-by=timings --timings-type=filename --index=${CIRCLE_NODE_INDEX} --total=${CIRCLE_NODE_TOTAL} /tmp/all_java_unit_tests.txt > /tmp/java_tests_${CIRCLE_NODE_INDEX}.txt
@ -1723,7 +1723,7 @@ jobs:
echo "***java tests***"
# get all of our unit test filenames
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" > /tmp/all_java_unit_tests.txt
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" "$HOME/cassandra/test/simulator/test/**/*.java" > /tmp/all_java_unit_tests.txt
# split up the unit tests into groups based on the number of containers we have
set -eo pipefail && circleci tests split --split-by=timings --timings-type=filename --index=${CIRCLE_NODE_INDEX} --total=${CIRCLE_NODE_TOTAL} /tmp/all_java_unit_tests.txt > /tmp/java_tests_${CIRCLE_NODE_INDEX}.txt
@ -2335,7 +2335,7 @@ jobs:
echo "***java tests***"
# get all of our unit test filenames
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" > /tmp/all_java_unit_tests.txt
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" "$HOME/cassandra/test/simulator/test/**/*.java" > /tmp/all_java_unit_tests.txt
# split up the unit tests into groups based on the number of containers we have
set -eo pipefail && circleci tests split --split-by=timings --timings-type=filename --index=${CIRCLE_NODE_INDEX} --total=${CIRCLE_NODE_TOTAL} /tmp/all_java_unit_tests.txt > /tmp/java_tests_${CIRCLE_NODE_INDEX}.txt

View File

@ -39,7 +39,7 @@ jobs:
echo "***java tests***"
# get all of our unit test filenames
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" > /tmp/all_java_unit_tests.txt
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" "$HOME/cassandra/test/simulator/test/**/*.java" > /tmp/all_java_unit_tests.txt
# split up the unit tests into groups based on the number of containers we have
set -eo pipefail && circleci tests split --split-by=timings --timings-type=filename --index=${CIRCLE_NODE_INDEX} --total=${CIRCLE_NODE_TOTAL} /tmp/all_java_unit_tests.txt > /tmp/java_tests_${CIRCLE_NODE_INDEX}.txt
@ -1723,7 +1723,7 @@ jobs:
echo "***java tests***"
# get all of our unit test filenames
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" > /tmp/all_java_unit_tests.txt
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" "$HOME/cassandra/test/simulator/test/**/*.java" > /tmp/all_java_unit_tests.txt
# split up the unit tests into groups based on the number of containers we have
set -eo pipefail && circleci tests split --split-by=timings --timings-type=filename --index=${CIRCLE_NODE_INDEX} --total=${CIRCLE_NODE_TOTAL} /tmp/all_java_unit_tests.txt > /tmp/java_tests_${CIRCLE_NODE_INDEX}.txt
@ -2335,7 +2335,7 @@ jobs:
echo "***java tests***"
# get all of our unit test filenames
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" > /tmp/all_java_unit_tests.txt
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" "$HOME/cassandra/test/simulator/test/**/*.java" > /tmp/all_java_unit_tests.txt
# split up the unit tests into groups based on the number of containers we have
set -eo pipefail && circleci tests split --split-by=timings --timings-type=filename --index=${CIRCLE_NODE_INDEX} --total=${CIRCLE_NODE_TOTAL} /tmp/all_java_unit_tests.txt > /tmp/java_tests_${CIRCLE_NODE_INDEX}.txt

View File

@ -39,7 +39,7 @@ jobs:
echo "***java tests***"
# get all of our unit test filenames
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" > /tmp/all_java_unit_tests.txt
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" "$HOME/cassandra/test/simulator/test/**/*.java" > /tmp/all_java_unit_tests.txt
# split up the unit tests into groups based on the number of containers we have
set -eo pipefail && circleci tests split --split-by=timings --timings-type=filename --index=${CIRCLE_NODE_INDEX} --total=${CIRCLE_NODE_TOTAL} /tmp/all_java_unit_tests.txt > /tmp/java_tests_${CIRCLE_NODE_INDEX}.txt
@ -1723,7 +1723,7 @@ jobs:
echo "***java tests***"
# get all of our unit test filenames
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" > /tmp/all_java_unit_tests.txt
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" "$HOME/cassandra/test/simulator/test/**/*.java" > /tmp/all_java_unit_tests.txt
# split up the unit tests into groups based on the number of containers we have
set -eo pipefail && circleci tests split --split-by=timings --timings-type=filename --index=${CIRCLE_NODE_INDEX} --total=${CIRCLE_NODE_TOTAL} /tmp/all_java_unit_tests.txt > /tmp/java_tests_${CIRCLE_NODE_INDEX}.txt
@ -2335,7 +2335,7 @@ jobs:
echo "***java tests***"
# get all of our unit test filenames
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" > /tmp/all_java_unit_tests.txt
set -eo pipefail && circleci tests glob "$HOME/cassandra/test/distributed/**/*.java" "$HOME/cassandra/test/simulator/test/**/*.java" > /tmp/all_java_unit_tests.txt
# split up the unit tests into groups based on the number of containers we have
set -eo pipefail && circleci tests split --split-by=timings --timings-type=filename --index=${CIRCLE_NODE_INDEX} --total=${CIRCLE_NODE_TOTAL} /tmp/all_java_unit_tests.txt > /tmp/java_tests_${CIRCLE_NODE_INDEX}.txt

View File

@ -74,6 +74,18 @@ New features
- A new ALL TABLES IN KEYSPACE resource has been added. It allows to grant permissions for all tables and user types
in a keyspace while preventing the user to use those permissions on the keyspace itself.
- Added support for type casting in the WHERE clause components and in the values of INSERT and UPDATE statements.
- A new implementation of Paxos (named v2) has been included that improves the safety and performance of LWT operations.
Importantly, v2 guarantees linearizability across safe range movements, so users are encouraged to enable v2.
v2 also halves the number of WAN messages required to be exchanged if used on conjunction with the new Paxos Repair
mechanism (see below) and with some minor modifications to applications using LWTs.
The new implementation may be enabled at any time by setting paxos_variant: v2, and disabled by setting to v1,
and this alone will reduce the number of WAN round-trips by between one and two for reads, and one for writes.
- A new Paxos Repair mechanism has been introduced as part of Repair, that permits further reducing the number of WAN
round-trips for write LWTs. This process may be manually executed for v1 and is run automatically alongside normal
repairs for v2. Once users are running regular repairs that include paxos repairs they are encouraged to set
paxos_state_purging: repaired. Once this has been set across the cluster, users are encouraged to set their
applications to supply a Commit consistency level of ANY with their LWT write operations, saving one additional WAN
round-trip. See upgrade notes below.
- Warn/abort thresholds added to read queries notifying clients when these thresholds trigger (by
emitting a client warning or aborting the query). This feature is disabled by default, scheduled
to be enabled in 4.2; it is controlled with the configuration track_warnings.enabled,
@ -122,6 +134,18 @@ Upgrading
what was actually applied in previous versions. This also affects the setters and getters for these properties in
the JMX MBean `org.apache.cassandra.db:type=StorageService` and the nodetool commands `set/getstreamthroughput`
and `set/getinterdcstreamthroughput`.
- Steps for upgrading Paxos
- Set paxos_variant: v2 across the cluster. This may be set via JMX, but should also be written
persistently to any yaml.
- Ensure paxos repairs are running regularly, either as part of normal incremental repair workflows or on their
own separate schedule. These operations are cheap and better to run frequently (e.g. once per hour)
- Set paxos_state_purging: repaired across the cluster. This may be set via JMX, but should also be written
persistently to any yaml. NOTE: once this has been set, you must not restore paxos_state_purging: legacy. If
this setting must be disabled you must instead set paxos_state_purging: gc_grace. This may be necessary if
paxos repairs must be disabled for some reason on an extended basis, but in this case your applications must
restore default commit consistency to ensure correctness.
- Applications may now safely be updated to use ANY commit consistency level (or LOCAL_QUORUM, as preferred).
Uncontended writes should now take 2 round-trips, and uncontended reads should typically take one round-trip.
Deprecation
-----------

View File

@ -66,6 +66,7 @@
<property name="test.simulator.src" value="${test.dir}/simulator/main"/>
<property name="test.simulator-asm.src" value="${test.dir}/simulator/asm"/>
<property name="test.simulator-bootstrap.src" value="${test.dir}/simulator/bootstrap"/>
<property name="test.simulator-test.src" value="${test.dir}/simulator/test"/>
<property name="test.driver.connection_timeout_ms" value="5000"/>
<property name="test.driver.read_timeout_ms" value="12000"/>
<property name="dist.dir" value="${build.dir}/dist"/>
@ -1070,6 +1071,7 @@
<include name="**/asm-*${asm.version}.jar" />
</fileset>
</classpath>
<compilerarg value="-XDignore.symbol.file"/>
</javac>
</target>
@ -1154,10 +1156,10 @@
<attribute name="Can-Retransform-Classes" value="true"/>
</manifest>
<mkdir dir="${simulator-asm.build.classes}/META-INF" />
<mkdir dir="${build.dir}/test/lib/" />
<jar destfile="${build.dir}/test/lib/simulator-asm.jar" manifest="${simulator-asm.manifest}">
<mkdir dir="${test.lib}/jars/" />
<jar destfile="${test.lib}/jars/simulator-asm.jar" manifest="${simulator-asm.manifest}">
<fileset dir="${simulator-asm.build.classes}"/>
<fileset dir="${test.lib}">
<fileset dir="${test.lib}/jars">
<include name="**/asm-*${asm.version}.jar" />
</fileset>
</jar>
@ -1167,10 +1169,10 @@
<attribute name="Built-By" value="Benedict Elliott Smith"/>
</manifest>
<mkdir dir="${simulator-bootstrap.build.classes}/META-INF" />
<mkdir dir="${build.dir}/test/lib/" />
<jar destfile="${build.dir}/test/lib/simulator-bootstrap.jar" manifest="${simulator-bootstrap.manifest}">
<mkdir dir="${test.lib}/jars/" />
<jar destfile="${test.lib}/jars/simulator-bootstrap.jar" manifest="${simulator-bootstrap.manifest}">
<fileset dir="${simulator-bootstrap.build.classes}"/>
<fileset dir="${test.lib}">
<fileset dir="${test.lib}/jars">
<include name="**/asm-*${asm.version}.jar" />
</fileset>
</jar>
@ -1367,7 +1369,7 @@
</jar>
</target>
<target name="build-test" depends="_main-jar,stress-build,fqltool-build,resolver-dist-lib" unless="no-build-test"
<target name="build-test" depends="_main-jar,stress-build,fqltool-build,resolver-dist-lib,simulator-jars" unless="no-build-test"
description="Compile test classes">
<antcall target="_build-test"/>
</target>
@ -1397,6 +1399,7 @@
<src path="${test.simulator.src}"/>
<src path="${test.simulator-asm.src}"/>
<src path="${test.simulator-bootstrap.src}"/>
<src path="${test.simulator-test.src}"/>
</javac>
<!-- Non-java resources needed by the test suite -->
@ -1504,6 +1507,8 @@
<pathelement location="${stress.test.classes}"/>
<pathelement location="${fqltool.test.classes}"/>
<pathelement location="${test.conf}"/>
<pathelement path="${java.class.path}"/>
<path refid="cassandra.classpath" />
<fileset dir="${test.lib}">
<include name="**/*.jar" />
<exclude name="**/ant-*.jar"/>
@ -1960,23 +1965,36 @@
</target>
<target name="dtest-jar" depends="build-test, build" description="Create dtest-compatible jar, including all dependencies">
<jar jarfile="${build.dir}/dtest-${base.version}.jar">
<zipgroupfileset dir="${build.lib}" includes="*.jar" excludes="META-INF/*.SF"/>
<zipgroupfileset dir="${build.dir.lib}/jars" includes="javassist-*.jar,reflections-*.jar" excludes="META-INF/*.SF"/>
<zipgroupfileset dir="${build.dir.lib}/jars" includes="jimfs-1.1.jar" excludes="META-INF/*.SF"/>
<jar jarfile="${build.dir}/dtest-${base.version}.jar" duplicate="preserve">
<fileset dir="${build.classes.main}"/>
<fileset dir="${test.classes}"/>
<fileset dir="${test.conf}" />
<zipgroupfileset dir="${build.lib}" includes="*.jar" excludes="META-INF/*.SF"/>
<zipgroupfileset dir="${test.lib}/jars" includes="jimfs-1.1.jar,dtest-api-*.jar,asm-*.jar,javassist-*.jar,reflections-*.jar,semver4j-*.jar" excludes="META-INF/*.SF"/>
<zipgroupfileset dir="${build.dir.lib}/jars" includes="asm-*.jar" excludes="META-INF/*.SF"/>
</jar>
</target>
<target name="test-jvm-dtest" depends="build-test" description="Execute in-jvm dtests">
<testmacro inputdir="${test.distributed.src}" timeout="${test.distributed.timeout}" forkmode="once" showoutput="true" filter="**/test/*Test.java">
<testmacro inputdir="${test.distributed.src}" timeout="${test.distributed.timeout}" forkmode="once" showoutput="true" filter="**/test/${test.name}.java">
<jvmarg value="-Dlogback.configurationFile=test/conf/logback-dtest.xml"/>
<jvmarg value="-Dcassandra.ring_delay_ms=10000"/>
<jvmarg value="-Dcassandra.tolerate_sstable_size=true"/>
<jvmarg value="-Dcassandra.skip_sync=true" />
</testmacro>
<testmacro inputdir="${test.simulator-test.src}" timeout="${test.distributed.timeout}" forkmode="perTest" showoutput="true" filter="**/test/${test.name}.java">
<jvmarg value="-Dlogback.configurationFile=test/conf/logback-simulator.xml"/>
<jvmarg value="-Dcassandra.ring_delay_ms=10000"/>
<jvmarg value="-Dcassandra.tolerate_sstable_size=true"/>
<jvmarg value="-Dcassandra.skip_sync=true" />
<!-- Support Simulator Tests -->
<jvmarg line="-javaagent:${test.lib}/jars/simulator-asm.jar"/>
<jvmarg line="-Xbootclasspath/a:${test.lib}/jars/simulator-bootstrap.jar"/>
<jvmarg line="-XX:ActiveProcessorCount=4"/>
<jvmarg line="-XX:-TieredCompilation"/>
<jvmarg line="-XX:Tier4CompileThreshold=1000"/>
<jvmarg line="-XX:ReservedCodeCacheSize=256M"/>
</testmacro>
</target>
<target name="test-jvm-upgrade-dtest" depends="build-test" description="Execute in-jvm dtests">

View File

@ -30,6 +30,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Iterables;
@ -50,7 +51,6 @@ import org.apache.cassandra.db.Mutation;
import org.apache.cassandra.db.SystemKeyspace;
import org.apache.cassandra.db.WriteType;
import org.apache.cassandra.db.marshal.BytesType;
import org.apache.cassandra.db.marshal.UUIDType;
import org.apache.cassandra.db.partitions.PartitionUpdate;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.exceptions.WriteFailureException;
@ -501,9 +501,9 @@ public class BatchlogManager implements BatchlogManagerMBean
}
}
ReplicaPlan.ForTokenWrite replicaPlan = new ReplicaPlan.ForTokenWrite(keyspace, liveAndDown.replicationStrategy(),
ConsistencyLevel.ONE, liveRemoteOnly.pending(), liveRemoteOnly.all(), liveRemoteOnly.all(), liveRemoteOnly.all());
ReplayWriteResponseHandler<Mutation> handler = new ReplayWriteResponseHandler<>(replicaPlan, nanoTime());
ReplicaPlan.ForWrite replicaPlan = new ReplicaPlan.ForWrite(keyspace, liveAndDown.replicationStrategy(),
ConsistencyLevel.ONE, liveRemoteOnly.pending(), liveRemoteOnly.all(), liveRemoteOnly.all(), liveRemoteOnly.all());
ReplayWriteResponseHandler<Mutation> handler = new ReplayWriteResponseHandler<>(replicaPlan, mutation, nanoTime());
Message<Mutation> message = Message.outWithFlag(MUTATION_REQ, mutation, MessageFlag.CALL_BACK_ON_FAILURE);
for (Replica replica : liveRemoteOnly.all())
MessagingService.instance().sendWriteWithCallback(message, replica, handler, false);
@ -526,9 +526,10 @@ public class BatchlogManager implements BatchlogManagerMBean
{
private final Set<InetAddressAndPort> undelivered = Collections.newSetFromMap(new ConcurrentHashMap<>());
ReplayWriteResponseHandler(ReplicaPlan.ForTokenWrite replicaPlan, long queryStartNanoTime)
// TODO: should we be hinting here, since presumably batch log will retry? Maintaining historical behaviour for the moment.
ReplayWriteResponseHandler(ReplicaPlan.ForWrite replicaPlan, Supplier<Mutation> hintOnFailure, long queryStartNanoTime)
{
super(replicaPlan, null, WriteType.UNLOGGED_BATCH, queryStartNanoTime);
super(replicaPlan, null, WriteType.UNLOGGED_BATCH, hintOnFailure, queryStartNanoTime);
Iterables.addAll(undelivered, replicaPlan.contacts().endpoints());
}

View File

@ -25,6 +25,7 @@ import org.apache.cassandra.utils.JVMStabilityInspector;
import org.apache.cassandra.utils.Shared;
import static java.lang.Thread.*;
import static org.apache.cassandra.concurrent.ExecutorFactory.SimulatorSemantics.NORMAL;
import static org.apache.cassandra.concurrent.InfiniteLoopExecutor.Daemon.DAEMON;
import static org.apache.cassandra.concurrent.InfiniteLoopExecutor.Interrupts.UNSYNCHRONIZED;
import static org.apache.cassandra.concurrent.NamedThreadFactory.createThread;
@ -72,6 +73,11 @@ public interface ExecutorFactory extends ExecutorBuilderFactory.Jmxable<Executor
default LocalAwareSubFactoryWithJMX withJmxInternal() { return withJmx("internal"); }
}
public enum SimulatorSemantics
{
NORMAL, DISCARD
}
/**
* @return a factory that configures executors that propagate {@link ExecutorLocals} to the executing thread
*/
@ -83,6 +89,13 @@ public interface ExecutorFactory extends ExecutorBuilderFactory.Jmxable<Executor
*/
default ScheduledExecutorPlus scheduled(String name) { return scheduled(true, name, NORM_PRIORITY); }
/**
* @param name the name of the executor, the executor's thread group, and of any worker threads
* @param simulatorSemantics indicate special semantics for the executor under simulation
* @return a default-configured {@link ScheduledExecutorPlus}
*/
default ScheduledExecutorPlus scheduled(String name, SimulatorSemantics simulatorSemantics) { return scheduled(true, name, NORM_PRIORITY, simulatorSemantics); }
/**
* @param executeOnShutdown if false, waiting tasks will be cancelled on shutdown
* @param name the name of the executor, the executor's thread group, and of any worker threads
@ -96,7 +109,16 @@ public interface ExecutorFactory extends ExecutorBuilderFactory.Jmxable<Executor
* @param priority the thread priority of workers
* @return a {@link ScheduledExecutorPlus}
*/
ScheduledExecutorPlus scheduled(boolean executeOnShutdown, String name, int priority);
default ScheduledExecutorPlus scheduled(boolean executeOnShutdown, String name, int priority) { return scheduled(executeOnShutdown, name, priority, NORMAL); }
/**
* @param executeOnShutdown if false, waiting tasks will be cancelled on shutdown
* @param name the name of the executor, the executor's thread group, and of any worker threads
* @param priority the thread priority of workers
* @param simulatorSemantics indicate special semantics for the executor under simulation
* @return a {@link ScheduledExecutorPlus}
*/
ScheduledExecutorPlus scheduled(boolean executeOnShutdown, String name, int priority, SimulatorSemantics simulatorSemantics);
/**
* Create and start a new thread to execute {@code runnable}
@ -249,7 +271,7 @@ public interface ExecutorFactory extends ExecutorBuilderFactory.Jmxable<Executor
}
@Override
public ScheduledExecutorPlus scheduled(boolean executeOnShutdown, String name, int priority)
public ScheduledExecutorPlus scheduled(boolean executeOnShutdown, String name, int priority, SimulatorSemantics simulatorSemantics)
{
ScheduledThreadPoolExecutorPlus executor = new ScheduledThreadPoolExecutorPlus(newThreadFactory(name, priority));
if (!executeOnShutdown)

View File

@ -30,9 +30,9 @@ import org.apache.cassandra.utils.concurrent.AsyncFuture;
*/
public class FutureTask<V> extends AsyncFuture<V> implements RunnableFuture<V>
{
private Callable<V> call;
private Callable<? extends V> call;
public FutureTask(Callable<V> call)
public FutureTask(Callable<? extends V> call)
{
this.call = call;
}

View File

@ -29,21 +29,25 @@ import java.util.function.BiFunction;
import java.util.function.Consumer;
import org.apache.cassandra.utils.Shared;
import org.apache.cassandra.utils.concurrent.Condition;
import org.apache.cassandra.utils.concurrent.UncheckedInterruptedException;
import static org.apache.cassandra.concurrent.InfiniteLoopExecutor.InternalState.SHUTTING_DOWN_NOW;
import static org.apache.cassandra.concurrent.InfiniteLoopExecutor.InternalState.TERMINATED;
import static org.apache.cassandra.concurrent.InfiniteLoopExecutor.Interrupts.SYNCHRONIZED;
import static org.apache.cassandra.concurrent.InfiniteLoopExecutor.Interrupts.UNSYNCHRONIZED;
import static org.apache.cassandra.concurrent.Interruptible.State.INTERRUPTED;
import static org.apache.cassandra.concurrent.Interruptible.State.NORMAL;
import static org.apache.cassandra.concurrent.Interruptible.State.SHUTTING_DOWN;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
import static org.apache.cassandra.utils.concurrent.Condition.newOneTimeCondition;
public class InfiniteLoopExecutor implements Interruptible
{
private static final Logger logger = LoggerFactory.getLogger(InfiniteLoopExecutor.class);
@Shared(scope = Shared.Scope.SIMULATION)
public enum InternalState { TERMINATED }
public enum InternalState { SHUTTING_DOWN_NOW, TERMINATED }
@Shared(scope = Shared.Scope.SIMULATION)
public enum SimulatorSafe { SAFE, UNSAFE }
@ -59,6 +63,7 @@ public class InfiniteLoopExecutor implements Interruptible
private final Task task;
private volatile Object state = NORMAL;
private final Consumer<Thread> interruptHandler;
private final Condition isTerminated = newOneTimeCondition();
public InfiniteLoopExecutor(String name, Task task, Daemon daemon)
{
@ -102,33 +107,41 @@ public class InfiniteLoopExecutor implements Interruptible
private void loop()
{
boolean interrupted = false;
while (true)
try
{
try
while (true)
{
Object cur = state;
if (cur == TERMINATED) break;
try
{
Object cur = state;
if (cur == SHUTTING_DOWN_NOW) break;
interrupted |= Thread.interrupted();
if (cur == NORMAL && interrupted) cur = INTERRUPTED;
task.run((State) cur);
interrupted |= Thread.interrupted();
if (cur == NORMAL && interrupted) cur = INTERRUPTED;
task.run((State) cur);
interrupted = false;
if (cur == SHUTTING_DOWN) state = TERMINATED;
}
catch (TerminateException ignore)
{
state = TERMINATED;
}
catch (UncheckedInterruptedException | InterruptedException ignore)
{
interrupted = true;
}
catch (Throwable t)
{
logger.error("Exception thrown by runnable, continuing with loop", t);
interrupted = false;
if (cur == SHUTTING_DOWN) break;
}
catch (TerminateException ignore)
{
break;
}
catch (UncheckedInterruptedException | InterruptedException ignore)
{
interrupted = true;
}
catch (Throwable t)
{
logger.error("Exception thrown by runnable, continuing with loop", t);
}
}
}
finally
{
state = TERMINATED;
isTerminated.signal();
}
}
public void interrupt()
@ -138,13 +151,13 @@ public class InfiniteLoopExecutor implements Interruptible
public void shutdown()
{
stateUpdater.updateAndGet(this, cur -> cur != TERMINATED ? SHUTTING_DOWN : TERMINATED);
stateUpdater.updateAndGet(this, cur -> cur != TERMINATED && cur != SHUTTING_DOWN_NOW ? SHUTTING_DOWN : cur);
interruptHandler.accept(thread);
}
public Object shutdownNow()
{
state = TERMINATED;
stateUpdater.updateAndGet(this, cur -> cur != TERMINATED ? SHUTTING_DOWN_NOW : TERMINATED);
interruptHandler.accept(thread);
return null;
}
@ -152,12 +165,16 @@ public class InfiniteLoopExecutor implements Interruptible
@Override
public boolean isTerminated()
{
return state == TERMINATED && !thread.isAlive();
return state == TERMINATED;
}
public boolean awaitTermination(long time, TimeUnit unit) throws InterruptedException
{
thread.join(unit.toMillis(time));
if (isTerminated())
return true;
long deadlineNanos = nanoTime() + unit.toNanos(time);
isTerminated.awaitUntil(deadlineNanos);
return isTerminated();
}

View File

@ -53,16 +53,18 @@ public enum Stage
MISC ("MiscStage", "internal", () -> 1, null, Stage::singleThreadedStage),
TRACING ("TracingStage", "internal", () -> 1, null, Stage::tracingStage),
INTERNAL_RESPONSE ("InternalResponseStage", "internal", FBUtilities::getAvailableProcessors, null, Stage::multiThreadedStage),
IMMEDIATE ("ImmediateStage", "internal", () -> 0, null, Stage::immediateExecutor);
IMMEDIATE ("ImmediateStage", "internal", () -> 0, null, Stage::immediateExecutor),
PAXOS_REPAIR ("PaxosRepairStage", "internal", FBUtilities::getAvailableProcessors, null, Stage::multiThreadedStage),
;
public final String jmxName;
private final Supplier<ExecutorPlus> initialiser;
private volatile ExecutorPlus executor = null;
private final Supplier<ExecutorPlus> executorSupplier;
private volatile ExecutorPlus executor;
Stage(String jmxName, String jmxType, IntSupplier numThreads, LocalAwareExecutorPlus.MaximumPoolSizeListener onSetMaximumPoolSize, ExecutorServiceInitialiser initialiser)
Stage(String jmxName, String jmxType, IntSupplier numThreads, LocalAwareExecutorPlus.MaximumPoolSizeListener onSetMaximumPoolSize, ExecutorServiceInitialiser executorSupplier)
{
this.jmxName = jmxName;
this.initialiser = () -> initialiser.init(jmxName, jmxType, numThreads.getAsInt(), onSetMaximumPoolSize);
this.executorSupplier = () -> executorSupplier.init(jmxName, jmxType, numThreads.getAsInt(), onSetMaximumPoolSize);
}
private static String normalizeName(String stageName)
@ -128,7 +130,7 @@ public enum Stage
{
if (executor == null)
{
executor = initialiser.get();
executor = executorSupplier.get();
}
}
}

View File

@ -71,8 +71,7 @@ public class SyncFutureTask<T> extends SyncFuture<T> implements RunnableFuture<T
catch (Throwable t)
{
tryFailure(t);
Thread thread = Thread.currentThread();
thread.getUncaughtExceptionHandler().uncaughtException(thread, t);
ExecutionFailure.handle(t);
}
}

View File

@ -244,7 +244,9 @@ public enum CassandraRelevantProperties
TEST_SIMULATOR_PRINT_ASM("cassandra.test.simulator.print_asm", "none"),
TEST_SIMULATOR_PRINT_ASM_TYPES("cassandra.test.simulator.print_asm_types", ""),
TEST_SIMULATOR_LIVENESS_CHECK("cassandra.test.simulator.livenesscheck", "true"),
TEST_SIMULATOR_DEBUG("cassandra.test.simulator.debug", "true"),
TEST_SIMULATOR_DEBUG("cassandra.test.simulator.debug", "false"),
TEST_SIMULATOR_DETERMINISM_CHECK("cassandra.test.simulator.determinismcheck", "none"),
TEST_JVM_DTEST_DISABLE_SSL("cassandra.test.disable_ssl", "false"),
// determinism properties for testing
DETERMINISM_SSTABLE_COMPRESSION_DEFAULT("cassandra.sstable_compression_default", "true"),
@ -320,6 +322,15 @@ public enum CassandraRelevantProperties
return STRING_CONVERTER.convert(value);
}
public <T> T convert(PropertyConverter<T> converter)
{
String value = System.getProperty(key);
if (value == null)
value = defaultVal;
return converter.convert(value);
}
/**
* Sets the value into system properties.
* @param value to set
@ -415,7 +426,7 @@ public enum CassandraRelevantProperties
System.setProperty(key, Long.toString(value));
}
private interface PropertyConverter<T>
public interface PropertyConverter<T>
{
T convert(String value);
}

View File

@ -29,6 +29,7 @@ import java.util.TreeMap;
import java.util.function.Supplier;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import org.slf4j.Logger;
@ -48,6 +49,20 @@ public class Config
{
private static final Logger logger = LoggerFactory.getLogger(Config.class);
public static Set<String> splitCommaDelimited(String src)
{
if (src == null)
return ImmutableSet.of();
String[] split = src.split(",\\s*");
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
for (String s : split)
{
s = s.trim();
if (!s.isEmpty())
builder.add(s);
}
return builder.build();
}
/*
* Prefix for Java properties for internal Cassandra configuration options
*/
@ -125,11 +140,13 @@ public class Config
public volatile SmallestDurationMilliseconds counter_write_request_timeout = new SmallestDurationMilliseconds("5000ms");
@Replaces(oldName = "cas_contention_timeout_in_ms", converter = Converters.MILLIS_DURATION, deprecated = true)
public volatile SmallestDurationMilliseconds cas_contention_timeout = new SmallestDurationMilliseconds("1000ms");
public volatile SmallestDurationMilliseconds cas_contention_timeout = new SmallestDurationMilliseconds("1800ms");
@Replaces(oldName = "truncate_request_timeout_in_ms", converter = Converters.MILLIS_DURATION, deprecated = true)
public volatile SmallestDurationMilliseconds truncate_request_timeout = new SmallestDurationMilliseconds("60000ms");
public volatile Long repair_request_timeout_in_ms = 120000L;
public Integer streaming_connections_per_host = 1;
@Replaces(oldName = "streaming_keep_alive_period_in_secs", converter = Converters.SECONDS_DURATION, deprecated = true)
public SmallestDurationSeconds streaming_keep_alive_period = new SmallestDurationSeconds("300s");
@ -406,6 +423,8 @@ public class Config
public volatile SmallestDurationSeconds counter_cache_save_period = new SmallestDurationSeconds("7200s");
public volatile int counter_cache_keys_to_save = Integer.MAX_VALUE;
public SmallestDataStorageMebibytes paxos_cache_size = null;
@Replaces(oldName = "cache_load_timeout_seconds ", converter = Converters.SECONDS_DURATION, deprecated = true)
public SmallestDurationSeconds cache_load_timeout = new SmallestDurationSeconds("30s");
@ -780,13 +799,190 @@ public class Config
/** The configuration of startup checks. */
public volatile Map<StartupCheckType, Map<String, Object>> startup_checks = new HashMap<>();
/**
* The variants of paxos implementation and semantics supported by Cassandra.
*/
public enum PaxosVariant
{
v1_without_linearizable_reads, // with legacy semantics for read/read linearizability (i.e. not guaranteed)
v1
/**
* v1 Paxos lacks most optimisations. Expect 4RTs for a write and 2RTs for a read.
*
* With legacy semantics for read/read and rejected write linearizability, i.e. not guaranteed.
*/
v1_without_linearizable_reads_or_rejected_writes,
/**
* v1 Paxos lacks most optimisations. Expect 4RTs for a write and 3RTs for a read.
*/
v1,
/**
* v2 Paxos. With PaxosStatePurging.repaired safe to use ANY Commit consistency.
* Expect 2RTs for a write and 1RT for a read.
*
* With legacy semantics for read/read linearizability, i.e. not guaranteed.
*/
v2_without_linearizable_reads,
/**
* v2 Paxos. With PaxosStatePurging.repaired safe to use ANY Commit consistency.
* Expect 2RTs for a write and 1RT for a read.
*
* With legacy semantics for read/read and rejected write linearizability, i.e. not guaranteed.
*/
v2_without_linearizable_reads_or_rejected_writes,
/**
* v2 Paxos. With PaxosStatePurging.repaired safe to use ANY Commit consistency.
* Expect 2RTs for a write, and either 1RT or 2RT for a read.
*/
v2
}
/**
* Select the kind of paxos state purging to use. Migration to repaired is recommended, but requires that
* regular paxos repairs are performed (which by default run as part of incremental repair).
*
* Once migrated from legacy it is unsafe to return to legacy, but gc_grace mode may be used in its place
* and performs a very similar cleanup process.
*
* Should only be modified once paxos_variant = v2.
*/
public enum PaxosStatePurging
{
/**
* system.paxos records are written and garbage collected with TTLs. Unsafe to use with Commit consistency ANY.
* Once migrated from, cannot be migrated back to safely. Must use gc_grace or repaired instead, as TTLs
* will not have been set.
*/
legacy,
/**
* Functionally similar to legacy, but the gc_grace expiry is applied at compaction and read time rather than
* using TTLs, so may be safely enabled at any point.
*/
gc_grace,
/**
* Clears system.paxos records only once they are known to be persisted to a quorum of replica's base tables
* through the use of paxos repair. Requires that regular paxos repairs are performed on the cluster
* (which by default are included in incremental repairs if paxos_variant = v2).
*
* This setting permits the use of Commit consistency ANY or LOCAL_QUORUM without any loss of durability or consistency,
* saving 1 RT.
*/
repaired;
public static PaxosStatePurging fromBoolean(boolean enabled)
{
return enabled ? repaired : gc_grace;
}
}
/**
* See {@link PaxosVariant}. Defaults to v1, recommend upgrading to v2 at earliest opportunity.
*/
public volatile PaxosVariant paxos_variant = PaxosVariant.v1;
/**
* If true, paxos topology change repair will not run on a topology change - this option should only be used in
* rare operation circumstances e.g. where for some reason the repair is impossible to perform (e.g. too few replicas)
* and an unsafe topology change must be made
*/
public volatile boolean skip_paxos_repair_on_topology_change = Boolean.getBoolean("cassandra.skip_paxos_repair_on_topology_change");
/**
* A safety margin when purging paxos state information that has been safely replicated to a quorum.
* Data for transactions initiated within this grace period will not be expunged.
*/
public volatile DurationSpec paxos_purge_grace_period = new SmallestDurationSeconds("60s");
/**
* A safety mechanism for detecting incorrect paxos state, that may be down either to a bug or incorrect usage of LWTs
* (most likely due to unsafe mixing of SERIAL and LOCAL_SERIAL operations), and rejecting
*/
public enum PaxosOnLinearizabilityViolation
{
// reject an operation when a linearizability violation is detected.
// note this does not guarantee a violation has been averted,
// as it may be a prior operation that invalidated the state.
fail,
// log any detected linearizability violation
log,
// ignore any detected linearizability violation
ignore
}
/**
* See {@link PaxosOnLinearizabilityViolation}.
*
* Default is to ignore, as applications may readily mix SERIAL and LOCAL_SERIAL and this is the most likely source
* of linearizability violations. this facility should be activated only for debugging Cassandra or by power users
* who are investigating their own application behaviour.
*/
public volatile PaxosOnLinearizabilityViolation paxos_on_linearizability_violations = PaxosOnLinearizabilityViolation.ignore;
/**
* See {@link PaxosStatePurging} default is legacy.
*/
public volatile PaxosStatePurging paxos_state_purging;
/**
* Enable/disable paxos repair. This is a global flag that not only determines default behaviour but overrides
* explicit paxos repair requests, paxos repair on topology changes and paxos auto repairs.
*/
public volatile boolean paxos_repair_enabled = true;
/**
* If true, paxos topology change repair only requires a global quorum of live nodes. If false,
* it requires a global quorum as well as a local quorum for each dc (EACH_QUORUM), with the
* exception explained in paxos_topology_repair_strict_each_quorum
*/
public boolean paxos_topology_repair_no_dc_checks = false;
/**
* If true, a quorum will be required for the global and local quorum checks. If false, we will
* accept a quorum OR n - 1 live nodes. This is to allow for topologies like 2:2:2, where paxos queries
* always use SERIAL, and a single node down in a dc should not preclude a paxos repair
*/
public boolean paxos_topology_repair_strict_each_quorum = false;
/**
* If necessary for operational purposes, permit certain keyspaces to be ignored for paxos topology repairs
*/
public volatile Set<String> skip_paxos_repair_on_topology_change_keyspaces = splitCommaDelimited(System.getProperty("cassandra.skip_paxos_repair_on_topology_change_keyspaces"));
/**
* See {@link org.apache.cassandra.service.paxos.ContentionStrategy}
*/
public String paxos_contention_wait_randomizer;
/**
* See {@link org.apache.cassandra.service.paxos.ContentionStrategy}
*/
public String paxos_contention_min_wait;
/**
* See {@link org.apache.cassandra.service.paxos.ContentionStrategy}
*/
public String paxos_contention_max_wait;
/**
* See {@link org.apache.cassandra.service.paxos.ContentionStrategy}
*/
public String paxos_contention_min_delta;
/**
* The amount of disk space paxos uncommitted key files can consume before we begin automatically scheduling paxos repairs.
* Note that these repairs are uncoordinated and so do not contribute to expunging system.paxos records.
*/
public volatile int paxos_auto_repair_threshold_mb = 32;
/**
* The number of keys we may simultaneously attempt to finish incomplete paxos operations for.
*/
public volatile int paxos_repair_parallelism = -1;
public static Supplier<Config> getOverrideLoadConfig()
{
return overrideLoadConfig;

View File

@ -88,7 +88,7 @@ public class DataStorageSpec
public DataStorageSpec (String value, DataStorageUnit minUnit)
{
if (value == null || value.equals("null"))
if (value == null || value.equals("null") || value.equals("0"))
{
quantity = 0;
unit = minUnit;

View File

@ -32,6 +32,15 @@ import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import com.google.common.util.concurrent.RateLimiter;
import org.apache.cassandra.config.Config.PaxosOnLinearizabilityViolation;
import org.apache.cassandra.db.ConsistencyLevel;
import org.apache.cassandra.gms.IFailureDetector;
import org.apache.cassandra.io.util.File;
import org.apache.cassandra.config.Config.PaxosStatePurging;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.audit.AuditLogOptions;
import org.apache.cassandra.fql.FullQueryLoggerOptions;
import org.apache.cassandra.auth.AllowAllInternodeAuthenticator;
@ -42,15 +51,12 @@ import org.apache.cassandra.auth.IInternodeAuthenticator;
import org.apache.cassandra.auth.INetworkAuthorizer;
import org.apache.cassandra.auth.IRoleManager;
import org.apache.cassandra.config.Config.CommitLogSync;
import org.apache.cassandra.db.ConsistencyLevel;
import org.apache.cassandra.db.commitlog.AbstractCommitLogSegmentManager;
import org.apache.cassandra.db.commitlog.CommitLog;
import org.apache.cassandra.db.commitlog.CommitLogSegmentManagerCDC;
import org.apache.cassandra.db.commitlog.CommitLogSegmentManagerStandard;
import org.apache.cassandra.dht.IPartitioner;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.gms.IFailureDetector;
import org.apache.cassandra.io.util.File;
import org.apache.cassandra.io.FSWriteError;
import org.apache.cassandra.io.util.DiskOptimizationStrategy;
import org.apache.cassandra.io.util.FileUtils;
@ -81,6 +87,7 @@ import static org.apache.cassandra.config.CassandraRelevantProperties.OS_ARCH;
import static org.apache.cassandra.config.CassandraRelevantProperties.SUN_ARCH_DATA_MODEL;
import static org.apache.cassandra.io.util.FileUtils.ONE_GIB;
import static org.apache.cassandra.io.util.FileUtils.ONE_MIB;
import static org.apache.cassandra.config.CassandraRelevantProperties.TEST_JVM_DTEST_DISABLE_SSL;
import static org.apache.cassandra.utils.Clock.Global.logInitializationOutcome;
public class DatabaseDescriptor
@ -132,6 +139,7 @@ public class DatabaseDescriptor
private static long preparedStatementsCacheSizeInMiB;
private static long keyCacheSizeInMiB;
private static long paxosCacheSizeInMiB;
private static long counterCacheSizeInMiB;
private static long indexSummaryCapacityInMiB;
@ -730,6 +738,28 @@ public class DatabaseDescriptor
+ (conf.counter_cache_size !=null ?conf.counter_cache_size.toString() : null) + "', supported values are <integer> >= 0.", false);
}
try
{
// if paxosCacheSizeInMiB option was set to "auto" then size of the cache should be "min(1% of Heap (in MB), 50MB)
paxosCacheSizeInMiB = (conf.paxos_cache_size == null)
? Math.min(Math.max(1, (int) (Runtime.getRuntime().totalMemory() * 0.01 / 1024 / 1024)), 50)
: conf.paxos_cache_size.toMebibytes();
if (paxosCacheSizeInMiB < 0)
throw new NumberFormatException(); // to escape duplicating error message
}
catch (NumberFormatException e)
{
throw new ConfigurationException("paxos_cache_size option was set incorrectly to '"
+ conf.paxos_cache_size + "', supported values are <integer> >= 0.", false);
}
if (conf.paxos_auto_repair_threshold_mb < 0)
{
throw new ConfigurationException("paxos_auto_repair_threshold_mb option was set incorrectly to '"
+ conf.paxos_auto_repair_threshold_mb + "', supported values are <integer> >= 0.", false);
}
// if set to empty/"auto" then use 5% of Heap size
indexSummaryCapacityInMiB = (conf.index_summary_capacity == null)
? Math.max(1, (int) (Runtime.getRuntime().totalMemory() * 0.05 / 1024 / 1024))
@ -844,18 +874,12 @@ public class DatabaseDescriptor
conf.default_keyspace_rf, conf.minimum_keyspace_rf));
}
if (conf.paxos_variant == Config.PaxosVariant.v1_without_linearizable_reads)
{
logger.warn("This node was started with paxos_variant config option set to v1_norrl. " +
"SERIAL (and LOCAL_SERIAL) reads coordinated by this node " +
"will not offer linearizability (see CASSANDRA-12126 for details on what this mean) with " +
"respect to other SERIAL operations. Please note that, with this option, SERIAL reads will be " +
"slower than QUORUM reads, yet offer no more guarantee. This flag should only be used in " +
"the restricted case of upgrading from a pre-CASSANDRA-12126 version, and only if you " +
"understand the tradeoff.");
}
if (conf.paxos_repair_parallelism <= 0)
conf.paxos_repair_parallelism = Math.max(1, conf.concurrent_writes / 8);
Paxos.setPaxosVariant(conf.paxos_variant);
if (conf.paxos_state_purging == null)
conf.paxos_state_purging = PaxosStatePurging.legacy;
logInitializationOutcome(logger);
}
@ -1053,6 +1077,9 @@ public class DatabaseDescriptor
public static void applySslContext()
{
if (TEST_JVM_DTEST_DISABLE_SSL.getBoolean())
return;
try
{
SSLFactory.validateSslContext("Internode messaging", conf.server_encryption_options, true, true);
@ -1756,6 +1783,16 @@ public class DatabaseDescriptor
conf.truncate_request_timeout = SmallestDurationMilliseconds.inMilliseconds(timeOutInMillis);
}
public static long getRepairRpcTimeout()
{
return conf.repair_request_timeout_in_ms;
}
public static void setRepairRpcTimeout(Long timeOutInMillis)
{
conf.repair_request_timeout_in_ms = timeOutInMillis;
}
public static boolean hasCrossNodeTimeout()
{
return conf.internode_timeout;
@ -2462,6 +2499,126 @@ public class DatabaseDescriptor
conf.paxos_variant = variant;
}
public static String getPaxosContentionWaitRandomizer()
{
return conf.paxos_contention_wait_randomizer;
}
public static String getPaxosContentionMinWait()
{
return conf.paxos_contention_min_wait;
}
public static String getPaxosContentionMaxWait()
{
return conf.paxos_contention_max_wait;
}
public static String getPaxosContentionMinDelta()
{
return conf.paxos_contention_min_delta;
}
public static void setPaxosContentionWaitRandomizer(String waitRandomizer)
{
conf.paxos_contention_wait_randomizer = waitRandomizer;
}
public static void setPaxosContentionMinWait(String minWait)
{
conf.paxos_contention_min_wait = minWait;
}
public static void setPaxosContentionMaxWait(String maxWait)
{
conf.paxos_contention_max_wait = maxWait;
}
public static void setPaxosContentionMinDelta(String minDelta)
{
conf.paxos_contention_min_delta = minDelta;
}
public static boolean skipPaxosRepairOnTopologyChange()
{
return conf.skip_paxos_repair_on_topology_change;
}
public static void setSkipPaxosRepairOnTopologyChange(boolean value)
{
conf.skip_paxos_repair_on_topology_change = value;
}
public static long getPaxosPurgeGrace(TimeUnit units)
{
return conf.paxos_purge_grace_period.to(units);
}
public static void setPaxosPurgeGrace(long value, TimeUnit units)
{
conf.paxos_purge_grace_period = new DurationSpec(value, units);
}
public static PaxosOnLinearizabilityViolation paxosOnLinearizabilityViolations()
{
return conf.paxos_on_linearizability_violations;
}
public static void setPaxosOnLinearizabilityViolations(PaxosOnLinearizabilityViolation v)
{
conf.paxos_on_linearizability_violations = v;
}
public static PaxosStatePurging paxosStatePurging()
{
return conf.paxos_state_purging;
}
public static void setPaxosStatePurging(PaxosStatePurging v)
{
conf.paxos_state_purging = v;
}
public static boolean paxosRepairEnabled()
{
return conf.paxos_repair_enabled;
}
public static void setPaxosRepairEnabled(boolean v)
{
conf.paxos_repair_enabled = v;
}
public static Set<String> skipPaxosRepairOnTopologyChangeKeyspaces()
{
return conf.skip_paxos_repair_on_topology_change_keyspaces;
}
public static void setSkipPaxosRepairOnTopologyChangeKeyspaces(String keyspaces)
{
conf.skip_paxos_repair_on_topology_change_keyspaces = Config.splitCommaDelimited(keyspaces);
}
public static boolean paxoTopologyRepairNoDcChecks()
{
return conf.paxos_topology_repair_no_dc_checks;
}
public static boolean paxoTopologyRepairStrictEachQuorum()
{
return conf.paxos_topology_repair_strict_each_quorum;
}
public static int getPaxosAutoRepairThresholdMB()
{
return conf.paxos_auto_repair_threshold_mb;
}
public static void setPaxosAutoRepairThresholdMB(int threshold)
{
conf.paxos_auto_repair_threshold_mb = threshold;
}
public static void setNativeTransportMaxConcurrentRequestsInBytesPerIp(long maxConcurrentRequestsInBytes)
{
conf.native_transport_max_concurrent_requests_in_bytes_per_ip = maxConcurrentRequestsInBytes;
@ -2952,6 +3109,11 @@ public class DatabaseDescriptor
return conf.row_cache_keys_to_save;
}
public static long getPaxosCacheSizeInMiB()
{
return paxosCacheSizeInMiB;
}
public static long getCounterCacheSizeInMiB()
{
return counterCacheSizeInMiB;
@ -3081,6 +3243,17 @@ public class DatabaseDescriptor
conf.repair_session_space = SmallestDataStorageMebibytes.inMebibytes(sizeInMiB);
}
public static int getPaxosRepairParallelism()
{
return conf.paxos_repair_parallelism;
}
public static void setPaxosRepairParallelism(int v)
{
Preconditions.checkArgument(v > 0);
conf.paxos_repair_parallelism = v;
}
public static Float getMemtableCleanupThreshold()
{
return conf.memtable_cleanup_threshold;

View File

@ -64,7 +64,7 @@ public class DurationSpec
public DurationSpec(String value)
{
if (value == null || value.equals("null") || value.toLowerCase(Locale.ROOT).equals("nan"))
if (value == null || value.equals("null") || value.toLowerCase(Locale.ROOT).equals("nan") || value.equals("0"))
{
quantity = 0;
unit = MILLISECONDS;
@ -74,7 +74,7 @@ public class DurationSpec
//parse the string field value
Matcher matcher = TIME_UNITS_PATTERN.matcher(value);
if(matcher.find())
if (matcher.find())
{
quantity = Long.parseLong(matcher.group(1));
unit = fromSymbol(matcher.group(2));

View File

@ -25,6 +25,10 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManagerFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
@ -33,6 +37,8 @@ import org.slf4j.LoggerFactory;
import org.apache.cassandra.locator.IEndpointSnitch;
import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.security.AbstractSslContextFactory;
import org.apache.cassandra.security.DisableSslContextFactory;
import org.apache.cassandra.security.ISslContextFactory;
import org.apache.cassandra.utils.FBUtilities;
@ -277,8 +283,15 @@ public class EncryptionOptions
putSslContextFactoryParameter(sslContextFactoryParameters, ConfigKey.ENABLED, this.enabled);
putSslContextFactoryParameter(sslContextFactoryParameters, ConfigKey.OPTIONAL, this.optional);
sslContextFactoryInstance = FBUtilities.newSslContextFactory(ssl_context_factory.class_name,
sslContextFactoryParameters);
if (CassandraRelevantProperties.TEST_JVM_DTEST_DISABLE_SSL.getBoolean())
{
sslContextFactoryInstance = new DisableSslContextFactory();
}
else
{
sslContextFactoryInstance = FBUtilities.newSslContextFactory(ssl_context_factory.class_name,
sslContextFactoryParameters);
}
}
private void putSslContextFactoryParameter(Map<String,Object> existingParameters, ConfigKey configKey,

View File

@ -60,6 +60,11 @@ public abstract class QueryOptions
return new DefaultQueryOptions(consistency, values, false, SpecificOptions.DEFAULT, ProtocolVersion.V3);
}
public static QueryOptions forInternalCallsWithNowInSec(int nowInSec, ConsistencyLevel consistency, List<ByteBuffer> values)
{
return new DefaultQueryOptions(consistency, values, false, SpecificOptions.DEFAULT.withNowInSec(nowInSec), ProtocolVersion.CURRENT);
}
public static QueryOptions forInternalCalls(List<ByteBuffer> values)
{
return new DefaultQueryOptions(ConsistencyLevel.ONE, values, false, SpecificOptions.DEFAULT, ProtocolVersion.V3);
@ -209,6 +214,12 @@ public abstract class QueryOptions
/** The keyspace that this query is bound to, or null if not relevant. */
public String getKeyspace() { return getSpecificOptions().keyspace; }
public int getNowInSec(int ifNotSet)
{
int nowInSec = getSpecificOptions().nowInSeconds;
return nowInSec != Integer.MIN_VALUE ? nowInSec : ifNotSet;
}
/**
* The protocol version for the query.
*/
@ -506,6 +517,11 @@ public abstract class QueryOptions
this.keyspace = keyspace;
this.nowInSeconds = nowInSeconds;
}
public SpecificOptions withNowInSec(int nowInSec)
{
return new SpecificOptions(pageSize, state, serialConsistency, timestamp, keyspace, nowInSec);
}
}
private static class Codec implements CBCodec<QueryOptions>

View File

@ -52,6 +52,7 @@ import org.apache.cassandra.cql3.functions.Function;
import org.apache.cassandra.cql3.functions.FunctionName;
import org.apache.cassandra.cql3.statements.*;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.rows.Row;
import org.apache.cassandra.db.rows.RowIterator;
import org.apache.cassandra.db.partitions.PartitionIterator;
import org.apache.cassandra.db.partitions.PartitionIterators;
@ -375,6 +376,16 @@ public class QueryProcessor implements QueryHandler
}
private static QueryOptions makeInternalOptions(CQLStatement prepared, Object[] values, ConsistencyLevel cl)
{
return makeInternalOptionsWithNowInSec(prepared, FBUtilities.nowInSeconds(), values, cl);
}
public static QueryOptions makeInternalOptionsWithNowInSec(CQLStatement prepared, int nowInSec, Object[] values)
{
return makeInternalOptionsWithNowInSec(prepared, nowInSec, values, ConsistencyLevel.ONE);
}
private static QueryOptions makeInternalOptionsWithNowInSec(CQLStatement prepared, int nowInSec, Object[] values, ConsistencyLevel cl)
{
if (prepared.getBindVariables().size() != values.length)
throw new IllegalArgumentException(String.format("Invalid number of values. Expecting %d but got %d", prepared.getBindVariables().size(), values.length));
@ -383,10 +394,10 @@ public class QueryProcessor implements QueryHandler
for (int i = 0; i < values.length; i++)
{
Object value = values[i];
AbstractType type = prepared.getBindVariables().get(i).type;
boundValues.add(value instanceof ByteBuffer || value == null ? (ByteBuffer)value : type.decompose(value));
AbstractType<?> type = prepared.getBindVariables().get(i).type;
boundValues.add(value instanceof ByteBuffer || value == null ? (ByteBuffer)value : type.decomposeUntyped(value));
}
return QueryOptions.forInternalCalls(cl, boundValues);
return QueryOptions.forInternalCallsWithNowInSec(nowInSec, cl, boundValues);
}
public static Prepared prepareInternal(String query) throws RequestValidationException
@ -497,6 +508,16 @@ public class QueryProcessor implements QueryHandler
return execute(query, cl, internalQueryState(), values);
}
public static UntypedResultSet executeInternalWithNowInSec(String query, int nowInSec, Object... values)
{
Prepared prepared = prepareInternal(query);
ResultMessage result = prepared.statement.executeLocally(internalQueryState(), makeInternalOptionsWithNowInSec(prepared.statement, nowInSec, values));
if (result instanceof ResultMessage.Rows)
return UntypedResultSet.create(((ResultMessage.Rows)result).result);
else
return null;
}
public static UntypedResultSet execute(String query, ConsistencyLevel cl, QueryState state, Object... values)
throws RequestExecutionException
{
@ -572,6 +593,19 @@ public class QueryProcessor implements QueryHandler
return UntypedResultSet.create(((ResultMessage.Rows)result).result);
}
/**
* A special version of executeInternal that takes the time used as "now" for the query in argument.
* Note that this only make sense for Selects so this only accept SELECT statements and is only useful in rare
* cases.
*/
public static Map<DecoratedKey, List<Row>> executeInternalRawWithNow(int nowInSec, String query, Object... values)
{
Prepared prepared = prepareInternal(query);
assert prepared.statement instanceof SelectStatement;
SelectStatement select = (SelectStatement)prepared.statement;
return select.executeRawInternal(makeInternalOptions(prepared.statement, values), internalQueryState().getClientState(), nowInSec);
}
public static UntypedResultSet resultify(String query, RowIterator partition)
{
return resultify(query, PartitionIterators.singletonIterator(partition));

View File

@ -374,6 +374,12 @@ public abstract class UntypedResultSet implements Iterable<UntypedResultSet.Row>
return Int32Type.instance.compose(data.get(column));
}
public int getInt(String column, int ifNull)
{
ByteBuffer bytes = data.get(column);
return bytes == null ? ifNull : Int32Type.instance.compose(bytes);
}
public double getDouble(String column)
{
return DoubleType.instance.compose(data.get(column));

View File

@ -94,7 +94,7 @@ public abstract class TimeFcts
/**
* Function that convert a value of <code>TIMEUUID</code> into a value of type <code>TIMESTAMP</code>.
* @deprecated Replaced by the {@link #timeUuidToTimestamp} function
* @deprecated Replaced by the {@link #toTimestamp} function
*/
public static final NativeScalarFunction dateOfFct = new NativeScalarFunction("dateof", TimestampType.instance, TimeUUIDType.instance)
{
@ -120,7 +120,7 @@ public abstract class TimeFcts
/**
* Function that convert a value of type <code>TIMEUUID</code> into an UNIX timestamp.
* @deprecated Replaced by the {@link #timeUuidToUnixTimestamp} function
* @deprecated Replaced by the {@link #toUnixTimestamp} function
*/
public static final NativeScalarFunction unixTimestampOfFct = new NativeScalarFunction("unixtimestampof", LongType.instance, TimeUUIDType.instance)
{

View File

@ -36,6 +36,7 @@ import org.apache.cassandra.db.partitions.PartitionUpdate;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.service.CASRequest;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.paxos.Ballot;
import org.apache.cassandra.utils.Pair;
import org.apache.cassandra.utils.TimeUUID;
@ -180,7 +181,7 @@ public class CQL3CasRequest implements CASRequest
return new RegularAndStaticColumns(statics, regulars);
}
public SinglePartitionReadQuery readCommand(int nowInSec)
public SinglePartitionReadCommand readCommand(int nowInSec)
{
assert staticConditions != null || !conditions.isEmpty();
@ -190,7 +191,7 @@ public class CQL3CasRequest implements CASRequest
// With only a static condition, we still want to make the distinction between a non-existing partition and one
// that exists (has some live data) but has not static content. So we query the first live row of the partition.
if (conditions.isEmpty())
return SinglePartitionReadQuery.create(metadata,
return SinglePartitionReadCommand.create(metadata,
nowInSec,
columnFilter,
RowFilter.NONE,
@ -199,7 +200,7 @@ public class CQL3CasRequest implements CASRequest
new ClusteringIndexSliceFilter(Slices.ALL, false));
ClusteringIndexNamesFilter filter = new ClusteringIndexNamesFilter(conditions.navigableKeySet(), false);
return SinglePartitionReadQuery.create(metadata, nowInSec, key, columnFilter, filter);
return SinglePartitionReadCommand.create(metadata, nowInSec, key, columnFilter, filter);
}
/**
@ -232,14 +233,14 @@ public class CQL3CasRequest implements CASRequest
return builder.build();
}
public PartitionUpdate makeUpdates(FilteredPartition current, ClientState state, TimeUUID ballot) throws InvalidRequestException
public PartitionUpdate makeUpdates(FilteredPartition current, ClientState clientState, Ballot ballot) throws InvalidRequestException
{
PartitionUpdate.Builder updateBuilder = new PartitionUpdate.Builder(metadata, key, updatedColumns(), conditions.size());
long timeUuidNanos = 0;
for (RowUpdate upd : updates)
timeUuidNanos = upd.applyUpdates(current, updateBuilder, state, ballot.msb(), timeUuidNanos);
timeUuidNanos = upd.applyUpdates(current, updateBuilder, clientState, ballot.msb(), timeUuidNanos);
for (RangeDeletion upd : rangeDeletions)
upd.applyUpdates(current, updateBuilder, state);
upd.applyUpdates(current, updateBuilder, clientState);
PartitionUpdate partitionUpdate = updateBuilder.build();
IndexRegistry.obtain(metadata).validate(partitionUpdate);

View File

@ -51,18 +51,18 @@ import org.apache.cassandra.exceptions.*;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.service.paxos.Ballot;
import org.apache.cassandra.service.paxos.BallotGenerator;
import org.apache.cassandra.service.paxos.Commit;
import org.apache.cassandra.service.paxos.Commit.Proposal;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.triggers.TriggerExecutor;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.MD5Digest;
import org.apache.cassandra.utils.Pair;
import org.apache.cassandra.utils.TimeUUID;
import org.apache.cassandra.utils.UUIDGen;
import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
import static org.apache.cassandra.cql3.statements.RequestValidations.checkNull;
import static org.apache.cassandra.service.paxos.Ballot.Flag.NONE;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
/*
@ -660,7 +660,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
static RowIterator casInternal(ClientState state, CQL3CasRequest request, long timestamp, int nowInSeconds)
{
TimeUUID ballot = BallotGenerator.Global.randomBallot(timestamp, false);
Ballot ballot = BallotGenerator.Global.atUnixMicros(timestamp, NONE);
SinglePartitionReadQuery readCommand = request.readCommand(nowInSeconds);
FilteredPartition current;
@ -676,7 +676,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
PartitionUpdate updates = request.makeUpdates(current, state, ballot);
updates = TriggerExecutor.instance.execute(updates);
Commit proposal = Commit.newProposal(ballot, updates);
Proposal proposal = Proposal.of(ballot, updates);
proposal.makeMutation().apply();
return null;
}

View File

@ -254,7 +254,7 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
query.trackWarnings();
if (aggregationSpec == null && (pageSize <= 0 || (query.limits().count() <= pageSize)))
return execute(query, options, state, selectors, nowInSec, userLimit, queryStartNanoTime);
return execute(query, options, state.getClientState(), selectors, nowInSec, userLimit, queryStartNanoTime);
QueryPager pager = getPager(query, options);
@ -300,12 +300,12 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
private ResultMessage.Rows execute(ReadQuery query,
QueryOptions options,
QueryState state,
ClientState state,
Selectors selectors,
int nowInSec,
int userLimit, long queryStartNanoTime) throws RequestValidationException, RequestExecutionException
{
try (PartitionIterator data = query.execute(options.getConsistency(), state.getClientState(), queryStartNanoTime))
try (PartitionIterator data = query.execute(options.getConsistency(), state, queryStartNanoTime))
{
return processResults(data, options, selectors, nowInSec, userLimit);
}
@ -491,6 +491,50 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
return new AggregationQueryPager(pager, query.limits());
}
public Map<DecoratedKey, List<Row>> executeRawInternal(QueryOptions options, ClientState state, int nowInSec) throws RequestExecutionException, RequestValidationException
{
int userLimit = getLimit(options);
int userPerPartitionLimit = getPerPartitionLimit(options);
if (options.getPageSize() > 0)
throw new IllegalStateException();
if (aggregationSpec != null)
throw new IllegalStateException();
Selectors selectors = selection.newSelectors(options);
ReadQuery query = getQuery(options, state, selectors.getColumnFilter(), nowInSec, userLimit, userPerPartitionLimit, Integer.MAX_VALUE);
Map<DecoratedKey, List<Row>> result = Collections.emptyMap();
try (ReadExecutionController executionController = query.executionController())
{
try (PartitionIterator data = query.executeInternal(executionController))
{
while (data.hasNext())
{
try (RowIterator in = data.next())
{
List<Row> out = Collections.emptyList();
while (in.hasNext())
{
switch (out.size())
{
case 0: out = Collections.singletonList(in.next()); break;
case 1: out = new ArrayList<>(out);
default: out.add(in.next());
}
}
switch (result.size())
{
case 0: result = Collections.singletonMap(in.partitionKey(), out); break;
case 1: result = new TreeMap<>(result);
default: result.put(in.partitionKey(), out);
}
}
}
return result;
}
}
}
public ResultSet process(PartitionIterator partitions, int nowInSec) throws InvalidRequestException
{
QueryOptions options = QueryOptions.DEFAULT;

View File

@ -40,10 +40,10 @@ import java.util.stream.Collectors;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.*;
import com.google.common.base.Throwables;
import com.google.common.collect.*;
import com.google.common.util.concurrent.*;
import org.apache.cassandra.service.paxos.Ballot;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.TimeUUID;
import org.apache.cassandra.utils.concurrent.AsyncPromise;
@ -99,7 +99,15 @@ import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.service.snapshot.SnapshotManifest;
import org.apache.cassandra.service.snapshot.TableSnapshot;
import org.apache.cassandra.streaming.TableStreamManager;
import org.apache.cassandra.utils.*;
import org.apache.cassandra.service.paxos.PaxosRepairHistory;
import org.apache.cassandra.service.paxos.TablePaxosRepairHistory;
import org.apache.cassandra.utils.DefaultValue;
import org.apache.cassandra.utils.ExecutorUtils;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.JVMStabilityInspector;
import org.apache.cassandra.utils.MBeanWrapper;
import org.apache.cassandra.utils.NoSpamLogger;
import org.apache.cassandra.utils.WrappedRunnable;
import org.apache.cassandra.utils.concurrent.OpOrder;
import org.apache.cassandra.utils.concurrent.Promise;
import org.apache.cassandra.utils.concurrent.Refs;
@ -228,6 +236,29 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
private volatile boolean neverPurgeTombstones = false;
private class PaxosRepairHistoryLoader
{
private TablePaxosRepairHistory history;
TablePaxosRepairHistory get()
{
if (history != null)
return history;
synchronized (this)
{
if (history != null)
return history;
history = TablePaxosRepairHistory.load(keyspace.getName(), name);
return history;
}
}
}
private final PaxosRepairHistoryLoader paxosRepairHistory = new PaxosRepairHistoryLoader();
public static void shutdownPostFlushExecutor() throws InterruptedException
{
postFlushExecutor.shutdown();
@ -604,7 +635,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
return createColumnFamilyStore(keyspace, metadata.name, metadata, loadSSTables);
}
public static synchronized ColumnFamilyStore createColumnFamilyStore(Keyspace keyspace,
public static ColumnFamilyStore createColumnFamilyStore(Keyspace keyspace,
String columnFamily,
TableMetadataRef metadata,
boolean loadSSTables)
@ -1143,7 +1174,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
* with CL as we do with memtables/CFS-backed SecondaryIndexes.
*/
if (flushNonCf2i)
indexManager.flushAllNonCFSBackedIndexesBlocking();
indexManager.flushAllNonCFSBackedIndexesBlocking(memtable);
flushResults = Lists.newArrayList(FBUtilities.waitOnFutures(futures));
}
@ -1731,6 +1762,31 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
|| filter.isFullyCoveredBy(cached);
}
public PaxosRepairHistory getPaxosRepairHistory()
{
return paxosRepairHistory.get().getHistory();
}
public PaxosRepairHistory getPaxosRepairHistoryForRanges(Collection<Range<Token>> ranges)
{
return paxosRepairHistory.get().getHistoryForRanges(ranges);
}
public void syncPaxosRepairHistory(PaxosRepairHistory sync, boolean flush)
{
paxosRepairHistory.get().merge(sync, flush);
}
public void onPaxosRepairComplete(Collection<Range<Token>> ranges, Ballot highBallot)
{
paxosRepairHistory.get().add(ranges, highBallot, true);
}
public Ballot getPaxosRepairLowBound(DecoratedKey key)
{
return paxosRepairHistory.get().getBallotForToken(key.getToken());
}
public int gcBefore(int nowInSec)
{
return nowInSec - metadata().params.gcGraceSeconds;

View File

@ -20,6 +20,7 @@ package org.apache.cassandra.db;
import com.carrotsearch.hppc.ObjectIntHashMap;
import org.apache.cassandra.locator.Endpoints;
import org.apache.cassandra.locator.InOurDc;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.exceptions.InvalidRequestException;
@ -28,7 +29,6 @@ import org.apache.cassandra.locator.NetworkTopologyStrategy;
import org.apache.cassandra.transport.ProtocolException;
import static org.apache.cassandra.locator.Replicas.addToCountPerDc;
import static org.apache.cassandra.locator.Replicas.countInOurDc;
public enum ConsistencyLevel
{
@ -41,7 +41,7 @@ public enum ConsistencyLevel
LOCAL_QUORUM(6, true),
EACH_QUORUM (7),
SERIAL (8),
LOCAL_SERIAL(9),
LOCAL_SERIAL(9, true),
LOCAL_ONE (10, true),
NODE_LOCAL (11, true);
@ -173,7 +173,7 @@ public enum ConsistencyLevel
break;
case LOCAL_ONE: case LOCAL_QUORUM: case LOCAL_SERIAL:
// we will only count local replicas towards our response count, as these queries only care about local guarantees
blockFor += countInOurDc(pending).allReplicas();
blockFor += pending.count(InOurDc.replicas());
break;
case ONE: case TWO: case THREE:
case QUORUM: case EACH_QUORUM:

View File

@ -21,6 +21,7 @@ import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.function.Supplier;
import com.google.common.base.Function;
import com.google.common.base.Objects;
@ -82,6 +83,12 @@ public class CounterMutation implements IMutation
return mutation.getPartitionUpdates();
}
@Override
public Supplier<Mutation> hintOnFailure()
{
return null;
}
public void validateSize(int version, int overhead)
{
long totalSize = serializedSize(version) + overhead;

View File

@ -19,6 +19,7 @@ package org.apache.cassandra.db;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.partitions.PartitionUpdate;
@ -35,6 +36,7 @@ public interface IMutation
public long getTimeout(TimeUnit unit);
public String toString(boolean shallow);
public Collection<PartitionUpdate> getPartitionUpdates();
public Supplier<Mutation> hintOnFailure();
public default void validateIndexedColumns()
{

View File

@ -119,6 +119,11 @@ public class Keyspace
private static volatile boolean initialized = false;
public static boolean isInitialized()
{
return initialized;
}
public static void setInitialized()
{
initialized = true;

View File

@ -21,6 +21,7 @@ import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
@ -44,7 +45,7 @@ import static org.apache.cassandra.net.MessagingService.VERSION_3014;
import static org.apache.cassandra.net.MessagingService.VERSION_40;
import static org.apache.cassandra.utils.MonotonicClock.Global.approxTime;
public class Mutation implements IMutation
public class Mutation implements IMutation, Supplier<Mutation>
{
public static final MutationSerializer serializer = new MutationSerializer();
@ -132,6 +133,18 @@ public class Mutation implements IMutation
return modifications.values();
}
@Override
public Supplier<Mutation> hintOnFailure()
{
return this;
}
@Override
public Mutation get()
{
return this;
}
public void validateSize(int version, int overhead)
{
long totalSize = serializedSize(version) + overhead;
@ -210,9 +223,14 @@ public class Mutation implements IMutation
return ks.applyFuture(this, Keyspace.open(keyspaceName).getMetadata().params.durableWrites, true);
}
private void apply(Keyspace keyspace, boolean durableWrites, boolean isDroppable)
{
keyspace.apply(this, durableWrites, true, isDroppable);
}
public void apply(boolean durableWrites, boolean isDroppable)
{
Keyspace.open(keyspaceName).apply(this, durableWrites, true, isDroppable);
apply(Keyspace.open(keyspaceName), durableWrites, isDroppable);
}
public void apply(boolean durableWrites)
@ -226,7 +244,8 @@ public class Mutation implements IMutation
*/
public void apply()
{
apply(Keyspace.open(keyspaceName).getMetadata().params.durableWrites);
Keyspace keyspace = Keyspace.open(keyspaceName);
apply(keyspace, keyspace.getMetadata().params.durableWrites, true);
}
public void applyUnsafe()

View File

@ -40,19 +40,11 @@ public class MutationVerbHandler implements IVerbHandler<Mutation>
public void doVerb(Message<Mutation> message)
{
// Check if there were any forwarding headers in this message
InetAddressAndPort from = message.respondTo();
InetAddressAndPort respondToAddress;
if (from == null)
{
respondToAddress = message.from();
ForwardingInfo forwardTo = message.forwardTo();
if (forwardTo != null) forwardToLocalNodes(message, forwardTo);
}
else
{
respondToAddress = from;
}
ForwardingInfo forwardTo = message.forwardTo();
if (forwardTo != null)
forwardToLocalNodes(message, forwardTo);
InetAddressAndPort respondToAddress = message.respondTo();
try
{
message.payload.applyFuture().addCallback(o -> respond(message, respondToAddress), wto -> failed());

View File

@ -293,7 +293,7 @@ public class PartitionRangeReadCommand extends ReadCommand implements PartitionR
return dataRange.isReversed();
}
public PartitionIterator execute(ConsistencyLevel consistency, ClientState clientState, long queryStartNanoTime) throws RequestExecutionException
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, long queryStartNanoTime) throws RequestExecutionException
{
return StorageProxy.getRangeSlice(this, consistency, queryStartNanoTime);
}
@ -512,7 +512,7 @@ public class PartitionRangeReadCommand extends ReadCommand implements PartitionR
}
@Override
public PartitionIterator execute(ConsistencyLevel consistency, ClientState clientState, long queryStartNanoTime) throws RequestExecutionException
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, long queryStartNanoTime) throws RequestExecutionException
{
return executeInternal(executionController());
}

View File

@ -48,7 +48,7 @@ public interface ReadQuery
return ReadExecutionController.empty();
}
public PartitionIterator execute(ConsistencyLevel consistency, ClientState clientState, long queryStartNanoTime) throws RequestExecutionException
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, long queryStartNanoTime) throws RequestExecutionException
{
return EmptyIterators.partition();
}
@ -140,12 +140,10 @@ public interface ReadQuery
* Executes the query at the provided consistency level.
*
* @param consistency the consistency level to achieve for the query.
* @param clientState the {@code ClientState} for the query. In practice, this can be null unless
* {@code consistency} is a serial consistency.
*
* @param state client state
* @return the result of the query.
*/
public PartitionIterator execute(ConsistencyLevel consistency, ClientState clientState, long queryStartNanoTime) throws RequestExecutionException;
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, long queryStartNanoTime) throws RequestExecutionException;
/**
* Execute the query for internal queries (that is, it basically executes the query locally).

View File

@ -34,6 +34,8 @@ import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.utils.ByteBufferUtil;
import static org.apache.cassandra.db.RepairedDataInfo.NO_OP_REPAIRED_DATA_INFO;
public abstract class ReadResponse
{
// Serializer for single partition read response
@ -48,6 +50,11 @@ public abstract class ReadResponse
return new LocalDataResponse(data, command, rdi);
}
public static ReadResponse createSimpleDataResponse(UnfilteredPartitionIterator data, ColumnFilter selection)
{
return new LocalDataResponse(data, selection);
}
@VisibleForTesting
public static ReadResponse createRemoteDataResponse(UnfilteredPartitionIterator data,
ByteBuffer repairedDataDigest,
@ -184,6 +191,11 @@ public abstract class ReadResponse
DeserializationHelper.Flag.LOCAL);
}
private LocalDataResponse(UnfilteredPartitionIterator iter, ColumnFilter selection)
{
super(build(iter, selection), null, false, MessagingService.current_version, DeserializationHelper.Flag.LOCAL);
}
private static ByteBuffer build(UnfilteredPartitionIterator iter, ColumnFilter selection)
{
try (DataOutputBuffer buffer = new DataOutputBuffer())

View File

@ -36,7 +36,6 @@ import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.CounterId;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.UUIDGen;
import static org.apache.cassandra.utils.TimeUUID.Generator.nextTimeUUIDAsBytes;

View File

@ -434,12 +434,13 @@ public class SinglePartitionReadCommand extends ReadCommand implements SinglePar
return cmd;
}
public PartitionIterator execute(ConsistencyLevel consistency, ClientState clientState, long queryStartNanoTime) throws RequestExecutionException
@Override
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, long queryStartNanoTime) throws RequestExecutionException
{
if (clusteringIndexFilter.isEmpty(metadata().comparator))
return EmptyIterators.partition();
return StorageProxy.read(Group.one(this), consistency, clientState, queryStartNanoTime);
return StorageProxy.read(Group.one(this), consistency, queryStartNanoTime);
}
protected void recordLatency(TableMetrics metric, long latencyNanos)
@ -1221,9 +1222,9 @@ public class SinglePartitionReadCommand extends ReadCommand implements SinglePar
new Group(commands, limits);
}
public PartitionIterator execute(ConsistencyLevel consistency, ClientState clientState, long queryStartNanoTime) throws RequestExecutionException
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, long queryStartNanoTime) throws RequestExecutionException
{
return StorageProxy.read(this, consistency, clientState, queryStartNanoTime);
return StorageProxy.read(this, consistency, queryStartNanoTime);
}
}
@ -1235,13 +1236,13 @@ public class SinglePartitionReadCommand extends ReadCommand implements SinglePar
}
@Override
public PartitionIterator execute(ConsistencyLevel consistency, ClientState clientState, long queryStartNanoTime) throws RequestExecutionException
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, long queryStartNanoTime) throws RequestExecutionException
{
if (queries.size() == 1)
return queries.get(0).execute(consistency, clientState, queryStartNanoTime);
return queries.get(0).execute(consistency, state, queryStartNanoTime);
return PartitionIterators.concat(queries.stream()
.map(q -> q.execute(consistency, clientState, queryStartNanoTime))
.map(q -> q.execute(consistency, state, queryStartNanoTime))
.collect(Collectors.toList()));
}
}
@ -1314,7 +1315,7 @@ public class SinglePartitionReadCommand extends ReadCommand implements SinglePar
}
@Override
public PartitionIterator execute(ConsistencyLevel consistency, ClientState clientState, long queryStartNanoTime) throws RequestExecutionException
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, long queryStartNanoTime) throws RequestExecutionException
{
return executeInternal(executionController());
}

View File

@ -34,6 +34,7 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.common.io.ByteStreams;
@ -51,6 +52,7 @@ import org.apache.cassandra.db.compaction.CompactionHistoryTabularData;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.db.partitions.PartitionUpdate;
import org.apache.cassandra.db.rows.Rows;
import org.apache.cassandra.db.rows.Row;
import org.apache.cassandra.dht.*;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.io.util.*;
@ -60,10 +62,14 @@ import org.apache.cassandra.metrics.RestorableMeter;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.schema.*;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.service.paxos.Commit;
import org.apache.cassandra.service.paxos.PaxosState;
import org.apache.cassandra.streaming.StreamOperation;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.service.paxos.*;
import org.apache.cassandra.service.paxos.Commit.Accepted;
import org.apache.cassandra.service.paxos.Commit.AcceptedWithTTL;
import org.apache.cassandra.service.paxos.Commit.Committed;
import org.apache.cassandra.service.paxos.uncommitted.PaxosRows;
import org.apache.cassandra.service.paxos.uncommitted.PaxosUncommittedIndex;
import org.apache.cassandra.utils.*;
import org.apache.cassandra.utils.concurrent.Future;
@ -71,8 +77,12 @@ import static java.lang.String.format;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.apache.cassandra.config.Config.PaxosStatePurging.*;
import static org.apache.cassandra.config.DatabaseDescriptor.paxosStatePurging;
import static org.apache.cassandra.cql3.QueryProcessor.executeInternal;
import static org.apache.cassandra.cql3.QueryProcessor.executeInternalWithNowInSec;
import static org.apache.cassandra.cql3.QueryProcessor.executeOnceInternal;
import static org.apache.cassandra.service.paxos.Commit.latest;
import static org.apache.cassandra.utils.Clock.Global.currentTimeMillis;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
import static org.apache.cassandra.utils.FBUtilities.now;
@ -100,6 +110,7 @@ public final class SystemKeyspace
public static final String BATCHES = "batches";
public static final String PAXOS = "paxos";
public static final String PAXOS_REPAIR_HISTORY = "paxos_repair_history";
public static final String BUILT_INDEXES = "IndexInfo";
public static final String LOCAL = "local";
public static final String PEERS_V2 = "peers_v2";
@ -153,6 +164,7 @@ public final class SystemKeyspace
+ "row_key blob,"
+ "cf_id UUID,"
+ "in_progress_ballot timeuuid,"
+ "in_progress_read_ballot timeuuid,"
+ "most_recent_commit blob,"
+ "most_recent_commit_at timeuuid,"
+ "most_recent_commit_version int,"
@ -161,6 +173,7 @@ public final class SystemKeyspace
+ "proposal_version int,"
+ "PRIMARY KEY ((row_key), cf_id))")
.compaction(CompactionParams.lcs(emptyMap()))
.indexes(PaxosUncommittedIndex.indexes())
.build();
private static final TableMetadata BuiltIndexes =
@ -173,6 +186,17 @@ public final class SystemKeyspace
+ "PRIMARY KEY ((table_name), index_name)) ")
.build();
private static final TableMetadata PaxosRepairHistoryTable =
parse(PAXOS_REPAIR_HISTORY,
"paxos repair history",
"CREATE TABLE %s ("
+ "keyspace_name text,"
+ "table_name text,"
+ "points frozen<list<tuple<blob, timeuuid>>>, "
+ "PRIMARY KEY (keyspace_name, table_name))"
+ "WITH COMMENT='Last successful paxos repairs by range'")
.build();
private static final TableMetadata Local =
parse(LOCAL,
"information about the local node",
@ -425,6 +449,7 @@ public final class SystemKeyspace
return Tables.of(BuiltIndexes,
Batches,
Paxos,
PaxosRepairHistoryTable,
Local,
PeersV2,
LegacyPeers,
@ -1179,72 +1204,177 @@ public final class SystemKeyspace
return null;
}
public static PaxosState loadPaxosState(DecoratedKey key, TableMetadata metadata, int nowInSec)
/**
* Load the current paxos state for the table and key
*/
public static PaxosState.Snapshot loadPaxosState(DecoratedKey partitionKey, TableMetadata metadata, int nowInSec)
{
String req = "SELECT * FROM system.%s WHERE row_key = ? AND cf_id = ?";
UntypedResultSet results = QueryProcessor.executeInternalWithNow(nowInSec, nanoTime(), format(req, PAXOS), key.getKey(), metadata.id.asUUID());
if (results.isEmpty())
return new PaxosState(key, metadata);
UntypedResultSet.Row row = results.one();
String cql = "SELECT * FROM system." + PAXOS + " WHERE row_key = ? AND cf_id = ?";
List<Row> results = QueryProcessor.executeInternalRawWithNow(nowInSec, cql, partitionKey.getKey(), metadata.id.asUUID()).get(partitionKey);
if (results == null || results.isEmpty())
{
Committed noneCommitted = Committed.none(partitionKey, metadata);
return new PaxosState.Snapshot(Ballot.none(), Ballot.none(), null, noneCommitted);
}
Row row = results.get(0);
Ballot promisedWrite = PaxosRows.getWritePromise(row);
Ballot promised = latest(promisedWrite, PaxosRows.getPromise(row));
Commit promised = row.has("in_progress_ballot")
? new Commit(row.getTimeUUID("in_progress_ballot"), new PartitionUpdate.Builder(metadata, key, metadata.regularAndStaticColumns(), 1).build())
: Commit.emptyCommit(key, metadata);
// either we have both a recently accepted ballot and update or we have neither
Commit accepted = row.has("proposal_version") && row.has("proposal")
? new Commit(row.getTimeUUID("proposal_ballot"),
PartitionUpdate.fromBytes(row.getBytes("proposal"), row.getInt("proposal_version")))
: Commit.emptyCommit(key, metadata);
// either most_recent_commit and most_recent_commit_at will both be set, or neither
Commit mostRecent = row.has("most_recent_commit_version") && row.has("most_recent_commit")
? new Commit(row.getTimeUUID("most_recent_commit_at"),
PartitionUpdate.fromBytes(row.getBytes("most_recent_commit"), row.getInt("most_recent_commit_version")))
: Commit.emptyCommit(key, metadata);
return new PaxosState(promised, accepted, mostRecent);
Accepted accepted = PaxosRows.getAccepted(row);
Committed committed = PaxosRows.getCommitted(metadata, partitionKey, row);
// fix a race with TTL/deletion resolution, where TTL expires after equal deletion is inserted; TTL wins the resolution, and is read using an old ballot's nowInSec
if (accepted != null && !accepted.isAfter(committed))
accepted = null;
return new PaxosState.Snapshot(promised, promisedWrite, accepted, committed);
}
public static void savePaxosPromise(Commit promise)
public static int legacyPaxosTtlSec(TableMetadata metadata)
{
String req = "UPDATE system.%s USING TIMESTAMP ? AND TTL ? SET in_progress_ballot = ? WHERE row_key = ? AND cf_id = ?";
executeInternal(format(req, PAXOS),
promise.ballot.unixMicros(),
paxosTtlSec(promise.update.metadata()),
promise.ballot,
promise.update.partitionKey().getKey(),
promise.update.metadata().id.asUUID());
// keep paxos state around for at least 3h
return Math.max(3 * 3600, metadata.params.gcGraceSeconds);
}
public static void savePaxosWritePromise(DecoratedKey key, TableMetadata metadata, Ballot ballot)
{
if (paxosStatePurging() == legacy)
{
String cql = "UPDATE system." + PAXOS + " USING TIMESTAMP ? AND TTL ? SET in_progress_ballot = ? WHERE row_key = ? AND cf_id = ?";
executeInternal(cql,
ballot.unixMicros(),
legacyPaxosTtlSec(metadata),
ballot,
key.getKey(),
metadata.id.asUUID());
}
else
{
String cql = "UPDATE system." + PAXOS + " USING TIMESTAMP ? SET in_progress_ballot = ? WHERE row_key = ? AND cf_id = ?";
executeInternal(cql,
ballot.unixMicros(),
ballot,
key.getKey(),
metadata.id.asUUID());
}
}
public static void savePaxosReadPromise(DecoratedKey key, TableMetadata metadata, Ballot ballot)
{
if (paxosStatePurging() == legacy)
{
String cql = "UPDATE system." + PAXOS + " USING TIMESTAMP ? AND TTL ? SET in_progress_read_ballot = ? WHERE row_key = ? AND cf_id = ?";
executeInternal(cql,
ballot.unixMicros(),
legacyPaxosTtlSec(metadata),
ballot,
key.getKey(),
metadata.id.asUUID());
}
else
{
String cql = "UPDATE system." + PAXOS + " USING TIMESTAMP ? SET in_progress_read_ballot = ? WHERE row_key = ? AND cf_id = ?";
executeInternal(cql,
ballot.unixMicros(),
ballot,
key.getKey(),
metadata.id.asUUID());
}
}
public static void savePaxosProposal(Commit proposal)
{
executeInternal(format("UPDATE system.%s USING TIMESTAMP ? AND TTL ? SET proposal_ballot = ?, proposal = ?, proposal_version = ? WHERE row_key = ? AND cf_id = ?", PAXOS),
proposal.ballot.unixMicros(),
paxosTtlSec(proposal.update.metadata()),
proposal.ballot,
PartitionUpdate.toBytes(proposal.update, MessagingService.current_version),
MessagingService.current_version,
proposal.update.partitionKey().getKey(),
proposal.update.metadata().id.asUUID());
}
public static int paxosTtlSec(TableMetadata metadata)
{
// keep paxos state around for at least 3h
return Math.max(3 * 3600, metadata.params.gcGraceSeconds);
if (proposal instanceof AcceptedWithTTL)
{
int localDeletionTime = ((Commit.AcceptedWithTTL) proposal).localDeletionTime;
int ttlInSec = legacyPaxosTtlSec(proposal.update.metadata());
int nowInSec = localDeletionTime - ttlInSec;
String cql = "UPDATE system." + PAXOS + " USING TIMESTAMP ? AND TTL ? SET proposal_ballot = ?, proposal = ?, proposal_version = ? WHERE row_key = ? AND cf_id = ?";
executeInternalWithNowInSec(cql,
nowInSec,
proposal.ballot.unixMicros(),
ttlInSec,
proposal.ballot,
PartitionUpdate.toBytes(proposal.update, MessagingService.current_version),
MessagingService.current_version,
proposal.update.partitionKey().getKey(),
proposal.update.metadata().id.asUUID());
}
else
{
String cql = "UPDATE system." + PAXOS + " USING TIMESTAMP ? SET proposal_ballot = ?, proposal = ?, proposal_version = ? WHERE row_key = ? AND cf_id = ?";
executeInternal(cql,
proposal.ballot.unixMicros(),
proposal.ballot,
PartitionUpdate.toBytes(proposal.update, MessagingService.current_version),
MessagingService.current_version,
proposal.update.partitionKey().getKey(),
proposal.update.metadata().id.asUUID());
}
}
public static void savePaxosCommit(Commit commit)
{
// We always erase the last proposal (with the commit timestamp to no erase more recent proposal in case the commit is old)
// even though that's really just an optimization since SP.beginAndRepairPaxos will exclude accepted proposal older than the mrc.
String cql = "UPDATE system.%s USING TIMESTAMP ? AND TTL ? SET proposal_ballot = null, proposal = null, most_recent_commit_at = ?, most_recent_commit = ?, most_recent_commit_version = ? WHERE row_key = ? AND cf_id = ?";
executeInternal(format(cql, PAXOS),
commit.ballot.unixMicros(),
paxosTtlSec(commit.update.metadata()),
commit.ballot,
PartitionUpdate.toBytes(commit.update, MessagingService.current_version),
MessagingService.current_version,
commit.update.partitionKey().getKey(),
commit.update.metadata().id.asUUID());
if (commit instanceof Commit.CommittedWithTTL)
{
int localDeletionTime = ((Commit.CommittedWithTTL) commit).localDeletionTime;
int ttlInSec = legacyPaxosTtlSec(commit.update.metadata());
int nowInSec = localDeletionTime - ttlInSec;
String cql = "UPDATE system." + PAXOS + " USING TIMESTAMP ? AND TTL ? SET proposal_ballot = null, proposal = null, proposal_version = null, most_recent_commit_at = ?, most_recent_commit = ?, most_recent_commit_version = ? WHERE row_key = ? AND cf_id = ?";
executeInternalWithNowInSec(cql,
nowInSec,
commit.ballot.unixMicros(),
ttlInSec,
commit.ballot,
PartitionUpdate.toBytes(commit.update, MessagingService.current_version),
MessagingService.current_version,
commit.update.partitionKey().getKey(),
commit.update.metadata().id.asUUID());
}
else
{
String cql = "UPDATE system." + PAXOS + " USING TIMESTAMP ? SET proposal_ballot = null, proposal = null, proposal_version = null, most_recent_commit_at = ?, most_recent_commit = ?, most_recent_commit_version = ? WHERE row_key = ? AND cf_id = ?";
executeInternal(cql,
commit.ballot.unixMicros(),
commit.ballot,
PartitionUpdate.toBytes(commit.update, MessagingService.current_version),
MessagingService.current_version,
commit.update.partitionKey().getKey(),
commit.update.metadata().id.asUUID());
}
}
@VisibleForTesting
public static void savePaxosRepairHistory(String keyspace, String table, PaxosRepairHistory history, boolean flush)
{
String cql = "INSERT INTO system.%s (keyspace_name, table_name, points) VALUES (?, ?, ?)";
executeInternal(String.format(cql, PAXOS_REPAIR_HISTORY), keyspace, table, history.toTupleBufferList());
if (flush)
flushPaxosRepairHistory();
}
public static void flushPaxosRepairHistory()
{
Schema.instance.getColumnFamilyStoreInstance(PaxosRepairHistoryTable.id).forceBlockingFlush();
}
public static PaxosRepairHistory loadPaxosRepairHistory(String keyspace, String table)
{
if (SchemaConstants.LOCAL_SYSTEM_KEYSPACE_NAMES.contains(keyspace))
return PaxosRepairHistory.EMPTY;
UntypedResultSet results = executeInternal(String.format("SELECT * FROM system.%s WHERE keyspace_name=? AND table_name=?", PAXOS_REPAIR_HISTORY), keyspace, table);
if (results.isEmpty())
return PaxosRepairHistory.EMPTY;
UntypedResultSet.Row row = Iterables.getOnlyElement(results);
List<ByteBuffer> points = row.getList("points", BytesType.instance);
return PaxosRepairHistory.fromTupleBufferList(points);
}
/**

View File

@ -52,7 +52,6 @@ import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.schema.TableId;
import org.apache.cassandra.schema.TableMetadataRef;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.WrappedRunnable;

View File

@ -38,7 +38,6 @@ import org.apache.cassandra.db.lifecycle.LifecycleTransaction;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.io.sstable.Component;
import org.apache.cassandra.io.sstable.ISSTableScanner;
import org.apache.cassandra.io.sstable.metadata.MetadataCollector;
import org.apache.cassandra.io.sstable.metadata.StatsMetadata;

View File

@ -17,16 +17,14 @@
*/
package org.apache.cassandra.db.compaction;
import org.apache.cassandra.utils.Shared;
@Shared
public class CompactionInterruptedException extends RuntimeException
{
private static final long serialVersionUID = -8651427062512310398L;
public CompactionInterruptedException(CompactionInfo info)
{
super("Compaction interrupted: " + info);
}
public CompactionInterruptedException(String info)
public CompactionInterruptedException(Object info)
{
super("Compaction interrupted: " + info);
}

View File

@ -19,11 +19,17 @@ package org.apache.cassandra.db.compaction;
import java.util.*;
import java.util.function.LongPredicate;
import java.util.concurrent.TimeUnit;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Ordering;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.schema.TableId;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.db.transform.DuplicateRowChecker;
@ -37,8 +43,14 @@ import org.apache.cassandra.db.transform.Transformation;
import org.apache.cassandra.index.transactions.CompactionTransaction;
import org.apache.cassandra.io.sstable.ISSTableScanner;
import org.apache.cassandra.schema.CompactionParams.TombstoneOption;
import org.apache.cassandra.service.paxos.PaxosRepairHistory;
import org.apache.cassandra.service.paxos.uncommitted.PaxosRows;
import org.apache.cassandra.utils.TimeUUID;
import static java.util.concurrent.TimeUnit.MICROSECONDS;
import static org.apache.cassandra.config.Config.PaxosStatePurging.legacy;
import static org.apache.cassandra.config.DatabaseDescriptor.paxosStatePurging;
/**
* Merge multiple iterators over the content of sstable into a "compacted" iterator.
* <p>
@ -110,7 +122,10 @@ public class CompactionIterator extends CompactionInfo.Holder implements Unfilte
? EmptyIterators.unfilteredPartition(controller.cfs.metadata())
: UnfilteredPartitionIterators.merge(scanners, listener());
merged = Transformation.apply(merged, new GarbageSkipper(controller));
merged = Transformation.apply(merged, new Purger(controller, nowInSec));
Transformation<UnfilteredRowIterator> purger = isPaxos(controller.cfs) && paxosStatePurging() != legacy
? new PaxosPurger(nowInSec)
: new Purger(controller, nowInSec);
merged = Transformation.apply(merged, purger);
merged = DuplicateRowChecker.duringCompaction(merged, type);
compacted = Transformation.apply(merged, new AbortableUnfilteredPartitionTransformation(this));
}
@ -556,6 +571,78 @@ public class CompactionIterator extends CompactionInfo.Holder implements Unfilte
}
}
private class PaxosPurger extends Transformation<UnfilteredRowIterator>
{
private final long nowInSec;
private final long paxosPurgeGraceMicros = DatabaseDescriptor.getPaxosPurgeGrace(MICROSECONDS);
private final Map<TableId, PaxosRepairHistory.Searcher> tableIdToHistory = new HashMap<>();
private Token currentToken;
private int compactedUnfiltered;
private PaxosPurger(long nowInSec)
{
this.nowInSec = nowInSec;
}
protected void onEmptyPartitionPostPurge(DecoratedKey key)
{
if (type == OperationType.COMPACTION)
controller.cfs.invalidateCachedPartition(key);
}
protected void updateProgress()
{
if ((++compactedUnfiltered) % UNFILTERED_TO_UPDATE_PROGRESS == 0)
updateBytesRead();
}
@Override
@SuppressWarnings("resource")
protected UnfilteredRowIterator applyToPartition(UnfilteredRowIterator partition)
{
currentToken = partition.partitionKey().getToken();
UnfilteredRowIterator purged = Transformation.apply(partition, this);
if (purged.isEmpty())
{
onEmptyPartitionPostPurge(purged.partitionKey());
purged.close();
return null;
}
return purged;
}
@Override
protected Row applyToRow(Row row)
{
updateProgress();
TableId tableId = PaxosRows.getTableId(row);
switch (paxosStatePurging())
{
default: throw new AssertionError();
case legacy:
case gc_grace:
{
TableMetadata metadata = Schema.instance.getTableMetadata(tableId);
return row.purgeDataOlderThan(TimeUnit.SECONDS.toMicros(nowInSec - (metadata == null ? (3 * 3600) : metadata.params.gcGraceSeconds)), false);
}
case repaired:
{
PaxosRepairHistory.Searcher history = tableIdToHistory.computeIfAbsent(tableId, find -> {
TableMetadata metadata = Schema.instance.getTableMetadata(find);
if (metadata == null)
return null;
return Keyspace.openAndGetStore(metadata).getPaxosRepairHistory().searcher();
});
return history == null ? row :
row.purgeDataOlderThan(history.ballotForToken(currentToken).unixMicros() - paxosPurgeGraceMicros, false);
}
}
}
}
private static class AbortableUnfilteredPartitionTransformation extends Transformation<UnfilteredRowIterator>
{
private final AbortableUnfilteredRowTransformation abortableIter;
@ -574,7 +661,7 @@ public class CompactionIterator extends CompactionInfo.Holder implements Unfilte
}
}
private static class AbortableUnfilteredRowTransformation extends Transformation
private static class AbortableUnfilteredRowTransformation extends Transformation<UnfilteredRowIterator>
{
private final CompactionIterator iter;
@ -590,4 +677,9 @@ public class CompactionIterator extends CompactionInfo.Holder implements Unfilte
return row;
}
}
private static boolean isPaxos(ColumnFamilyStore cfs)
{
return cfs.name.equals(SystemKeyspace.PAXOS) && cfs.keyspace.getName().equals(SchemaConstants.SYSTEM_KEYSPACE_NAME);
}
}

View File

@ -29,6 +29,7 @@ import org.apache.cassandra.db.Digest;
import org.apache.cassandra.db.TypeSizes;
import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.service.paxos.Ballot;
import org.apache.cassandra.utils.TimeUUID;
import org.apache.cassandra.utils.ByteArrayUtil;
import org.apache.cassandra.utils.ByteBufferUtil;
@ -241,6 +242,12 @@ public class ByteArrayAccessor implements ValueAccessor<byte[]>
return TimeUUID.fromBytes(getLong(value, 0), getLong(value, 8));
}
@Override
public Ballot toBallot(byte[] value)
{
return Ballot.deserialize(value);
}
@Override
public int putShort(byte[] dst, int offset, short value)
{

View File

@ -28,6 +28,7 @@ import org.apache.cassandra.db.Digest;
import org.apache.cassandra.db.TypeSizes;
import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.service.paxos.Ballot;
import org.apache.cassandra.utils.TimeUUID;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FastByteOperations;
@ -245,6 +246,12 @@ public class ByteBufferAccessor implements ValueAccessor<ByteBuffer>
return TimeUUID.fromBytes(value.getLong(value.position()), value.getLong(value.position() + 8));
}
@Override
public Ballot toBallot(ByteBuffer value)
{
return Ballot.deserialize(value);
}
@Override
public int putShort(ByteBuffer dst, int offset, short value)
{

View File

@ -205,9 +205,17 @@ public class TupleType extends AbstractType<ByteBuffer>
*/
public ByteBuffer[] split(ByteBuffer value)
{
ByteBuffer[] components = new ByteBuffer[size()];
return split(value, size(), this);
}
/**
* Split a tuple value into its component values.
*/
public static ByteBuffer[] split(ByteBuffer value, int numberOfElements, TupleType type)
{
ByteBuffer[] components = new ByteBuffer[numberOfElements];
ByteBuffer input = value.duplicate();
for (int i = 0; i < size(); i++)
for (int i = 0; i < numberOfElements; i++)
{
if (!input.hasRemaining())
return Arrays.copyOfRange(components, 0, i);
@ -226,7 +234,7 @@ public class TupleType extends AbstractType<ByteBuffer>
{
throw new InvalidRequestException(String.format(
"Expected %s %s for %s column, but got more",
size(), size() == 1 ? "value" : "values", this.asCQL3Type()));
numberOfElements, numberOfElements == 1 ? "value" : "values", type.asCQL3Type()));
}
return components;

View File

@ -37,6 +37,7 @@ import org.apache.cassandra.db.rows.CellPath;
import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.service.paxos.Ballot;
import org.apache.cassandra.utils.TimeUUID;
import static org.apache.cassandra.db.ClusteringPrefix.Kind.*;
@ -326,6 +327,9 @@ public interface ValueAccessor<V>
/** returns a TimeUUID from offset 0 */
TimeUUID toTimeUUID(V value);
/** returns a TimeUUID from offset 0 */
Ballot toBallot(V value);
/**
* writes the short value {@param value} to {@param dst} at offset {@param offset}
* @return the number of bytes written to {@param value}

View File

@ -375,26 +375,52 @@ public abstract class AbstractBTreePartition implements Partition, Iterable<Row>
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
return toString(true);
}
sb.append(String.format("[%s] key=%s partition_deletion=%s columns=%s",
metadata(),
metadata().partitionKeyType.getString(partitionKey().getKey()),
partitionLevelDeletion(),
columns()));
public String toString(boolean includeFullDetails)
{
StringBuilder sb = new StringBuilder();
if (includeFullDetails)
{
sb.append(String.format("[%s.%s] key=%s partition_deletion=%s columns=%s",
metadata().keyspace,
metadata().name,
metadata().partitionKeyType.getString(partitionKey().getKey()),
partitionLevelDeletion(),
columns()));
}
else
{
sb.append("key=").append(metadata().partitionKeyType.getString(partitionKey().getKey()));
}
if (staticRow() != Rows.EMPTY_STATIC_ROW)
sb.append("\n ").append(staticRow().toString(metadata(), true));
sb.append("\n ").append(staticRow().toString(metadata(), includeFullDetails));
try (UnfilteredRowIterator iter = unfilteredIterator())
{
while (iter.hasNext())
sb.append("\n ").append(iter.next().toString(metadata(), true));
sb.append("\n ").append(iter.next().toString(metadata(), includeFullDetails));
}
return sb.toString();
}
@Override
public boolean equals(Object obj)
{
if (!(obj instanceof PartitionUpdate))
return false;
PartitionUpdate that = (PartitionUpdate) obj;
Holder a = this.holder(), b = that.holder();
return partitionKey.equals(that.partitionKey)
&& metadata().id.equals(that.metadata().id)
&& a.deletionInfo.equals(b.deletionInfo)
&& a.staticRow.equals(b.staticRow)
&& Iterators.elementsEqual(iterator(), that.iterator());
}
public int rowCount()
{
return BTree.size(holder().tree);

View File

@ -17,13 +17,16 @@
*/
package org.apache.cassandra.db.partitions;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -38,6 +41,9 @@ import org.apache.cassandra.schema.TableId;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.utils.btree.BTree;
import org.apache.cassandra.utils.btree.UpdateFunction;
import org.apache.cassandra.utils.vint.VIntCoding;
import static org.apache.cassandra.db.rows.UnfilteredRowIteratorSerializer.IS_EMPTY;
/**
* Stores updates made on a partition.
@ -322,23 +328,19 @@ public class PartitionUpdate extends AbstractBTreePartition
*/
public int dataSize()
{
int size = 0;
return Ints.saturatedCast(BTree.<Row>accumulate(holder.tree, (row, value) -> row.dataSize() + value, 0L)
+ holder.staticRow.dataSize() + holder.deletionInfo.dataSize());
}
if (holder.staticRow != null)
{
for (ColumnData cd : holder.staticRow.columnData())
{
size += cd.dataSize();
}
}
for (Row row : this)
{
size += row.clustering().dataSize();
for (ColumnData cd : row)
size += cd.dataSize();
}
return size;
/**
* The size of the data contained in this update.
*
* @return the size of the data contained in this update.
*/
public long unsharedHeapSize()
{
return BTree.<Row>accumulate(holder.tree, (row, value) -> row.unsharedHeapSize() + value, 0L)
+ holder.staticRow.unsharedHeapSize() + holder.deletionInfo.unsharedHeapSize();
}
public TableMetadata metadata()
@ -667,6 +669,21 @@ public class PartitionUpdate extends AbstractBTreePartition
false);
}
public static boolean isEmpty(ByteBuffer in, DeserializationHelper.Flag flag, DecoratedKey key) throws IOException
{
int position = in.position();
position += 16; // CFMetaData.serializer.deserialize(in, version);
if (position >= in.limit())
throw new EOFException();
// DecoratedKey key = metadata.decorateKey(ByteBufferUtil.readWithVIntLength(in));
int keyLength = (int) VIntCoding.getUnsignedVInt(in, position);
position += keyLength + VIntCoding.computeUnsignedVIntSize(keyLength);
if (position >= in.limit())
throw new EOFException();
int flags = in.get(position) & 0xff;
return (flags & IS_EMPTY) != 0;
}
public long serializedSize(PartitionUpdate update, int version)
{
try (UnfilteredRowIterator iter = update.unfilteredIterator())

View File

@ -98,6 +98,11 @@ public abstract class AbstractCell<V> extends Cell<V>
return this;
}
public Cell<?> purgeDataOlderThan(long timestamp)
{
return this.timestamp() < timestamp ? null : this;
}
public Cell<?> copy(AbstractAllocator allocator)
{
CellPath path = path();

View File

@ -474,6 +474,18 @@ public class BTreeRow extends AbstractRow
return transformAndFilter(newInfo, newDeletion, (cd) -> cd.purge(purger, nowInSec));
}
public Row purgeDataOlderThan(long timestamp, boolean enforceStrictLiveness)
{
LivenessInfo newInfo = primaryKeyLivenessInfo.timestamp() < timestamp ? LivenessInfo.EMPTY : primaryKeyLivenessInfo;
Deletion newDeletion = deletion.time().markedForDeleteAt() < timestamp ? Deletion.LIVE : deletion;
// when enforceStrictLiveness is set, a row is considered dead when it's PK liveness info is not present
if (enforceStrictLiveness && newDeletion.isLive() && newInfo.isEmpty())
return null;
return transformAndFilter(newInfo, newDeletion, cd -> cd.purgeDataOlderThan(timestamp));
}
private Row transformAndFilter(LivenessInfo info, Deletion deletion, Function<ColumnData, ColumnData> function)
{
Object[] transformed = BTree.transformAndFilter(btree, function);

View File

@ -26,7 +26,6 @@ import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.db.marshal.ByteType;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.ObjectSizes;
import org.apache.cassandra.utils.memory.AbstractAllocator;
import static java.lang.String.format;
@ -134,14 +133,6 @@ public class BufferCell extends AbstractCell<ByteBuffer>
return withUpdatedValue(ByteBufferUtil.EMPTY_BYTE_BUFFER);
}
public Cell<?> copy(AbstractAllocator allocator)
{
if (!value.hasRemaining())
return this;
return new BufferCell(column, timestamp, ttl, localDeletionTime, allocator.clone(value), path == null ? null : path.copy(allocator));
}
@Override
public long unsharedHeapSize()
{

View File

@ -167,6 +167,10 @@ public abstract class Cell<V> extends ColumnData
// Overrides super type to provide a more precise return type.
public abstract Cell<?> purge(DeletionPurger purger, int nowInSec);
@Override
// Overrides super type to provide a more precise return type.
public abstract Cell<?> purgeDataOlderThan(long timestamp);
/**
* The serialization format for cell is:
* [ flags ][ timestamp ][ deletion time ][ ttl ][ path size ][ path ][ value size ][ value ]

View File

@ -65,6 +65,8 @@ public abstract class CellPath implements IMeasurableMemory
public abstract long unsharedHeapSizeExcludingData();
public abstract long unsharedHeapSize();
@Override
public final int hashCode()
{

View File

@ -58,6 +58,8 @@ public abstract class ColumnData implements IMeasurableMemory
public abstract long unsharedHeapSizeExcludingData();
public abstract long unsharedHeapSize();
/**
* Validate the column data.
*
@ -95,6 +97,7 @@ public abstract class ColumnData implements IMeasurableMemory
public abstract ColumnData markCounterLocalToBeCleared();
public abstract ColumnData purge(DeletionPurger purger, int nowInSec);
public abstract ColumnData purgeDataOlderThan(long timestamp);
public abstract long maxTimestamp();
}

View File

@ -121,13 +121,10 @@ public class ComplexColumnData extends ColumnData implements Iterable<Cell<?>>
return size;
}
@Override
public long unsharedHeapSize()
{
long heapSize = EMPTY_SIZE + ObjectSizes.sizeOfArray(cells);
for (Cell<?> cell : this)
heapSize += cell.unsharedHeapSize();
return heapSize;
long heapSize = EMPTY_SIZE + ObjectSizes.sizeOfArray(cells) + complexDeletion.unsharedHeapSize();
return BTree.<Cell>accumulate(cells, (cell, value) -> value + cell.unsharedHeapSize(), heapSize);
}
@Override
@ -205,6 +202,12 @@ public class ComplexColumnData extends ColumnData implements Iterable<Cell<?>>
return transformAndFilter(complexDeletion, (cell) -> filter.fetchedCellIsQueried(column, cell.path()) ? null : cell);
}
public ComplexColumnData purgeDataOlderThan(long timestamp)
{
DeletionTime newDeletion = complexDeletion.markedForDeleteAt() < timestamp ? DeletionTime.LIVE : complexDeletion;
return transformAndFilter(newDeletion, (cell) -> cell.purgeDataOlderThan(timestamp));
}
private ComplexColumnData transformAndFilter(DeletionTime newDeletion, Function<? super Cell<?>, ? extends Cell<?>> function)
{
Object[] transformed = BTree.transformAndFilter(cells, function);

View File

@ -177,5 +177,4 @@ public class NativeCell extends AbstractCell<ByteBuffer>
{
return EMPTY_SIZE;
}
}

View File

@ -116,7 +116,7 @@ public interface Row extends Unfiltered, Iterable<ColumnData>, IMeasurableMemory
*
* @param nowInSec the current time to decide what is deleted and what isn't
* @param enforceStrictLiveness whether the row should be purged if there is no PK liveness info,
* normally retrieved from {@link CFMetaData#enforceStrictLiveness()}
* normally retrieved from {@link TableMetadata#enforceStrictLiveness()}
* @return true if there is some live information
*/
public boolean hasLiveData(int nowInSec, boolean enforceStrictLiveness);
@ -249,6 +249,11 @@ public interface Row extends Unfiltered, Iterable<ColumnData>, IMeasurableMemory
*/
public Row withOnlyQueriedData(ColumnFilter filter);
/*
* Returns a copy of this row without any data with a timestamp older than the one provided
*/
public Row purgeDataOlderThan(long timestamp, boolean enforceStrictLiveness);
/**
* Returns a copy of this row where all counter cells have they "local" shard marked for clearing.
*/
@ -281,6 +286,7 @@ public interface Row extends Unfiltered, Iterable<ColumnData>, IMeasurableMemory
public long unsharedHeapSizeExcludingData();
public String toString(TableMetadata metadata, boolean fullDetails);
public long unsharedHeapSize();
/**
* Apply a function to every column in a row

View File

@ -66,7 +66,7 @@ public class UnfilteredRowIteratorSerializer
{
protected static final Logger logger = LoggerFactory.getLogger(UnfilteredRowIteratorSerializer.class);
private static final int IS_EMPTY = 0x01;
public static final int IS_EMPTY = 0x01;
private static final int IS_REVERSED = 0x02;
private static final int HAS_PARTITION_DELETION = 0x04;
private static final int HAS_STATIC_ROW = 0x08;

View File

@ -23,7 +23,6 @@ import java.util.function.Predicate;
import com.google.common.collect.Iterables;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.locator.AbstractReplicationStrategy;
import org.apache.cassandra.locator.EndpointsForToken;

View File

@ -23,10 +23,12 @@ import java.util.UUID;
import java.util.stream.Collectors;
import org.apache.cassandra.db.DecoratedKey;
import org.apache.cassandra.db.marshal.TimeUUIDType;
import org.apache.cassandra.db.marshal.UUIDType;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.streaming.StreamManager;
import org.apache.cassandra.streaming.StreamingState;
import org.apache.cassandra.utils.TimeUUID;
import static org.apache.cassandra.cql3.statements.schema.CreateTableStatement.parse;
@ -35,7 +37,7 @@ public class StreamingVirtualTable extends AbstractVirtualTable
public StreamingVirtualTable(String keyspace)
{
super(parse("CREATE TABLE streaming (" +
" id uuid,\n" +
" id timeuuid,\n" +
" follower boolean,\n" +
" operation text, \n" +
" peers frozen<list<text>>,\n" +
@ -76,7 +78,7 @@ public class StreamingVirtualTable extends AbstractVirtualTable
@Override
public DataSet data(DecoratedKey partitionKey)
{
UUID id = UUIDType.instance.compose(partitionKey.getKey());
TimeUUID id = TimeUUIDType.instance.compose(partitionKey.getKey());
SimpleDataSet result = new SimpleDataSet(metadata());
StreamingState state = StreamManager.instance.getStreamingState(id);
if (state != null)

View File

@ -19,6 +19,7 @@ package org.apache.cassandra.db.virtual;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableMap;
@ -26,6 +27,7 @@ import com.google.common.collect.ImmutableMap;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.DecoratedKey;
import org.apache.cassandra.db.IMutation;
import org.apache.cassandra.db.Mutation;
import org.apache.cassandra.db.partitions.PartitionUpdate;
import org.apache.cassandra.schema.TableId;
@ -104,6 +106,12 @@ public final class VirtualMutation implements IMutation
return modifications.values();
}
@Override
public Supplier<Mutation> hintOnFailure()
{
return null;
}
@Override
public void validateIndexedColumns()
{

View File

@ -144,7 +144,7 @@ public class Murmur3Partitioner implements IPartitioner
{
static final long serialVersionUID = -5833580143318243006L;
final long token;
public final long token;
public LongToken(long token)
{
@ -204,11 +204,16 @@ public class Murmur3Partitioner implements IPartitioner
}
@Override
public Token increaseSlightly()
public LongToken increaseSlightly()
{
return new LongToken(token + 1);
}
public LongToken decreaseSlightly()
{
return new LongToken(token - 1);
}
/**
* Reverses murmur3 to find a possible 16 byte key that generates a given token
*/
@ -216,7 +221,7 @@ public class Murmur3Partitioner implements IPartitioner
public static ByteBuffer keyForToken(LongToken token)
{
ByteBuffer result = ByteBuffer.allocate(16);
long[] inv = MurmurHash.inv_hash3_x64_128(new long[] {token.token, 0L});
long[] inv = MurmurHash.inv_hash3_x64_128(new long[]{ token.token, 0L });
result.putLong(inv[0]).putLong(inv[1]).position(0);
return result;
}

View File

@ -483,7 +483,7 @@ public class Range<T extends RingPosition<T>> extends AbstractBounds<T> implemen
* Given a list of unwrapped ranges sorted by left position, return an
* equivalent list of ranges but with no overlapping ranges.
*/
private static <T extends RingPosition<T>> List<Range<T>> deoverlap(List<Range<T>> ranges)
public static <T extends RingPosition<T>> List<Range<T>> deoverlap(List<Range<T>> ranges)
{
if (ranges.isEmpty())
return ranges;

View File

@ -20,13 +20,14 @@ package org.apache.cassandra.exceptions;
import org.apache.cassandra.db.ConsistencyLevel;
import org.apache.cassandra.db.WriteType;
public class CasWriteTimeoutException extends WriteTimeoutException
{
public final int contentions;
public CasWriteTimeoutException(WriteType writeType, ConsistencyLevel consistency, int received, int blockFor, int contentions)
{
super(writeType, consistency, received, blockFor, String.format("CAS operation timed out - encountered contentions: %d", contentions));
super(writeType, consistency, received, blockFor, String.format("CAS operation timed out: received %d of %d required responses after %d contention retries", received, blockFor, contentions));
this.contentions = contentions;
}
}

View File

@ -35,7 +35,7 @@ public class RequestFailureException extends RequestExecutionException
this(code, buildErrorMessage(received, failureReasonByEndpoint), consistency, received, blockFor, failureReasonByEndpoint);
}
protected RequestFailureException(ExceptionCode code, String msg, ConsistencyLevel consistency, int received, int blockFor, Map<InetAddressAndPort, RequestFailureReason> failureReasonByEndpoint)
public RequestFailureException(ExceptionCode code, String msg, ConsistencyLevel consistency, int received, int blockFor, Map<InetAddressAndPort, RequestFailureReason> failureReasonByEndpoint)
{
super(code, buildErrorMessage(msg, failureReasonByEndpoint));
this.consistency = consistency;

View File

@ -36,8 +36,9 @@ public enum RequestFailureReason
READ_TOO_MANY_TOMBSTONES (1),
TIMEOUT (2),
INCOMPATIBLE_SCHEMA (3),
READ_SIZE (4);
READ_SIZE (4),
NODE_DOWN (5);
public static final Serializer serializer = new Serializer();
public final int code;

View File

@ -34,6 +34,7 @@ import org.apache.cassandra.io.IVersionedSerializer;
import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.utils.CassandraVersion;
import org.apache.cassandra.utils.NullableSerializer;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
@ -41,13 +42,12 @@ import static org.apache.cassandra.utils.Clock.Global.nanoTime;
* This abstraction represents both the HeartBeatState and the ApplicationState in an EndpointState
* instance. Any state for a given endpoint can be retrieved from this instance.
*/
public class EndpointState
{
protected static final Logger logger = LoggerFactory.getLogger(EndpointState.class);
public final static IVersionedSerializer<EndpointState> serializer = new EndpointStateSerializer();
public final static IVersionedSerializer<EndpointState> nullableSerializer = NullableSerializer.wrap(serializer);
private volatile HeartBeatState hbState;
private final AtomicReference<Map<ApplicationState, VersionedValue>> applicationState;
@ -255,6 +255,20 @@ public class EndpointState
{
return "EndpointState: HeartBeatState = " + hbState + ", AppStateMap = " + applicationState.get();
}
public boolean isSupersededBy(EndpointState that)
{
int thisGeneration = this.getHeartBeatState().getGeneration();
int thatGeneration = that.getHeartBeatState().getGeneration();
if (thatGeneration > thisGeneration)
return true;
if (thisGeneration > thatGeneration)
return false;
return Gossiper.getMaxEndpointStateVersion(that) > Gossiper.getMaxEndpointStateVersion(this);
}
}
class EndpointStateSerializer implements IVersionedSerializer<EndpointState>

View File

@ -43,11 +43,6 @@ public class GossipDigestAck2
{
this.epStateMap = epStateMap;
}
Map<InetAddressAndPort, EndpointState> getEndpointStateMap()
{
return epStateMap;
}
}
class GossipDigestAck2Serializer implements IVersionedSerializer<GossipDigestAck2>

View File

@ -44,7 +44,7 @@ public class GossipDigestAck2VerbHandler extends GossipVerbHandler<GossipDigestA
logger.trace("Ignoring GossipDigestAck2Message because gossip is disabled");
return;
}
Map<InetAddressAndPort, EndpointState> remoteEpStateMap = message.payload.getEndpointStateMap();
Map<InetAddressAndPort, EndpointState> remoteEpStateMap = message.payload.epStateMap;
/* Notify the Failure Detector */
Gossiper.instance.notifyFailureDetector(remoteEpStateMap);
Gossiper.instance.applyStateLocally(remoteEpStateMap);

View File

@ -610,7 +610,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean
* @param epState
* @return
*/
int getMaxEndpointStateVersion(EndpointState epState)
static int getMaxEndpointStateVersion(EndpointState epState)
{
int maxVersion = epState.getHeartBeatState().getHeartBeatVersion();
for (Map.Entry<ApplicationState, VersionedValue> state : epState.states())
@ -1144,6 +1144,15 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean
return ImmutableSet.copyOf(endpointStateMap.keySet());
}
public String getForEndpoint(InetAddressAndPort ep, ApplicationState state)
{
EndpointState epState = endpointStateMap.get(ep);
if (epState == null)
return null;
VersionedValue value = epState.getApplicationState(state);
return value == null ? null : value.value;
}
public int getEndpointCount()
{
return endpointStateMap.size();
@ -1261,7 +1270,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean
return ep1.getHeartBeatState().getGeneration() - ep2.getHeartBeatState().getGeneration();
}
void notifyFailureDetector(Map<InetAddressAndPort, EndpointState> remoteEpStateMap)
public void notifyFailureDetector(Map<InetAddressAndPort, EndpointState> remoteEpStateMap)
{
for (Entry<InetAddressAndPort, EndpointState> entry : remoteEpStateMap.entrySet())
{
@ -2143,7 +2152,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean
}
}
protected boolean isInShadowRound()
public boolean isInShadowRound()
{
return inShadowRound;
}

View File

@ -190,6 +190,14 @@ public class VersionedValue implements Comparable<VersionedValue>
Long.toString(expireTime)));
}
@VisibleForTesting
public VersionedValue left(Collection<Token> tokens, long expireTime, int generation)
{
return new VersionedValue(versionString(VersionedValue.STATUS_LEFT,
makeTokenString(tokens),
Long.toString(expireTime)), generation);
}
public VersionedValue moving(Token token)
{
return new VersionedValue(VersionedValue.STATUS_MOVING + VersionedValue.DELIMITER + partitioner.getTokenFactory().toString(token));

View File

@ -54,8 +54,8 @@ import org.apache.cassandra.dht.*;
import org.apache.cassandra.hadoop.*;
import org.apache.cassandra.utils.*;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
import static org.apache.cassandra.concurrent.ExecutorFactory.Global.executorFactory;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
/**
* Hadoop InputFormat allowing map/reduce against Cassandra rows within one ColumnFamily.

View File

@ -267,7 +267,27 @@ public interface Index
public Optional<ColumnFamilyStore> getBackingTable();
/**
* Return a task which performs a blocking flush of the index's data to persistent storage.
* Return a task which performs a blocking flush of the index's data corresponding to the provided
* base table's Memtable. This may extract any necessary data from the base table's Memtable as part of the flush.
*
* This version of the method is invoked whenever we flush the base table. If the index stores no in-memory data
* of its own, it is safe to only implement this method.
*
* @return task to be executed by the index manager to perform the flush.
*/
public default Callable<?> getBlockingFlushTask(Memtable baseCfs)
{
return getBlockingFlushTask();
}
/**
* Return a task which performs a blocking flush of any in-memory index data to persistent storage,
* independent of any flush of the base table.
*
* Note that this method is only invoked outside of normal flushes: if there is no in-memory storage
* for this index, and it only extracts data on flush from the base table's Memtable, then it is safe to
* perform no work.
*
* @return task to be executed by the index manager to perform the flush.
*/
public Callable<?> getBlockingFlushTask();

View File

@ -38,7 +38,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.concurrent.ExecutorPlus;
import org.apache.cassandra.concurrent.FutureTask;
import org.apache.cassandra.concurrent.ImmediateExecutor;
import org.apache.cassandra.concurrent.ScheduledExecutors;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.statements.schema.IndexTarget;
import org.apache.cassandra.db.*;
@ -63,6 +65,8 @@ import org.apache.cassandra.notifications.SSTableAddedNotification;
import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.schema.IndexMetadata;
import org.apache.cassandra.schema.Indexes;
import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.service.pager.SinglePartitionPager;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.ProtocolVersion;
@ -209,13 +213,15 @@ public class SecondaryIndexManager implements IndexRegistry, INotificationConsum
markIndexesBuilding(ImmutableSet.of(index), true, isNewCF);
Callable<?> initialBuildTask = null;
FutureTask<?> initialBuildTask = null;
// if the index didn't register itself, we can probably assume that no initialization needs to happen
if (indexes.containsKey(indexDef.name))
{
try
{
initialBuildTask = index.getInitializationTask();
Callable<?> call = index.getInitializationTask();
if (call != null)
initialBuildTask = new FutureTask<>(call);
}
catch (Throwable t)
{
@ -233,16 +239,20 @@ public class SecondaryIndexManager implements IndexRegistry, INotificationConsum
// otherwise run the initialization task asynchronously with a callback to mark it built or failed
final Promise<Void> initialization = new AsyncPromise<>();
asyncExecutor.submit(initialBuildTask)
.addCallback(
success -> {
markIndexBuilt(index, true);
initialization.trySuccess(null);
},
failure -> {
logAndMarkIndexesFailed(Collections.singleton(index), failure, true);
initialization.tryFailure(failure);
});
// we want to ensure we invoke this task asynchronously, so we want to add our callback before submission
// to ensure the work is not completed before we register the callback and so it gets performed by us.
// This is because Keyspace.open("system") can transitively attempt to open Keyspace.open("system")
initialBuildTask.addCallback(
success -> {
markIndexBuilt(index, true);
initialization.trySuccess(null);
},
failure -> {
logAndMarkIndexesFailed(Collections.singleton(index), failure, true);
initialization.tryFailure(failure);
}
);
asyncExecutor.execute(initialBuildTask);
return initialization;
}
@ -667,7 +677,7 @@ public class SecondaryIndexManager implements IndexRegistry, INotificationConsum
if (counter.decrementAndGet() == 0)
{
inProgressBuilds.remove(indexName);
if (!needsFullRebuild.contains(indexName) && DatabaseDescriptor.isDaemonInitialized())
if (!needsFullRebuild.contains(indexName) && DatabaseDescriptor.isDaemonInitialized() && Keyspace.isInitialized())
SystemKeyspace.setIndexBuilt(baseCfs.keyspace.getName(), indexName);
}
}
@ -798,17 +808,6 @@ public class SecondaryIndexManager implements IndexRegistry, INotificationConsum
flushIndexesBlocking(indexes, null);
}
/**
* Performs a blocking flush of all custom indexes
*/
public void flushAllNonCFSBackedIndexesBlocking()
{
executeAllBlocking(indexes.values()
.stream()
.filter(index -> !index.getBackingTable().isPresent()),
Index::getBlockingFlushTask, null);
}
/**
* Performs a blocking execution of pre-join tasks of all indexes
*/
@ -843,6 +842,18 @@ public class SecondaryIndexManager implements IndexRegistry, INotificationConsum
FBUtilities.waitOnFutures(wait);
}
/**
* Performs a blocking flush of all custom indexes
*/
public void flushAllNonCFSBackedIndexesBlocking(Memtable baseCfsMemtable)
{
executeAllBlocking(indexes.values()
.stream()
.filter(index -> !index.getBackingTable().isPresent()),
index -> index.getBlockingFlushTask(baseCfsMemtable),
null);
}
/**
* @return all indexes which are marked as built and ready to use
*/

View File

@ -28,10 +28,4 @@ public class FSDiskFullWriteError extends FSWriteError
mutationSize,
keyspace)));
}
@Override
public String toString()
{
return "FSDiskFullWriteError";
}
}

View File

@ -24,17 +24,30 @@ import org.apache.cassandra.io.util.File;
public abstract class FSError extends IOError
{
final String message;
public final String path;
public FSError(Throwable cause, File path)
{
super(cause);
this.path = path.toString();
this(null, cause, path);
}
public FSError(Throwable cause, Path path)
{
this(null, cause, path);
}
public FSError(String message, Throwable cause, File path)
{
super(cause);
this.message = message;
this.path = path.toString();
}
public FSError(String message, Throwable cause, Path path)
{
super(cause);
this.message = message;
this.path = path.toString();
}
@ -53,4 +66,10 @@ public abstract class FSError extends IOError
return null;
}
@Override
public String toString()
{
return getClass().getSimpleName() + (message != null ? ' ' + message : "") + (path != null ? " in " + path : "");
}
}

View File

@ -30,10 +30,4 @@ public class FSNoDiskAvailableForWriteError extends FSWriteError
super(new IOException(String.format("The data directories for the %s keyspace have been marked as unwritable",
keyspace)));
}
@Override
public String toString()
{
return "FSNoDiskAvailableForWriteError";
}
}

View File

@ -39,9 +39,23 @@ public class FSReadError extends FSError
this(cause, new File(path));
}
@Override
public String toString()
public FSReadError(String message, Throwable cause, Path path)
{
return "FSReadError in " + path;
super(message, cause, path);
}
public FSReadError(String message, Throwable cause, File path)
{
super(message, cause, path);
}
public FSReadError(String message, Throwable cause, String path)
{
this(message, cause, new File(path));
}
public FSReadError(String message, Throwable cause)
{
this(message, cause, new File(""));
}
}

View File

@ -44,9 +44,23 @@ public class FSWriteError extends FSError
this(cause, new File(""));
}
@Override
public String toString()
public FSWriteError(String message, Throwable cause, Path path)
{
return "FSWriteError in " + path;
super(message, cause, path);
}
public FSWriteError(String message, Throwable cause, File path)
{
super(message, cause, path);
}
public FSWriteError(String message, Throwable cause, String path)
{
this(message, cause, new File(path));
}
public FSWriteError(String message, Throwable cause)
{
this(message, cause, new File(""));
}
}

View File

@ -251,7 +251,7 @@ public class CQLSSTableWriter implements Closeable
ClientState.forInternalCalls(),
options,
insert.getTimestamp(TimeUnit.MILLISECONDS.toMicros(now), options),
(int) TimeUnit.MILLISECONDS.toSeconds(now),
options.getNowInSec((int) TimeUnit.MILLISECONDS.toSeconds(now)),
insert.getTimeToLive(options),
Collections.emptyMap());

View File

@ -281,6 +281,11 @@ public class IndexSummaryManager implements IndexSummaryManagerMBean
@VisibleForTesting
public void shutdownAndWait(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException
{
ExecutorUtils.shutdownNowAndWait(timeout, unit, executor);
if (future != null)
{
future.cancel(false);
future = null;
}
ExecutorUtils.shutdownAndWait(timeout, unit, executor);
}
}

View File

@ -34,7 +34,6 @@ import com.google.common.primitives.Longs;
import com.google.common.util.concurrent.RateLimiter;
import org.apache.cassandra.config.CassandraRelevantProperties;
import org.apache.cassandra.io.util.File;
import org.apache.cassandra.concurrent.ExecutorPlus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -18,7 +18,6 @@
package org.apache.cassandra.io.sstable.format.big;
import java.util.Collection;
import java.util.UUID;
import org.apache.cassandra.io.sstable.SSTable;
import org.apache.cassandra.schema.TableMetadata;

View File

@ -638,7 +638,7 @@ public class BigTableWriter extends SSTableWriter
protected Throwable doAbort(Throwable accumulate)
{
return indexFile.abort(accumulate);
return summary.close(indexFile.abort(accumulate));
}
@Override

View File

@ -36,7 +36,6 @@ import org.apache.cassandra.io.ISerializer;
import org.apache.cassandra.io.sstable.format.Version;
import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.EstimatedHistogram;
import org.apache.cassandra.utils.TimeUUID;

View File

@ -20,8 +20,6 @@ package org.apache.cassandra.io.util;
import java.nio.ByteBuffer;
import java.util.Optional;
import org.apache.cassandra.io.util.File;
public class ChecksummedSequentialWriter extends SequentialWriter
{
private static final SequentialWriterOption CRC_WRITER_OPTION = SequentialWriterOption.newBuilder()

View File

@ -18,7 +18,9 @@
package org.apache.cassandra.io.util;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.nio.channels.FileChannel;
import java.nio.file.*; // checkstyle: permit this import
@ -34,6 +36,7 @@ import javax.annotation.Nullable;
import com.google.common.util.concurrent.RateLimiter;
import net.openhft.chronicle.core.util.ThrowingFunction;
import org.apache.cassandra.io.FSWriteError;
import static org.apache.cassandra.io.util.PathUtils.filename;
@ -164,6 +167,16 @@ public class File implements Comparable<File>
maybeFail(delete(null, null));
}
/**
* This file will be deleted, with any failures being reported with an FSError
* @throws FSWriteError if cannot be deleted
*/
public void deleteIfExists()
{
if (path != null)
PathUtils.deleteIfExists(path);
}
/**
* This file will be deleted, obeying the provided rate limiter.
* @throws FSWriteError if cannot be deleted
@ -336,6 +349,11 @@ public class File implements Comparable<File>
return PathUtils.createFileIfNotExists(toPathForWrite());
}
public boolean createDirectoriesIfNotExists()
{
return PathUtils.createDirectoriesIfNotExists(toPathForWrite());
}
/**
* Try to create a directory at this path.
* Return true if a new directory was created at this path, and false otherwise.
@ -436,12 +454,29 @@ public class File implements Comparable<File>
PathUtils.forEachRecursive(path, path -> forEach.accept(new File(path)));
}
private static <V> ThrowingFunction<IOException, V, RuntimeException> nulls() { return ignore -> null; }
private static <V> ThrowingFunction<IOException, V, IOException> rethrow()
{
return fail -> {
if (fail == null) throw new FileNotFoundException();
throw fail;
};
}
private static <V> ThrowingFunction<IOException, V, UncheckedIOException> unchecked()
{
return fail -> {
if (fail == null) fail = new FileNotFoundException();
throw new UncheckedIOException(fail);
};
}
/**
* @return if a directory, the names of the files within; null otherwise
*/
public String[] tryListNames()
{
return tryListNames(path, Function.identity());
return tryListNames(nulls());
}
/**
@ -449,7 +484,7 @@ public class File implements Comparable<File>
*/
public String[] tryListNames(BiPredicate<File, String> filter)
{
return tryList(path, stream -> stream.map(PathUtils::filename).filter(filename -> filter.test(this, filename)), String[]::new);
return tryListNames(filter, nulls());
}
/**
@ -457,7 +492,7 @@ public class File implements Comparable<File>
*/
public File[] tryList()
{
return tryList(path, Function.identity());
return tryList(nulls());
}
/**
@ -465,7 +500,7 @@ public class File implements Comparable<File>
*/
public File[] tryList(Predicate<File> filter)
{
return tryList(path, stream -> stream.filter(filter));
return tryList(filter, nulls());
}
/**
@ -473,28 +508,148 @@ public class File implements Comparable<File>
*/
public File[] tryList(BiPredicate<File, String> filter)
{
return tryList(path, stream -> stream.filter(file -> filter.test(this, file.name())));
return tryList(filter, nulls());
}
private static String[] tryListNames(Path path, Function<Stream<File>, Stream<File>> toFiles)
/**
* @return if a directory, the names of the files within; null otherwise
*/
public String[] listNames() throws IOException
{
if (path == null)
return null;
return PathUtils.tryList(path, stream -> toFiles.apply(stream.map(File::new)).map(File::name), String[]::new);
return tryListNames(rethrow());
}
private static <T> T[] tryList(Path path, Function<Stream<Path>, Stream<T>> transformation, IntFunction<T[]> constructor)
/**
* @return if a directory, the names of the files within, filtered by the provided predicate; null otherwise
*/
public String[] listNames(BiPredicate<File, String> filter) throws IOException
{
if (path == null)
return null;
return PathUtils.tryList(path, transformation, constructor);
return tryListNames(filter, rethrow());
}
private static File[] tryList(Path path, Function<Stream<File>, Stream<File>> toFiles)
/**
* @return if a directory, the files within; null otherwise
*/
public File[] list() throws IOException
{
return tryList(rethrow());
}
/**
* @return if a directory, the files within, filtered by the provided predicate; null otherwise
*/
public File[] list(Predicate<File> filter) throws IOException
{
return tryList(filter, rethrow());
}
/**
* @return if a directory, the files within, filtered by the provided predicate; null otherwise
*/
public File[] list(BiPredicate<File, String> filter) throws IOException
{
return tryList(filter, rethrow());
}
/**
* @return if a directory, the names of the files within; null otherwise
*/
public String[] listNamesUnchecked() throws UncheckedIOException
{
return tryListNames(unchecked());
}
/**
* @return if a directory, the names of the files within, filtered by the provided predicate; null otherwise
*/
public String[] listNamesUnchecked(BiPredicate<File, String> filter) throws UncheckedIOException
{
return tryListNames(filter, unchecked());
}
/**
* @return if a directory, the files within; null otherwise
*/
public File[] listUnchecked() throws UncheckedIOException
{
return tryList(unchecked());
}
/**
* @return if a directory, the files within, filtered by the provided predicate; null otherwise
*/
public File[] listUnchecked(Predicate<File> filter) throws UncheckedIOException
{
return tryList(filter, unchecked());
}
/**
* @return if a directory, the files within, filtered by the provided predicate; throw an UncheckedIO exception otherwise
*/
public File[] listUnchecked(BiPredicate<File, String> filter) throws UncheckedIOException
{
return tryList(filter, unchecked());
}
/**
* @return if a directory, the names of the files within; null otherwise
*/
public <T extends Throwable> String[] tryListNames(ThrowingFunction<IOException, String[], T> orElse) throws T
{
return tryListNames(path, Function.identity(), orElse);
}
/**
* @return if a directory, the names of the files within, filtered by the provided predicate; null otherwise
*/
public <T extends Throwable> String[] tryListNames(BiPredicate<File, String> filter, ThrowingFunction<IOException, String[], T> orElse) throws T
{
return tryList(path, stream -> stream.map(PathUtils::filename).filter(filename -> filter.test(this, filename)), String[]::new, orElse);
}
/**
* @return if a directory, the files within; null otherwise
*/
private <T extends Throwable> File[] tryList(ThrowingFunction<IOException, File[], T> orElse) throws T
{
return tryList(path, Function.identity(), orElse);
}
/**
* @return if a directory, the files within, filtered by the provided predicate; null otherwise
*/
private <T extends Throwable> File[] tryList(Predicate<File> filter, ThrowingFunction<IOException, File[], T> orElse) throws T
{
return tryList(path, stream -> stream.filter(filter), orElse);
}
/**
* @return if a directory, the files within, filtered by the provided predicate; null otherwise
*/
private <T extends Throwable> File[] tryList(BiPredicate<File, String> filter, ThrowingFunction<IOException, File[], T> orElse) throws T
{
return tryList(path, stream -> stream.filter(file -> filter.test(this, file.name())), orElse);
}
private static <T extends Throwable> String[] tryListNames(Path path, Function<Stream<File>, Stream<File>> toFiles, ThrowingFunction<IOException, String[], T> orElse) throws T
{
if (path == null)
return null;
return PathUtils.tryList(path, stream -> toFiles.apply(stream.map(File::new)), File[]::new);
return orElse.apply(null);
return PathUtils.tryList(path, stream -> toFiles.apply(stream.map(File::new)).map(File::name), String[]::new, orElse);
}
private static <T extends Throwable, V> V[] tryList(Path path, Function<Stream<Path>, Stream<V>> transformation, IntFunction<V[]> constructor, ThrowingFunction<IOException, V[], T> orElse) throws T
{
if (path == null)
return orElse.apply(null);
return PathUtils.tryList(path, transformation, constructor, orElse);
}
private static <T extends Throwable> File[] tryList(Path path, Function<Stream<File>, Stream<File>> toFiles, ThrowingFunction<IOException, File[], T> orElse) throws T
{
if (path == null)
return orElse.apply(null);
return PathUtils.tryList(path, stream -> toFiles.apply(stream.map(File::new)), File[]::new, orElse);
}
/**

View File

@ -34,6 +34,7 @@ import com.google.common.util.concurrent.RateLimiter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.openhft.chronicle.core.util.ThrowingFunction;
import org.apache.cassandra.config.CassandraRelevantProperties;
import org.apache.cassandra.io.FSError;
import org.apache.cassandra.io.FSReadError;
@ -132,7 +133,7 @@ public final class PathUtils
}
}
public static <T> T[] tryList(Path path, Function<Stream<Path>, Stream<T>> transform, IntFunction<T[]> arrayFactory)
public static <T extends Throwable, V> V[] tryList(Path path, Function<Stream<Path>, Stream<V>> transform, IntFunction<V[]> arrayFactory, ThrowingFunction<IOException, V[], T> orElse) throws T
{
try (Stream<Path> stream = Files.list(path))
{
@ -141,7 +142,7 @@ public final class PathUtils
}
catch (IOException e)
{
return null;
return orElse.apply(e);
}
}
@ -255,6 +256,22 @@ public final class PathUtils
}
}
public static void deleteIfExists(Path file)
{
try
{
Files.delete(file);
onDeletion.accept(file);
}
catch (IOException e)
{
if (e instanceof FileNotFoundException | e instanceof NoSuchFileException)
return;
throw propagateUnchecked(e, file, true);
}
}
public static boolean tryDelete(Path file)
{
try
@ -375,9 +392,8 @@ public final class PathUtils
{
logger.trace("Could not move file {} to {}", from, to, e);
// TODO: this should be an FSError (either read or write)?
// (but for now this is maintaining legacy semantics)
throw new RuntimeException(String.format("Failed to rename %s to %s", from, to), e);
// TODO: try to decide if is read or write? for now, have assumed write
throw propagateUnchecked(String.format("Failed to rename %s to %s", from, to), e, to, true);
}
}
@ -733,6 +749,14 @@ public final class PathUtils
* propagate an IOException as an FSWriteError, FSReadError or UncheckedIOException
*/
public static RuntimeException propagateUnchecked(IOException ioe, Path path, boolean write)
{
return propagateUnchecked(null, ioe, path, write);
}
/**
* propagate an IOException as an FSWriteError, FSReadError or UncheckedIOException
*/
public static RuntimeException propagateUnchecked(String message, IOException ioe, Path path, boolean write)
{
if (ioe instanceof FileAlreadyExistsException
|| ioe instanceof NoSuchFileException
@ -741,10 +765,10 @@ public final class PathUtils
|| ioe instanceof java.nio.file.FileSystemLoopException
|| ioe instanceof java.nio.file.NotDirectoryException
|| ioe instanceof java.nio.file.NotLinkException)
throw new UncheckedIOException(ioe);
throw new UncheckedIOException(message, ioe);
if (write) throw new FSWriteError(ioe, path);
else throw new FSReadError(ioe, path);
if (write) throw new FSWriteError(message, ioe, path);
else throw new FSReadError(message, ioe, path);
}
/**

View File

@ -40,6 +40,7 @@ public class SequentialWriterOption
* </ul>
*/
public static final SequentialWriterOption DEFAULT = SequentialWriterOption.newBuilder().build();
public static final SequentialWriterOption FINISH_ON_CLOSE = SequentialWriterOption.newBuilder().finishOnClose(true).build();
private final int bufferSize;
private final BufferType bufferType;

View File

@ -32,9 +32,12 @@ import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.RandomAccess;
import java.util.Set;
import java.util.Spliterator;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
@ -45,6 +48,9 @@ import java.util.stream.Stream;
* A collection like class for Replica objects. Since the Replica class contains inetaddress, range, and
* transient replication status, basic contains and remove methods can be ambiguous. Replicas forces you
* to be explicit about what you're checking the container for, or removing from it.
*
* TODO: there's nothing about this collection that's unique to Replicas, and the implementation
* could make a useful general purpose immutable list<->set
*/
public abstract class AbstractReplicaCollection<C extends AbstractReplicaCollection<C>> implements ReplicaCollection<C>
{
@ -69,8 +75,10 @@ public abstract class AbstractReplicaCollection<C extends AbstractReplicaCollect
* A simple list with no comodification checks and immutability by default (only append permitted, and only one initial copy)
* this permits us to reduce the amount of garbage generated, by not wrapping iterators or unnecessarily copying
* and reduces the amount of indirection necessary, as well as ensuring monomorphic callsites
*
* TODO flatten into AbstractReplicaCollection?
*/
protected static class ReplicaList implements Iterable<Replica>
protected final static class ReplicaList implements Iterable<Replica>
{
private static final Replica[] EMPTY = new Replica[0];
Replica[] contents;
@ -125,7 +133,7 @@ public abstract class AbstractReplicaCollection<C extends AbstractReplicaCollect
return new ReplicaList(contents, this.begin + begin, end - begin);
}
public ReplicaList sorted(Comparator<Replica> comparator)
public ReplicaList sorted(Comparator<? super Replica> comparator)
{
Replica[] copy = Arrays.copyOfRange(contents, begin, begin + size);
Arrays.sort(copy, comparator);
@ -137,6 +145,37 @@ public abstract class AbstractReplicaCollection<C extends AbstractReplicaCollect
return Arrays.stream(contents, begin, begin + size);
}
@Override
public void forEach(Consumer<? super Replica> forEach)
{
for (int i = begin, end = begin + size ; i < end ; ++i)
forEach.accept(contents[i]);
}
/** see {@link ReplicaCollection#count(Predicate)}*/
public int count(Predicate<? super Replica> test)
{
int count = 0;
for (int i = begin, end = i + size ; i < end ; ++i)
if (test.test(contents[i]))
++count;
return count;
}
public final boolean anyMatch(Predicate<? super Replica> predicate)
{
for (int i = begin, end = i + size ; i < end ; ++i)
if (predicate.test(contents[i]))
return true;
return false;
}
@Override
public Spliterator<Replica> spliterator()
{
return Arrays.spliterator(contents, begin, begin + size);
}
// we implement our own iterator, because it is trivial to do so, and in monomorphic call sites
// will compile down to almost optimal indexed for loop
@Override
@ -163,7 +202,7 @@ public abstract class AbstractReplicaCollection<C extends AbstractReplicaCollect
// we implement our own iterator, because it is trivial to do so, and in monomorphic call sites
// will compile down to almost optimal indexed for loop
public <K> Iterator<K> transformIterator(Function<Replica, K> function)
public <K> Iterator<K> transformIterator(Function<? super Replica, ? extends K> function)
{
return new Iterator<K>()
{
@ -186,7 +225,7 @@ public abstract class AbstractReplicaCollection<C extends AbstractReplicaCollect
// we implement our own iterator, because it is trivial to do so, and in monomorphic call sites
// will compile down to almost optimal indexed for loop
// in this case, especially, it is impactful versus Iterables.limit(Iterables.filter())
private Iterator<Replica> filterIterator(Predicate<Replica> predicate, int limit)
private Iterator<Replica> filterIterator(Predicate<? super Replica> predicate, int limit)
{
return new Iterator<Replica>()
{
@ -218,6 +257,12 @@ public abstract class AbstractReplicaCollection<C extends AbstractReplicaCollect
};
}
protected <T> void forEach(Function<? super Replica, T> function, Consumer<? super T> action)
{
for (int i = begin, end = begin + size ; i < end ; ++i)
action.accept(function.apply(contents[i]));
}
@VisibleForTesting
public boolean equals(Object to)
{
@ -263,6 +308,12 @@ public abstract class AbstractReplicaCollection<C extends AbstractReplicaCollect
public boolean contains(Object o) { return containsKey(o); }
@Override
public Iterator<K> iterator() { return list.transformIterator(toKey); }
@Override
public void forEach(Consumer<? super K> action)
{
list.forEach(toKey, action);
}
}
class EntrySet extends AbstractImmutableSet<Entry<K, Replica>>
@ -318,7 +369,7 @@ public abstract class AbstractReplicaCollection<C extends AbstractReplicaCollect
public boolean containsKey(Object key)
{
Preconditions.checkNotNull(key);
return get((K)key) != null;
return get(key) != null;
}
public Replica get(Object key)
@ -368,6 +419,35 @@ public abstract class AbstractReplicaCollection<C extends AbstractReplicaCollect
}
}
static class AsList<T> extends AbstractList<T> implements RandomAccess
{
final Function<Replica, T> view;
final ReplicaList list;
AsList(Function<Replica, T> view, ReplicaList list)
{
this.view = view;
this.list = list;
}
public final T get(int index)
{
return view.apply(list.get(index));
}
public final int size()
{
return list.size;
}
@Override
public final void forEach(Consumer<? super T> forEach)
{
list.forEach(view, forEach);
}
}
protected final ReplicaList list;
AbstractReplicaCollection(ReplicaList list)
{
@ -398,40 +478,30 @@ public abstract class AbstractReplicaCollection<C extends AbstractReplicaCollect
return snapshot(subList);
}
public final <T> List<T> asList(Function<Replica, T> viewTransform)
public final <T> List<T> asList(Function<Replica, T> view)
{
return new AbstractList<T>()
{
public T get(int index)
{
return viewTransform.apply(list.get(index));
}
public int size()
{
return list.size;
}
};
return new AsList<>(view, list);
}
/** see {@link ReplicaCollection#count(Predicate)}*/
public int count(Predicate<Replica> predicate)
public final int count(Predicate<? super Replica> test)
{
int count = 0;
for (int i = 0 ; i < list.size() ; ++i)
if (predicate.test(list.get(i)))
++count;
return count;
return list.count(test);
}
public final boolean anyMatch(Predicate<? super Replica> test)
{
return list.anyMatch(test);
}
/** see {@link ReplicaCollection#filter(Predicate)}*/
public final C filter(Predicate<Replica> predicate)
public final C filter(Predicate<? super Replica> predicate)
{
return filter(predicate, Integer.MAX_VALUE);
}
/** see {@link ReplicaCollection#filter(Predicate, int)}*/
public final C filter(Predicate<Replica> predicate, int limit)
public final C filter(Predicate<? super Replica> predicate, int limit)
{
if (isEmpty())
return snapshot();
@ -475,19 +545,19 @@ public abstract class AbstractReplicaCollection<C extends AbstractReplicaCollect
}
/** see {@link ReplicaCollection#filterLazily(Predicate)}*/
public final Iterable<Replica> filterLazily(Predicate<Replica> predicate)
public final Iterable<Replica> filterLazily(Predicate<? super Replica> predicate)
{
return filterLazily(predicate, Integer.MAX_VALUE);
}
/** see {@link ReplicaCollection#filterLazily(Predicate,int)}*/
public final Iterable<Replica> filterLazily(Predicate<Replica> predicate, int limit)
public final Iterable<Replica> filterLazily(Predicate<? super Replica> predicate, int limit)
{
return () -> list.filterIterator(predicate, limit);
}
/** see {@link ReplicaCollection#sorted(Comparator)}*/
public final C sorted(Comparator<Replica> comparator)
public final C sorted(Comparator<? super Replica> comparator)
{
return snapshot(list.sorted(comparator));
}
@ -512,6 +582,11 @@ public abstract class AbstractReplicaCollection<C extends AbstractReplicaCollect
return list.iterator();
}
public final void forEach(Consumer<? super Replica> forEach)
{
list.forEach(forEach);
}
public final Stream<Replica> stream() { return list.stream(); }
/**

View File

@ -24,6 +24,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import com.google.common.base.Preconditions;
@ -32,6 +33,7 @@ import org.slf4j.LoggerFactory;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.ConsistencyLevel;
import org.apache.cassandra.db.Mutation;
import org.apache.cassandra.db.WriteType;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.RingPosition;
@ -128,17 +130,20 @@ public abstract class AbstractReplicationStrategy
*/
public abstract EndpointsForRange calculateNaturalReplicas(Token searchToken, TokenMetadata tokenMetadata);
public <T> AbstractWriteResponseHandler<T> getWriteResponseHandler(ReplicaPlan.ForTokenWrite replicaPlan,
public <T> AbstractWriteResponseHandler<T> getWriteResponseHandler(ReplicaPlan.ForWrite replicaPlan,
Runnable callback,
WriteType writeType,
Supplier<Mutation> hintOnFailure,
long queryStartNanoTime)
{
return getWriteResponseHandler(replicaPlan, callback, writeType, queryStartNanoTime, DatabaseDescriptor.getIdealConsistencyLevel());
return getWriteResponseHandler(replicaPlan, callback, writeType, hintOnFailure,
queryStartNanoTime, DatabaseDescriptor.getIdealConsistencyLevel());
}
public <T> AbstractWriteResponseHandler<T> getWriteResponseHandler(ReplicaPlan.ForTokenWrite replicaPlan,
public <T> AbstractWriteResponseHandler<T> getWriteResponseHandler(ReplicaPlan.ForWrite replicaPlan,
Runnable callback,
WriteType writeType,
Supplier<Mutation> hintOnFailure,
long queryStartNanoTime,
ConsistencyLevel idealConsistencyLevel)
{
@ -146,15 +151,15 @@ public abstract class AbstractReplicationStrategy
if (replicaPlan.consistencyLevel().isDatacenterLocal())
{
// block for in this context will be localnodes block.
resultResponseHandler = new DatacenterWriteResponseHandler<T>(replicaPlan, callback, writeType, queryStartNanoTime);
resultResponseHandler = new DatacenterWriteResponseHandler<T>(replicaPlan, callback, writeType, hintOnFailure, queryStartNanoTime);
}
else if (replicaPlan.consistencyLevel() == ConsistencyLevel.EACH_QUORUM && (this instanceof NetworkTopologyStrategy))
{
resultResponseHandler = new DatacenterSyncWriteResponseHandler<T>(replicaPlan, callback, writeType, queryStartNanoTime);
resultResponseHandler = new DatacenterSyncWriteResponseHandler<T>(replicaPlan, callback, writeType, hintOnFailure, queryStartNanoTime);
}
else
{
resultResponseHandler = new WriteResponseHandler<T>(replicaPlan, callback, writeType, queryStartNanoTime);
resultResponseHandler = new WriteResponseHandler<T>(replicaPlan, callback, writeType, hintOnFailure, queryStartNanoTime);
}
//Check if tracking the ideal consistency level is configured
@ -173,6 +178,7 @@ public abstract class AbstractReplicationStrategy
AbstractWriteResponseHandler<T> idealHandler = getWriteResponseHandler(replicaPlan.withConsistencyLevel(idealConsistencyLevel),
callback,
writeType,
hintOnFailure,
queryStartNanoTime,
idealConsistencyLevel);
resultResponseHandler.setIdealCLResponseHandler(idealHandler);

View File

@ -21,7 +21,6 @@ package org.apache.cassandra.locator;
import org.apache.cassandra.locator.ReplicaCollection.Builder.Conflict;
import org.apache.cassandra.utils.FBUtilities;
import java.util.AbstractList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@ -54,20 +53,14 @@ public abstract class Endpoints<E extends Endpoints<E>> extends AbstractReplicaC
return byEndpoint().keySet();
}
public InetAddressAndPort endpoint(int i)
{
return get(i).endpoint();
}
public List<InetAddressAndPort> endpointList()
{
return new AbstractList<InetAddressAndPort>()
{
public InetAddressAndPort get(int index)
{
return list.get(index).endpoint();
}
public int size()
{
return list.size;
}
};
return asList(Replica::endpoint);
}
public Map<InetAddressAndPort, Replica> byEndpoint()
@ -87,6 +80,11 @@ public abstract class Endpoints<E extends Endpoints<E>> extends AbstractReplicaC
replica);
}
public boolean contains(InetAddressAndPort endpoint)
{
return endpoint != null && byEndpoint().containsKey(endpoint);
}
public E withoutSelf()
{
InetAddressAndPort self = FBUtilities.getBroadcastAddressAndPort();

View File

@ -19,7 +19,11 @@
package org.apache.cassandra.locator;
import com.google.common.base.Preconditions;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.StorageService;
import java.util.Arrays;
import java.util.Collection;
@ -67,6 +71,11 @@ public class EndpointsForToken extends Endpoints<EndpointsForToken>
return new EndpointsForToken(token, newList, byEndpoint);
}
public Replica lookup(InetAddressAndPort endpoint)
{
return byEndpoint().get(endpoint);
}
public static class Builder extends EndpointsForToken implements ReplicaCollection.Builder<EndpointsForToken>
{
boolean built;
@ -146,4 +155,34 @@ public class EndpointsForToken extends Endpoints<EndpointsForToken>
if (replicas.isEmpty()) return empty(token);
return builder(token, replicas.size()).addAll(replicas).build();
}
public static EndpointsForToken natural(Keyspace keyspace, Token token)
{
return keyspace.getReplicationStrategy().getNaturalReplicasForToken(token);
}
public static EndpointsForToken natural(AbstractReplicationStrategy replicationStrategy, Token token)
{
return replicationStrategy.getNaturalReplicasForToken(token);
}
public static EndpointsForToken natural(TableMetadata table, Token token)
{
return natural(Keyspace.open(table.keyspace), token);
}
public static EndpointsForToken pending(TableMetadata table, Token token)
{
return pending(table.keyspace, token);
}
public static EndpointsForToken pending(Keyspace keyspace, Token token)
{
return pending(keyspace.getName(), token);
}
public static EndpointsForToken pending(String keyspace, Token token)
{
return StorageService.instance.getTokenMetadata().pendingEndpointsForToken(token, keyspace);
}
}

View File

@ -18,11 +18,12 @@
package org.apache.cassandra.locator;
import org.apache.cassandra.config.DatabaseDescriptor;
import java.util.function.Predicate;
public class InOurDcTester
import static org.apache.cassandra.config.DatabaseDescriptor.getEndpointSnitch;
import static org.apache.cassandra.config.DatabaseDescriptor.getLocalDataCenter;
public class InOurDc
{
private static ReplicaTester replicas;
private static EndpointTester endpoints;
@ -30,7 +31,7 @@ public class InOurDcTester
final String dc;
final IEndpointSnitch snitch;
private InOurDcTester(String dc, IEndpointSnitch snitch)
private InOurDc(String dc, IEndpointSnitch snitch)
{
this.dc = dc;
this.snitch = snitch;
@ -38,15 +39,15 @@ public class InOurDcTester
boolean stale()
{
return dc != DatabaseDescriptor.getLocalDataCenter()
|| snitch != DatabaseDescriptor.getEndpointSnitch()
return dc != getLocalDataCenter()
|| snitch != getEndpointSnitch()
// this final clause checks if somehow the snitch/localDc have got out of whack;
// presently, this is possible but very unlikely, but this check will also help
// resolve races on these global fields as well
|| !dc.equals(snitch.getLocalDatacenter());
}
private static final class ReplicaTester extends InOurDcTester implements Predicate<Replica>
private static final class ReplicaTester extends InOurDc implements Predicate<Replica>
{
private ReplicaTester(String dc, IEndpointSnitch snitch)
{
@ -60,7 +61,7 @@ public class InOurDcTester
}
}
private static final class EndpointTester extends InOurDcTester implements Predicate<InetAddressAndPort>
private static final class EndpointTester extends InOurDc implements Predicate<InetAddressAndPort>
{
private EndpointTester(String dc, IEndpointSnitch snitch)
{
@ -78,7 +79,7 @@ public class InOurDcTester
{
ReplicaTester cur = replicas;
if (cur == null || cur.stale())
replicas = cur = new ReplicaTester(DatabaseDescriptor.getLocalDataCenter(), DatabaseDescriptor.getEndpointSnitch());
replicas = cur = new ReplicaTester(getLocalDataCenter(), getEndpointSnitch());
return cur;
}
@ -86,8 +87,18 @@ public class InOurDcTester
{
EndpointTester cur = endpoints;
if (cur == null || cur.stale())
endpoints = cur = new EndpointTester(DatabaseDescriptor.getLocalDataCenter(), DatabaseDescriptor.getEndpointSnitch());
endpoints = cur = new EndpointTester(getLocalDataCenter(), getEndpointSnitch());
return cur;
}
public static boolean isInOurDc(Replica replica)
{
return replicas().test(replica);
}
public static boolean isInOurDc(InetAddressAndPort endpoint)
{
return endpoints().test(endpoint);
}
}

View File

@ -62,14 +62,14 @@ public interface ReplicaCollection<C extends ReplicaCollection<C>> extends Itera
/**
* @return the number of replicas that match the predicate
*/
public abstract int count(Predicate<Replica> predicate);
public abstract int count(Predicate<? super Replica> predicate);
/**
* @return a *eagerly constructed* copy of this collection containing the Replica that match the provided predicate.
* An effort will be made to either return ourself, or a subList, where possible.
* It is guaranteed that no changes to any upstream Builder will affect the state of the result.
*/
public abstract C filter(Predicate<Replica> predicate);
public abstract C filter(Predicate<? super Replica> predicate);
/**
* @return a *eagerly constructed* copy of this collection containing the Replica that match the provided predicate.
@ -77,18 +77,18 @@ public interface ReplicaCollection<C extends ReplicaCollection<C>> extends Itera
* It is guaranteed that no changes to any upstream Builder will affect the state of the result.
* Only the first maxSize items will be returned.
*/
public abstract C filter(Predicate<Replica> predicate, int maxSize);
public abstract C filter(Predicate<? super Replica> predicate, int maxSize);
/**
* @return a *lazily constructed* Iterable over this collection, containing the Replica that match the provided predicate.
*/
public abstract Iterable<Replica> filterLazily(Predicate<Replica> predicate);
public abstract Iterable<Replica> filterLazily(Predicate<? super Replica> predicate);
/**
* @return a *lazily constructed* Iterable over this collection, containing the Replica that match the provided predicate.
* Only the first maxSize matching items will be returned.
*/
public abstract Iterable<Replica> filterLazily(Predicate<Replica> predicate, int maxSize);
public abstract Iterable<Replica> filterLazily(Predicate<? super Replica> predicate, int maxSize);
/**
* @return an *eagerly constructed* copy of this collection containing the Replica at positions [start..end);
@ -101,7 +101,7 @@ public interface ReplicaCollection<C extends ReplicaCollection<C>> extends Itera
* @return an *eagerly constructed* copy of this collection containing the Replica re-ordered according to this comparator
* It is guaranteed that no changes to any upstream Builder will affect the state of the result.
*/
public abstract C sorted(Comparator<Replica> comparator);
public abstract C sorted(Comparator<? super Replica> comparator);
public abstract Iterator<Replica> iterator();
public abstract Stream<Replica> stream();

View File

@ -25,7 +25,6 @@ import org.apache.cassandra.db.PartitionPosition;
import org.apache.cassandra.dht.AbstractBounds;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.gms.FailureDetector;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.FBUtilities;
import java.util.Set;
@ -169,13 +168,14 @@ public abstract class ReplicaLayout<E extends Endpoints<E>>
@Override
public Token token() { return natural().token(); }
public ReplicaLayout.ForTokenWrite filter(Predicate<Replica> filter)
public ForTokenWrite filter(Predicate<Replica> filter)
{
EndpointsForToken filtered = all().filter(filter);
// AbstractReplicaCollection.filter returns itself if all elements match the filter
if (filtered == all()) return this;
if (pending().isEmpty()) return new ForTokenWrite(replicationStrategy(), filtered, pending(), filtered);
// unique by endpoint, so can for efficiency filter only on endpoint
return new ReplicaLayout.ForTokenWrite(
return new ForTokenWrite(
replicationStrategy(),
natural().keep(filtered.endpoints()),
pending().keep(filtered.endpoints()),
@ -206,8 +206,8 @@ public abstract class ReplicaLayout<E extends Endpoints<E>>
// TODO: these should be cached, not the natural replicas
// TODO: race condition to fetch these. implications??
AbstractReplicationStrategy replicationStrategy = keyspace.getReplicationStrategy();
EndpointsForToken natural = replicationStrategy.getNaturalReplicasForToken(token);
EndpointsForToken pending = StorageService.instance.getTokenMetadata().pendingEndpointsForToken(token, keyspace.getName());
EndpointsForToken natural = EndpointsForToken.natural(replicationStrategy, token);
EndpointsForToken pending = EndpointsForToken.pending(keyspace, token);
return forTokenWrite(replicationStrategy, natural, pending);
}
@ -325,7 +325,7 @@ public abstract class ReplicaLayout<E extends Endpoints<E>>
* @return the read layout for a token - this includes only live natural replicas, i.e. those that are not pending
* and not marked down by the failure detector. these are reverse sorted by the badness score of the configured snitch
*/
static ReplicaLayout.ForTokenRead forTokenReadLiveSorted(AbstractReplicationStrategy replicationStrategy, Token token)
public static ReplicaLayout.ForTokenRead forTokenReadLiveSorted(AbstractReplicationStrategy replicationStrategy, Token token)
{
EndpointsForToken replicas = replicationStrategy.getNaturalReplicasForToken(token);
replicas = DatabaseDescriptor.getEndpointSnitch().sortedByProximity(FBUtilities.getBroadcastAddressAndPort(), replicas);

View File

@ -25,69 +25,89 @@ import org.apache.cassandra.db.PartitionPosition;
import org.apache.cassandra.dht.AbstractBounds;
import java.util.function.Predicate;
import java.util.function.Supplier;
public abstract class ReplicaPlan<E extends Endpoints<E>>
public interface ReplicaPlan<E extends Endpoints<E>, P extends ReplicaPlan<E, P>>
{
protected final Keyspace keyspace;
protected final ConsistencyLevel consistencyLevel;
// The snapshot of the replication strategy when instantiating.
// It could be different than the one fetched from Keyspace later, e.g. RS altered during the query.
// Use the snapshot to calculate {@code blockFor} in order to have a consistent view of RS for the query.
protected final AbstractReplicationStrategy replicationStrategy;
Keyspace keyspace();
AbstractReplicationStrategy replicationStrategy();
ConsistencyLevel consistencyLevel();
// all nodes we will contact via any mechanism, including hints
// i.e., for:
// - reads, only live natural replicas
// ==> live.natural().subList(0, blockFor + initial speculate)
// - writes, includes all full, and any pending replicas, (and only any necessary transient ones to make up the difference)
// ==> liveAndDown.natural().filter(isFull) ++ liveAndDown.pending() ++ live.natural.filter(isTransient, req)
// - paxos, includes all live replicas (natural+pending), for this DC if SERIAL_LOCAL
// ==> live.all() (if consistencyLevel.isDCLocal(), then .filter(consistencyLevel.isLocal))
private final E contacts;
E contacts();
ReplicaPlan(Keyspace keyspace, AbstractReplicationStrategy replicationStrategy, ConsistencyLevel consistencyLevel, E contacts)
Replica lookup(InetAddressAndPort endpoint);
P withContacts(E contacts);
interface ForRead<E extends Endpoints<E>, P extends ReplicaPlan.ForRead<E, P>> extends ReplicaPlan<E, P>
{
assert contacts != null;
this.keyspace = keyspace;
this.replicationStrategy = replicationStrategy;
this.consistencyLevel = consistencyLevel;
this.contacts = contacts;
int readQuorum();
E readCandidates();
default Replica firstUncontactedCandidate(Predicate<Replica> extraPredicate)
{
return Iterables.tryFind(readCandidates(), r -> extraPredicate.test(r) && !contacts().contains(r)).orNull();
}
}
public abstract int blockFor();
abstract class AbstractReplicaPlan<E extends Endpoints<E>, P extends ReplicaPlan<E, P>> implements ReplicaPlan<E, P>
{
protected final Keyspace keyspace;
protected final ConsistencyLevel consistencyLevel;
// The snapshot of the replication strategy when instantiating.
// It could be different than the one fetched from Keyspace later, e.g. RS altered during the query.
// Use the snapshot to calculate {@code blockFor} in order to have a consistent view of RS for the query.
protected final AbstractReplicationStrategy replicationStrategy;
public E contacts() { return contacts; }
// all nodes we will contact via any mechanism, including hints
// i.e., for:
// - reads, only live natural replicas
// ==> live.natural().subList(0, blockFor + initial speculate)
// - writes, includes all full, and any pending replicas, (and only any necessary transient ones to make up the difference)
// ==> liveAndDown.natural().filter(isFull) ++ liveAndDown.pending() ++ live.natural.filter(isTransient, req)
// - paxos, includes all live replicas (natural+pending), for this DC if SERIAL_LOCAL
// ==> live.all() (if consistencyLevel.isDCLocal(), then .filter(consistencyLevel.isLocal))
private final E contacts;
// TODO: should this semantically return true if we contain the endpoint, not the exact replica?
public boolean contacts(Replica replica) { return contacts.contains(replica); }
public Keyspace keyspace() { return keyspace; }
public AbstractReplicationStrategy replicationStrategy() { return replicationStrategy; }
public ConsistencyLevel consistencyLevel() { return consistencyLevel; }
AbstractReplicaPlan(Keyspace keyspace, AbstractReplicationStrategy replicationStrategy, ConsistencyLevel consistencyLevel, E contacts)
{
assert contacts != null;
this.keyspace = keyspace;
this.replicationStrategy = replicationStrategy;
this.consistencyLevel = consistencyLevel;
this.contacts = contacts;
}
public static abstract class ForRead<E extends Endpoints<E>> extends ReplicaPlan<E>
public E contacts() { return contacts; }
public Keyspace keyspace() { return keyspace; }
public AbstractReplicationStrategy replicationStrategy() { return replicationStrategy; }
public ConsistencyLevel consistencyLevel() { return consistencyLevel; }
}
public static abstract class AbstractForRead<E extends Endpoints<E>, P extends ForRead<E, P>> extends AbstractReplicaPlan<E, P> implements ForRead<E, P>
{
// all nodes we *could* contacts; typically all natural replicas that are believed to be alive
// we will consult this collection to find uncontacted nodes we might contact if we doubt we will meet consistency level
private final E candidates;
final E candidates;
ForRead(Keyspace keyspace, AbstractReplicationStrategy replicationStrategy, ConsistencyLevel consistencyLevel, E candidates, E contacts)
AbstractForRead(Keyspace keyspace, AbstractReplicationStrategy replicationStrategy, ConsistencyLevel consistencyLevel, E candidates, E contacts)
{
super(keyspace, replicationStrategy, consistencyLevel, contacts);
this.candidates = candidates;
}
public int blockFor() { return consistencyLevel.blockFor(replicationStrategy); }
public int readQuorum() { return consistencyLevel.blockFor(replicationStrategy); }
public E candidates() { return candidates; }
public E readCandidates() { return candidates; }
public Replica firstUncontactedCandidate(Predicate<Replica> extraPredicate)
{
return Iterables.tryFind(candidates(), r -> extraPredicate.test(r) && !contacts(r)).orNull();
return Iterables.tryFind(readCandidates(), r -> extraPredicate.test(r) && !contacts().contains(r)).orNull();
}
public Replica lookup(InetAddressAndPort endpoint)
{
return candidates().byEndpoint().get(endpoint);
return readCandidates().byEndpoint().get(endpoint);
}
public String toString()
@ -96,7 +116,7 @@ public abstract class ReplicaPlan<E extends Endpoints<E>>
}
}
public static class ForTokenRead extends ForRead<EndpointsForToken>
public static class ForTokenRead extends AbstractForRead<EndpointsForToken, ForTokenRead>
{
public ForTokenRead(Keyspace keyspace,
AbstractReplicationStrategy replicationStrategy,
@ -107,13 +127,13 @@ public abstract class ReplicaPlan<E extends Endpoints<E>>
super(keyspace, replicationStrategy, consistencyLevel, candidates, contacts);
}
ForTokenRead withContact(EndpointsForToken newContact)
public ForTokenRead withContacts(EndpointsForToken newContact)
{
return new ForTokenRead(keyspace, replicationStrategy, consistencyLevel, candidates(), newContact);
return new ForTokenRead(keyspace, replicationStrategy, consistencyLevel, candidates, newContact);
}
}
public static class ForRangeRead extends ForRead<EndpointsForRange>
public static class ForRangeRead extends AbstractForRead<EndpointsForRange, ForRangeRead>
{
final AbstractBounds<PartitionPosition> range;
final int vnodeCount;
@ -138,20 +158,20 @@ public abstract class ReplicaPlan<E extends Endpoints<E>>
*/
public int vnodeCount() { return vnodeCount; }
ForRangeRead withContact(EndpointsForRange newContact)
public ForRangeRead withContacts(EndpointsForRange newContact)
{
return new ForRangeRead(keyspace, replicationStrategy, consistencyLevel, range, candidates(), newContact, vnodeCount);
return new ForRangeRead(keyspace, replicationStrategy, consistencyLevel, range, readCandidates(), newContact, vnodeCount);
}
}
public static abstract class ForWrite<E extends Endpoints<E>> extends ReplicaPlan<E>
public static class ForWrite extends AbstractReplicaPlan<EndpointsForToken, ForWrite>
{
// TODO: this is only needed because of poor isolation of concerns elsewhere - we can remove it soon, and will do so in a follow-up patch
final E pending;
final E liveAndDown;
final E live;
final EndpointsForToken pending;
final EndpointsForToken liveAndDown;
final EndpointsForToken live;
ForWrite(Keyspace keyspace, AbstractReplicationStrategy replicationStrategy, ConsistencyLevel consistencyLevel, E pending, E liveAndDown, E live, E contact)
public ForWrite(Keyspace keyspace, AbstractReplicationStrategy replicationStrategy, ConsistencyLevel consistencyLevel, EndpointsForToken pending, EndpointsForToken liveAndDown, EndpointsForToken live, EndpointsForToken contact)
{
super(keyspace, replicationStrategy, consistencyLevel, contact);
this.pending = pending;
@ -159,46 +179,43 @@ public abstract class ReplicaPlan<E extends Endpoints<E>>
this.live = live;
}
public int blockFor() { return consistencyLevel.blockForWrite(replicationStrategy, pending()); }
public int writeQuorum() { return consistencyLevel.blockForWrite(replicationStrategy, pending()); }
/** Replicas that a region of the ring is moving to; not yet ready to serve reads, but should receive writes */
public E pending() { return pending; }
public EndpointsForToken pending() { return pending; }
/** Replicas that can participate in the write - this always includes all nodes (pending and natural) in all DCs, except for paxos LOCAL_QUORUM (which is local DC only) */
public E liveAndDown() { return liveAndDown; }
public EndpointsForToken liveAndDown() { return liveAndDown; }
/** The live replicas present in liveAndDown, usually derived from FailureDetector.isReplicaAlive */
public E live() { return live; }
public EndpointsForToken live() { return live; }
/** Calculate which live endpoints we could have contacted, but chose not to */
public E liveUncontacted() { return live().filter(r -> !contacts(r)); }
public EndpointsForToken liveUncontacted() { return live().filter(r -> !contacts().contains(r)); }
/** Test liveness, consistent with the upfront analysis done for this operation (i.e. test membership of live()) */
public boolean isAlive(Replica replica) { return live.endpoints().contains(replica.endpoint()); }
public Replica lookup(InetAddressAndPort endpoint)
{
return liveAndDown().byEndpoint().get(endpoint);
}
private ForWrite copy(ConsistencyLevel newConsistencyLevel, EndpointsForToken newContact)
{
return new ForWrite(keyspace, replicationStrategy, newConsistencyLevel, pending(), liveAndDown(), live(), newContact);
}
ForWrite withConsistencyLevel(ConsistencyLevel newConsistencylevel) { return copy(newConsistencylevel, contacts()); }
public ForWrite withContacts(EndpointsForToken newContact) { return copy(consistencyLevel, newContact); }
public String toString()
{
return "ReplicaPlan.ForWrite [ CL: " + consistencyLevel + " keyspace: " + keyspace + " liveAndDown: " + liveAndDown + " live: " + live + " contacts: " + contacts() + " ]";
}
}
public static class ForTokenWrite extends ForWrite<EndpointsForToken>
{
public ForTokenWrite(Keyspace keyspace, AbstractReplicationStrategy replicationStrategy, ConsistencyLevel consistencyLevel, EndpointsForToken pending, EndpointsForToken liveAndDown, EndpointsForToken live, EndpointsForToken contact)
{
super(keyspace, replicationStrategy, consistencyLevel, pending, liveAndDown, live, contact);
}
private ReplicaPlan.ForTokenWrite copy(ConsistencyLevel newConsistencyLevel, EndpointsForToken newContact)
{
return new ReplicaPlan.ForTokenWrite(keyspace, replicationStrategy, newConsistencyLevel, pending(), liveAndDown(), live(), newContact);
}
ForTokenWrite withConsistencyLevel(ConsistencyLevel newConsistencylevel) { return copy(newConsistencylevel, contacts()); }
public ForTokenWrite withContact(EndpointsForToken newContact) { return copy(consistencyLevel, newContact); }
}
public static class ForPaxosWrite extends ForWrite<EndpointsForToken>
public static class ForPaxosWrite extends ForWrite
{
final int requiredParticipants;
@ -219,8 +236,11 @@ public abstract class ReplicaPlan<E extends Endpoints<E>>
* the constructor should be visible by the normal process of sharing data between threads (i.e. executors, etc)
* and any updates will either be seen or not seen, perhaps not promptly, but certainly not incompletely.
* The contained ReplicaPlan has only final member properties, so it cannot be seen partially initialised.
*
* TODO: there's no reason this couldn't be achieved instead by a ReplicaPlan with mutable contacts,
* simplifying the hierarchy
*/
public interface Shared<E extends Endpoints<E>, P extends ReplicaPlan<E>>
public interface Shared<E extends Endpoints<E>, P extends ReplicaPlan<E, P>> extends Supplier<P>
{
/**
* add the provided replica to this shared plan, by updating the internal reference
@ -230,29 +250,22 @@ public abstract class ReplicaPlan<E extends Endpoints<E>>
* get the shared replica plan, non-volatile (so maybe stale) but no risk of partially initialised
*/
public P get();
/**
* get the shared replica plan, non-volatile (so maybe stale) but no risk of partially initialised,
* but replace its 'contacts' with those provided
*/
public abstract P getWithContacts(E endpoints);
}
public static class SharedForTokenRead implements Shared<EndpointsForToken, ForTokenRead>
{
private ForTokenRead replicaPlan;
SharedForTokenRead(ForTokenRead replicaPlan) { this.replicaPlan = replicaPlan; }
public void addToContacts(Replica replica) { replicaPlan = replicaPlan.withContact(Endpoints.append(replicaPlan.contacts(), replica)); }
public void addToContacts(Replica replica) { replicaPlan = replicaPlan.withContacts(Endpoints.append(replicaPlan.contacts(), replica)); }
public ForTokenRead get() { return replicaPlan; }
public ForTokenRead getWithContacts(EndpointsForToken newContact) { return replicaPlan.withContact(newContact); }
}
public static class SharedForRangeRead implements Shared<EndpointsForRange, ForRangeRead>
{
private ForRangeRead replicaPlan;
SharedForRangeRead(ForRangeRead replicaPlan) { this.replicaPlan = replicaPlan; }
public void addToContacts(Replica replica) { replicaPlan = replicaPlan.withContact(Endpoints.append(replicaPlan.contacts(), replica)); }
public void addToContacts(Replica replica) { replicaPlan = replicaPlan.withContacts(Endpoints.append(replicaPlan.contacts(), replica)); }
public ForRangeRead get() { return replicaPlan; }
public ForRangeRead getWithContacts(EndpointsForRange newContact) { return replicaPlan.withContact(newContact); }
}
public static SharedForTokenRead shared(ForTokenRead replicaPlan) { return new SharedForTokenRead(replicaPlan); }

View File

@ -133,7 +133,7 @@ public class ReplicaPlans
if (logger.isTraceEnabled())
{
logger.trace(String.format("Local replicas %s are insufficient to satisfy LOCAL_QUORUM requirement of %d live replicas and %d full replicas in '%s'",
allLive.filter(InOurDcTester.replicas()), blockFor, blockForFullReplicas, DatabaseDescriptor.getLocalDataCenter()));
allLive.filter(InOurDc.replicas()), blockFor, blockForFullReplicas, DatabaseDescriptor.getLocalDataCenter()));
}
throw UnavailableException.create(consistencyLevel, blockFor, blockForFullReplicas, localLive.allReplicas(), localLive.fullReplicas());
}
@ -175,23 +175,23 @@ public class ReplicaPlans
/**
* Construct a ReplicaPlan for writing to exactly one node, with CL.ONE. This node is *assumed* to be alive.
*/
public static ReplicaPlan.ForTokenWrite forSingleReplicaWrite(Keyspace keyspace, Token token, Replica replica)
public static ReplicaPlan.ForWrite forSingleReplicaWrite(Keyspace keyspace, Token token, Replica replica)
{
EndpointsForToken one = EndpointsForToken.of(token, replica);
EndpointsForToken empty = EndpointsForToken.empty(token);
return new ReplicaPlan.ForTokenWrite(keyspace, keyspace.getReplicationStrategy(), ConsistencyLevel.ONE, empty, one, one, one);
return new ReplicaPlan.ForWrite(keyspace, keyspace.getReplicationStrategy(), ConsistencyLevel.ONE, empty, one, one, one);
}
/**
* A forwarding counter write is always sent to a single owning coordinator for the range, by the original coordinator
* (if it is not itself an owner)
*/
public static ReplicaPlan.ForTokenWrite forForwardingCounterWrite(Keyspace keyspace, Token token, Replica replica)
public static ReplicaPlan.ForWrite forForwardingCounterWrite(Keyspace keyspace, Token token, Replica replica)
{
return forSingleReplicaWrite(keyspace, token, replica);
}
public static ReplicaPlan.ForTokenWrite forLocalBatchlogWrite()
public static ReplicaPlan.ForWrite forLocalBatchlogWrite()
{
Token token = DatabaseDescriptor.getPartitioner().getMinimumToken();
Keyspace systemKeypsace = Keyspace.open(SchemaConstants.SYSTEM_KEYSPACE_NAME);
@ -211,7 +211,7 @@ public class ReplicaPlans
*
* @param isAny if batch consistency level is ANY, in which case a local node will be picked
*/
public static ReplicaPlan.ForTokenWrite forBatchlogWrite(boolean isAny) throws UnavailableException
public static ReplicaPlan.ForWrite forBatchlogWrite(boolean isAny) throws UnavailableException
{
// A single case we write not for range or token, but multiple mutations to many tokens
Token token = DatabaseDescriptor.getPartitioner().getMinimumToken();
@ -317,41 +317,41 @@ public class ReplicaPlans
return result;
}
public static ReplicaPlan.ForTokenWrite forReadRepair(Token token, ReplicaPlan.ForRead<?> readPlan) throws UnavailableException
public static ReplicaPlan.ForWrite forReadRepair(Token token, ReplicaPlan<?, ?> readPlan) throws UnavailableException
{
return forWrite(readPlan.keyspace, readPlan.consistencyLevel, token, writeReadRepair(readPlan));
return forWrite(readPlan.keyspace(), readPlan.consistencyLevel(), token, writeReadRepair(readPlan));
}
public static ReplicaPlan.ForTokenWrite forWrite(Keyspace keyspace, ConsistencyLevel consistencyLevel, Token token, Selector selector) throws UnavailableException
public static ReplicaPlan.ForWrite forWrite(Keyspace keyspace, ConsistencyLevel consistencyLevel, Token token, Selector selector) throws UnavailableException
{
return forWrite(keyspace, consistencyLevel, ReplicaLayout.forTokenWriteLiveAndDown(keyspace, token), selector);
}
@VisibleForTesting
public static ReplicaPlan.ForTokenWrite forWrite(Keyspace keyspace, ConsistencyLevel consistencyLevel, EndpointsForToken natural, EndpointsForToken pending, Predicate<Replica> isAlive, Selector selector) throws UnavailableException
public static ReplicaPlan.ForWrite forWrite(Keyspace keyspace, ConsistencyLevel consistencyLevel, EndpointsForToken natural, EndpointsForToken pending, Predicate<Replica> isAlive, Selector selector) throws UnavailableException
{
return forWrite(keyspace, consistencyLevel, ReplicaLayout.forTokenWrite(keyspace.getReplicationStrategy(), natural, pending), isAlive, selector);
}
public static ReplicaPlan.ForTokenWrite forWrite(Keyspace keyspace, ConsistencyLevel consistencyLevel, ReplicaLayout.ForTokenWrite liveAndDown, Selector selector) throws UnavailableException
public static ReplicaPlan.ForWrite forWrite(Keyspace keyspace, ConsistencyLevel consistencyLevel, ReplicaLayout.ForTokenWrite liveAndDown, Selector selector) throws UnavailableException
{
return forWrite(keyspace, consistencyLevel, liveAndDown, FailureDetector.isReplicaAlive, selector);
}
private static ReplicaPlan.ForTokenWrite forWrite(Keyspace keyspace, ConsistencyLevel consistencyLevel, ReplicaLayout.ForTokenWrite liveAndDown, Predicate<Replica> isAlive, Selector selector) throws UnavailableException
private static ReplicaPlan.ForWrite forWrite(Keyspace keyspace, ConsistencyLevel consistencyLevel, ReplicaLayout.ForTokenWrite liveAndDown, Predicate<Replica> isAlive, Selector selector) throws UnavailableException
{
ReplicaLayout.ForTokenWrite live = liveAndDown.filter(isAlive);
return forWrite(keyspace, consistencyLevel, liveAndDown, live, selector);
}
public static ReplicaPlan.ForTokenWrite forWrite(Keyspace keyspace, ConsistencyLevel consistencyLevel, ReplicaLayout.ForTokenWrite liveAndDown, ReplicaLayout.ForTokenWrite live, Selector selector) throws UnavailableException
public static ReplicaPlan.ForWrite forWrite(Keyspace keyspace, ConsistencyLevel consistencyLevel, ReplicaLayout.ForTokenWrite liveAndDown, ReplicaLayout.ForTokenWrite live, Selector selector) throws UnavailableException
{
assert liveAndDown.replicationStrategy() == live.replicationStrategy()
: "ReplicaLayout liveAndDown and live should be derived from the same replication strategy.";
AbstractReplicationStrategy replicationStrategy = liveAndDown.replicationStrategy();
EndpointsForToken contacts = selector.select(consistencyLevel, liveAndDown, live);
assureSufficientLiveReplicasForWrite(replicationStrategy, consistencyLevel, live.all(), liveAndDown.pending());
return new ReplicaPlan.ForTokenWrite(keyspace, replicationStrategy, consistencyLevel, liveAndDown.pending(), liveAndDown.all(), live.all(), contacts);
return new ReplicaPlan.ForWrite(keyspace, replicationStrategy, consistencyLevel, liveAndDown.pending(), liveAndDown.all(), live.all(), contacts);
}
public interface Selector
@ -434,7 +434,7 @@ public class ReplicaPlans
* the minimal number of nodes to meet the consistency level, and prefer nodes we contacted on read to minimise
* data transfer.
*/
public static Selector writeReadRepair(ReplicaPlan.ForRead<?> readPlan)
public static Selector writeReadRepair(ReplicaPlan<?, ?> readPlan)
{
return new Selector()
{
@ -497,7 +497,7 @@ public class ReplicaPlans
{
// TODO: we should cleanup our semantics here, as we're filtering ALL nodes to localDC which is unexpected for ReplicaPlan
// Restrict natural and pending to node in the local DC only
liveAndDown = liveAndDown.filter(InOurDcTester.replicas());
liveAndDown = liveAndDown.filter(InOurDc.replicas());
}
ReplicaLayout.ForTokenWrite live = liveAndDown.filter(FailureDetector.isReplicaAlive);
@ -526,7 +526,7 @@ public class ReplicaPlans
private static <E extends Endpoints<E>> E candidatesForRead(ConsistencyLevel consistencyLevel, E liveNaturalReplicas)
{
return consistencyLevel.isDatacenterLocal()
? liveNaturalReplicas.filter(InOurDcTester.replicas())
? liveNaturalReplicas.filter(InOurDc.replicas())
: liveNaturalReplicas;
}
@ -621,7 +621,7 @@ public class ReplicaPlans
{
// TODO: should we be asserting that the ranges are adjacent?
AbstractBounds<PartitionPosition> newRange = left.range().withNewRight(right.range().right);
EndpointsForRange mergedCandidates = left.candidates().keep(right.candidates().endpoints());
EndpointsForRange mergedCandidates = left.readCandidates().keep(right.readCandidates().endpoints());
AbstractReplicationStrategy replicationStrategy = keyspace.getReplicationStrategy();
// Check if there are enough shared endpoints for the merge to be possible.

View File

@ -78,7 +78,7 @@ public class Replicas
public static ReplicaCount countInOurDc(ReplicaCollection<?> replicas)
{
ReplicaCount count = new ReplicaCount();
Predicate<Replica> inOurDc = InOurDcTester.replicas();
Predicate<Replica> inOurDc = InOurDc.replicas();
for (Replica replica : replicas)
if (inOurDc.test(replica))
count.increment(replica);

Some files were not shown because too many files have changed in this diff Show More