Add repair streaming preview

Patch by Blake Eggleston; Reviewed by Marcus Eriksson for CASSANDRA-13257
This commit is contained in:
Blake Eggleston 2017-02-23 09:35:04 -08:00
parent 1e20d95130
commit 4cfaf855c4
63 changed files with 1156 additions and 190 deletions

View File

@ -1,4 +1,5 @@
4.0
* Add repair streaming preview (CASSANDRA-13257)
* Cleanup isIncremental/repairedAt usage (CASSANDRA-13430)
* Change protocol to allow sending key space independent of query string (CASSANDRA-10145)
* Make gc_log and gc_warn settable at runtime (CASSANDRA-12661)

View File

@ -23,6 +23,8 @@ New features
- Support for arithmetic operations between `timestamp`/`date` and `duration` has been added.
See CASSANDRA-11936
- Support for arithmetic operations on number has been added. See CASSANDRA-11935
- Preview expected streaming required for a repair (nodetool repair --preview), and validate the
consistency of repaired data between nodes (nodetool repair --validate). See CASSANDRA-13257
Upgrading
---------
@ -46,7 +48,9 @@ Upgrading
data to be inconsistent between nodes. The fix changes the behavior of both
full and incremental repairs. For full repairs, data is no longer marked
repaired. For incremental repairs, anticompaction is run at the beginning
of the repair, instead of at the end.
of the repair, instead of at the end. If incremental repair was being used
prior to upgrading, a full repair should be run after upgrading to resolve
any inconsistencies.
- Config option index_interval has been removed (it was deprecated since 2.0)
- Deprecated repair JMX APIs are removed.

View File

@ -27,7 +27,7 @@ nodetool = "../bin/nodetool"
outdir = "source/tools/nodetool"
helpfilename = outdir + "/nodetool.txt"
command_re = re.compile("( )([_a-z]+)")
commandRSTContent = ".. _{0}\n\n{0}\n-------\n\nUsage\n---------\n\n.. include:: {0}.txt\n :literal:\n\n"
commandRSTContent = ".. _nodetool_{0}:\n\n{0}\n-------\n\nUsage\n---------\n\n.. include:: {0}.txt\n :literal:\n\n"
# create the documentation directory
if not os.path.exists(outdir):

View File

@ -16,7 +16,92 @@
.. highlight:: none
.. _repair:
Repair
------
.. todo:: todo
Cassandra is designed to remain available if one of it's nodes is down or unreachable. However, when a node is down or
unreachable, it needs to eventually discover the writes it missed. Hints attempt to inform a node of missed writes, but
are a best effort, and aren't guaranteed to inform a node of 100% of the writes it missed. These inconsistencies can
eventually result in data loss as nodes are replaced or tombstones expire.
These inconsistencies are fixed with the repair process. Repair synchronizes the data between nodes by comparing their
respective datasets for their common token ranges, and streaming the differences for any out of sync sections between
the nodes. It compares the data with merkle trees, which are a hierarchy of hashes.
Incremental and Full Repairs
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
There are 2 types of repairs: full repairs, and incremental repairs. Full repairs operate over all of the data in the
token range being repaired. Incremental repairs only repair data that's been written since the previous incremental repair.
Incremental repairs are the default repair type, and if run regularly, can significantly reduce the time and io cost of
performing a repair. However, it's important to understand that once an incremental repair marks data as repaired, it won't
try to repair it again. This is fine for syncing up missed writes, but it doesn't protect against things like disk corruption,
data loss by operator error, or bugs in Cassandra. For this reason, full repairs should still be run occasionally.
Usage and Best Practices
^^^^^^^^^^^^^^^^^^^^^^^^
Since repair can result in a lot of disk and network io, it's not run automatically by Cassandra. It is run by the operator
via nodetool.
Incremental repair is the default and is run with the following command:
::
nodetool repair
A full repair can be run with the following command:
::
nodetool repair --full
Additionally, repair can be run on a single keyspace:
::
nodetool repair [options] <keyspace_name>
Or even on specific tables:
::
nodetool repair [options] <keyspace_name> <table1> <table2>
The repair command only repairs token ranges on the node being repaired, it doesn't repair the whole cluster. By default, repair
will operate on all token ranges replicated by the node you're running repair on, which will cause duplicate work if you run it
on every node. The ``-pr`` flag will only repair the "primary" ranges on a node, so you can repair your entire cluster by running
``nodetool repair -pr`` on each node in a single datacenter.
The specific frequency of repair that's right for your cluster, of course, depends on several factors. However, if you're
just starting out and looking for somewhere to start, running an incremental repair every 1-3 days, and a full repair every
1-3 weeks is probably reasonable. If you don't want to run incremental repairs, a full repair every 5 days is a good place
to start.
At a minimum, repair should be run often enough that the gc grace period never expires on unrepaired data. Otherwise, deleted
data could reappear. With a default gc grace period of 10 days, repairing every node in your cluster at least once every 7 days
will prevent this, while providing enough slack to allow for delays.
Other Options
^^^^^^^^^^^^^
``-pr, --partitioner-range``
Restricts repair to the 'primary' token ranges of the node being repaired. A primary range is just a token range for
which a node is the first replica in the ring.
``-prv, --preview``
Estimates the amount of streaming that would occur for the given repair command. This builds the merkle trees, and prints
the expected streaming activity, but does not actually do any streaming. By default, incremental repairs are estimated,
add the ``--full`` flag to estimate a full repair.
``-vd, --validate``
Verifies that the repaired data is the same across all nodes. Similiar to ``--preview``, this builds and compares merkle
trees of repaired data, but doesn't do any streaming. This is useful for troubleshooting. If this shows that the repaired
data is out of sync, a full repair should be run.
.. seealso::
:ref:`nodetool repair docs <nodetool_repair>`

View File

@ -29,6 +29,7 @@ import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.TabularData;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.*;
import com.google.common.util.concurrent.*;
import org.slf4j.Logger;
@ -69,6 +70,7 @@ import org.apache.cassandra.repair.Validator;
import org.apache.cassandra.schema.CompactionParams.TombstoneOption;
import org.apache.cassandra.service.ActiveRepairService;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.utils.*;
import org.apache.cassandra.utils.Throwables;
import org.apache.cassandra.utils.concurrent.Refs;
@ -611,8 +613,11 @@ public class CompactionManager implements CompactionManagerMBean
UUID pendingRepair,
UUID parentRepairSession) throws InterruptedException, IOException
{
logger.info("[repair #{}] Starting anticompaction for {}.{} on {}/{} sstables", parentRepairSession, cfs.keyspace.getName(), cfs.getTableName(), validatedForRepair.size(), cfs.getLiveSSTables());
logger.trace("[repair #{}] Starting anticompaction for ranges {}", parentRepairSession, ranges);
ActiveRepairService.ParentRepairSession prs = ActiveRepairService.instance.getParentRepairSession(parentRepairSession);
Preconditions.checkArgument(!prs.isPreview(), "Cannot anticompact for previews");
logger.info("{} Starting anticompaction for {}.{} on {}/{} sstables", PreviewKind.NONE.logPrefix(parentRepairSession), cfs.keyspace.getName(), cfs.getTableName(), validatedForRepair.size(), cfs.getLiveSSTables());
logger.trace("{} Starting anticompaction for ranges {}", PreviewKind.NONE.logPrefix(parentRepairSession), ranges);
Set<SSTableReader> sstables = new HashSet<>(validatedForRepair);
Set<SSTableReader> mutatedRepairStatuses = new HashSet<>();
// we should only notify that repair status changed if it actually did:
@ -640,7 +645,7 @@ public class CompactionManager implements CompactionManagerMBean
{
if (r.contains(sstableRange))
{
logger.info("[repair #{}] SSTable {} fully contained in range {}, mutating repairedAt instead of anticompacting", parentRepairSession, sstable, r);
logger.info("{} SSTable {} fully contained in range {}, mutating repairedAt instead of anticompacting", PreviewKind.NONE.logPrefix(parentRepairSession), sstable, r);
sstable.descriptor.getMetadataSerializer().mutateRepaired(sstable.descriptor, repairedAt, pendingRepair);
sstable.reloadSSTableMetadata();
mutatedRepairStatuses.add(sstable);
@ -652,14 +657,14 @@ public class CompactionManager implements CompactionManagerMBean
}
else if (sstableRange.intersects(r))
{
logger.info("[repair #{}] SSTable {} ({}) will be anticompacted on range {}", parentRepairSession, sstable, sstableRange, r);
logger.info("{} SSTable {} ({}) will be anticompacted on range {}", PreviewKind.NONE.logPrefix(parentRepairSession), sstable, sstableRange, r);
shouldAnticompact = true;
}
}
if (!shouldAnticompact)
{
logger.info("[repair #{}] SSTable {} ({}) does not intersect repaired ranges {}, not touching repairedAt.", parentRepairSession, sstable, sstableRange, normalizedRanges);
logger.info("{} SSTable {} ({}) does not intersect repaired ranges {}, not touching repairedAt.", PreviewKind.NONE.logPrefix(parentRepairSession), sstable, sstableRange, normalizedRanges);
nonAnticompacting.add(sstable);
sstableIterator.remove();
}
@ -678,7 +683,7 @@ public class CompactionManager implements CompactionManagerMBean
txn.close();
}
logger.info("[repair #{}] Completed anticompaction successfully", parentRepairSession);
logger.info("{} Completed anticompaction successfully", PreviewKind.NONE.logPrefix(parentRepairSession));
}
public void performMaximal(final ColumnFamilyStore cfStore, boolean splitOutput)
@ -1427,7 +1432,12 @@ public class CompactionManager implements CompactionManagerMBean
Set<SSTableReader> sstablesToValidate = new HashSet<>();
com.google.common.base.Predicate<SSTableReader> predicate;
if (validator.isConsistent)
if (prs.isPreview())
{
predicate = prs.getPreviewPredicate();
}
else if (validator.isConsistent)
{
predicate = s -> validator.desc.parentSessionId.equals(s.getSSTableMetadata().pendingRepair);
}

View File

@ -39,6 +39,7 @@ import org.apache.cassandra.locator.AbstractReplicationStrategy;
import org.apache.cassandra.locator.IEndpointSnitch;
import org.apache.cassandra.locator.TokenMetadata;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.streaming.StreamPlan;
import org.apache.cassandra.streaming.StreamResultFuture;
import org.apache.cassandra.streaming.StreamOperation;
@ -156,7 +157,7 @@ public class RangeStreamer
this.tokens = tokens;
this.address = address;
this.description = streamOperation.getDescription();
this.streamPlan = new StreamPlan(streamOperation, connectionsPerHost, true, connectSequentially, null);
this.streamPlan = new StreamPlan(streamOperation, connectionsPerHost, true, connectSequentially, null, PreviewKind.NONE);
this.useStrictConsistency = useStrictConsistency;
this.snitch = snitch;
this.stateStore = stateStore;

View File

@ -18,6 +18,7 @@
package org.apache.cassandra.exceptions;
import org.apache.cassandra.repair.RepairJobDesc;
import org.apache.cassandra.streaming.PreviewKind;
/**
* Exception thrown during repair
@ -25,22 +26,23 @@ import org.apache.cassandra.repair.RepairJobDesc;
public class RepairException extends Exception
{
public final RepairJobDesc desc;
public final PreviewKind previewKind;
public RepairException(RepairJobDesc desc, String message)
{
super(message);
this.desc = desc;
this(desc, null, message);
}
public RepairException(RepairJobDesc desc, String message, Throwable cause)
public RepairException(RepairJobDesc desc, PreviewKind previewKind, String message)
{
super(message, cause);
super(message);
this.desc = desc;
this.previewKind = previewKind != null ? previewKind : PreviewKind.NONE;
}
@Override
public String getMessage()
{
return desc + " " + super.getMessage();
return desc.toString(previewKind) + ' ' + super.getMessage();
}
}

View File

@ -159,7 +159,7 @@ public class SSTableLoader implements StreamEventHandler
client.init(keyspace);
outputHandler.output("Established connection to initial hosts");
StreamPlan plan = new StreamPlan(StreamOperation.BULK_LOAD, connectionsPerHost, false, false, null).connectionFactory(client.getConnectionFactory());
StreamPlan plan = new StreamPlan(StreamOperation.BULK_LOAD, connectionsPerHost, false, false, null, PreviewKind.NONE).connectionFactory(client.getConnectionFactory());
Map<InetAddress, Collection<Range<Token>>> endpointToRanges = client.getEndpointToRangesMap();
openSSTables(endpointToRanges);

View File

@ -73,7 +73,7 @@ public class IncomingStreamingConnection extends Thread implements Closeable
// The receiving side distinguish two connections by looking at StreamInitMessage#isForOutgoing.
// Note: we cannot use the same socket for incoming and outgoing streams because we want to
// parallelize said streams and the socket is blocking, so we might deadlock.
StreamResultFuture.initReceivingSide(init.sessionIndex, init.planId, init.streamOperation, init.from, this, init.isForOutgoing, version, init.keepSSTableLevel, init.pendingRepair);
StreamResultFuture.initReceivingSide(init.sessionIndex, init.planId, init.streamOperation, init.from, this, init.isForOutgoing, version, init.keepSSTableLevel, init.pendingRepair, init.previewKind);
}
catch (Throwable t)
{

View File

@ -29,6 +29,7 @@ import org.apache.cassandra.db.SystemKeyspace;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.service.ActiveRepairService;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.streaming.ProgressInfo;
import org.apache.cassandra.streaming.StreamEvent;
import org.apache.cassandra.streaming.StreamEventHandler;
@ -38,6 +39,8 @@ import org.apache.cassandra.streaming.StreamOperation;
import org.apache.cassandra.tracing.TraceState;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.MerkleTree;
import org.apache.cassandra.utils.MerkleTrees;
/**
* LocalSyncTask performs streaming between local(coordinator) node and remote replica.
@ -51,9 +54,9 @@ public class LocalSyncTask extends SyncTask implements StreamEventHandler
private final UUID pendingRepair;
private final boolean pullRepair;
public LocalSyncTask(RepairJobDesc desc, TreeResponse r1, TreeResponse r2, UUID pendingRepair, boolean pullRepair)
public LocalSyncTask(RepairJobDesc desc, TreeResponse r1, TreeResponse r2, UUID pendingRepair, boolean pullRepair, PreviewKind previewKind)
{
super(desc, r1, r2);
super(desc, r1, r2, previewKind);
this.pendingRepair = pendingRepair;
this.pullRepair = pullRepair;
}
@ -62,7 +65,7 @@ public class LocalSyncTask extends SyncTask implements StreamEventHandler
@VisibleForTesting
StreamPlan createStreamPlan(InetAddress dst, InetAddress preferred, List<Range<Token>> differences)
{
StreamPlan plan = new StreamPlan(StreamOperation.REPAIR, 1, false, false, pendingRepair)
StreamPlan plan = new StreamPlan(StreamOperation.REPAIR, 1, false, false, pendingRepair, previewKind)
.listeners(this)
.flushBeforeTransfer(pendingRepair == null)
.requestRanges(dst, preferred, desc.keyspace, differences, desc.columnFamily); // request ranges from the remote node
@ -79,6 +82,7 @@ public class LocalSyncTask extends SyncTask implements StreamEventHandler
* Starts sending/receiving our list of differences to/from the remote endpoint: creates a callback
* that will be called out of band once the streams complete.
*/
@Override
protected void startSync(List<Range<Token>> differences)
{
InetAddress local = FBUtilities.getBroadcastAddress();
@ -87,7 +91,7 @@ public class LocalSyncTask extends SyncTask implements StreamEventHandler
InetAddress preferred = SystemKeyspace.getPreferredIP(dst);
String message = String.format("Performing streaming repair of %d ranges with %s", differences.size(), dst);
logger.info("[repair #{}] {}", desc.sessionId, message);
logger.info("{} {}", previewKind.logPrefix(desc.sessionId), message);
Tracing.traceRepair(message);
createStreamPlan(dst, preferred, differences).execute();
@ -122,9 +126,9 @@ public class LocalSyncTask extends SyncTask implements StreamEventHandler
public void onSuccess(StreamState result)
{
String message = String.format("Sync complete using session %s between %s and %s on %s", desc.sessionId, r1.endpoint, r2.endpoint, desc.columnFamily);
logger.info("[repair #{}] {}", desc.sessionId, message);
logger.info("{} {}", previewKind.logPrefix(desc.sessionId), message);
Tracing.traceRepair(message);
set(stat);
set(stat.withSummaries(result.createSummaries()));
}
public void onFailure(Throwable t)

View File

@ -28,6 +28,8 @@ import org.apache.cassandra.dht.Token;
import org.apache.cassandra.exceptions.RepairException;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.repair.messages.SyncRequest;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.streaming.SessionSummary;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.utils.FBUtilities;
@ -41,30 +43,31 @@ public class RemoteSyncTask extends SyncTask
{
private static final Logger logger = LoggerFactory.getLogger(RemoteSyncTask.class);
public RemoteSyncTask(RepairJobDesc desc, TreeResponse r1, TreeResponse r2)
public RemoteSyncTask(RepairJobDesc desc, TreeResponse r1, TreeResponse r2, PreviewKind previewKind)
{
super(desc, r1, r2);
super(desc, r1, r2, previewKind);
}
@Override
protected void startSync(List<Range<Token>> differences)
{
InetAddress local = FBUtilities.getBroadcastAddress();
SyncRequest request = new SyncRequest(desc, local, r1.endpoint, r2.endpoint, differences);
SyncRequest request = new SyncRequest(desc, local, r1.endpoint, r2.endpoint, differences, previewKind);
String message = String.format("Forwarding streaming repair of %d ranges to %s (to be streamed with %s)", request.ranges.size(), request.src, request.dst);
logger.info("[repair #{}] {}", desc.sessionId, message);
logger.info("{} {}", previewKind.logPrefix(desc.sessionId), message);
Tracing.traceRepair(message);
MessagingService.instance().sendOneWay(request.createMessage(), request.src);
}
public void syncComplete(boolean success)
public void syncComplete(boolean success, List<SessionSummary> summaries)
{
if (success)
{
set(stat);
set(stat.withSummaries(summaries));
}
else
{
setException(new RepairException(desc, String.format("Sync failed between %s and %s", r1.endpoint, r2.endpoint)));
setException(new RepairException(desc, previewKind, String.format("Sync failed between %s and %s", r1.endpoint, r2.endpoint)));
}
}
}

View File

@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.Pair;
@ -42,6 +43,7 @@ public class RepairJob extends AbstractFuture<RepairResult> implements Runnable
private final RepairParallelism parallelismDegree;
private final ListeningExecutorService taskExecutor;
private final boolean isConsistent;
private final PreviewKind previewKind;
/**
* Create repair job to run on specific columnfamily
@ -49,13 +51,14 @@ public class RepairJob extends AbstractFuture<RepairResult> implements Runnable
* @param session RepairSession that this RepairJob belongs
* @param columnFamily name of the ColumnFamily to repair
*/
public RepairJob(RepairSession session, String columnFamily, boolean isConsistent)
public RepairJob(RepairSession session, String columnFamily, boolean isConsistent, PreviewKind previewKind)
{
this.session = session;
this.desc = new RepairJobDesc(session.parentRepairSession, session.getId(), session.keyspace, columnFamily, session.getRanges());
this.taskExecutor = session.taskExecutor;
this.parallelismDegree = session.parallelismDegree;
this.isConsistent = isConsistent;
this.previewKind = previewKind;
}
/**
@ -128,11 +131,11 @@ public class RepairJob extends AbstractFuture<RepairResult> implements Runnable
SyncTask task;
if (r1.endpoint.equals(local) || r2.endpoint.equals(local))
{
task = new LocalSyncTask(desc, r1, r2, isConsistent ? desc.parentSessionId : null, session.pullRepair);
task = new LocalSyncTask(desc, r1, r2, isConsistent ? desc.parentSessionId : null, session.pullRepair, session.previewKind);
}
else
{
task = new RemoteSyncTask(desc, r1, r2);
task = new RemoteSyncTask(desc, r1, r2, session.previewKind);
// RemoteSyncTask expects SyncComplete message sent back.
// Register task to RepairSession to receive response.
session.waitForSync(Pair.create(desc, new NodePair(r1.endpoint, r2.endpoint)), (RemoteSyncTask) task);
@ -150,8 +153,11 @@ public class RepairJob extends AbstractFuture<RepairResult> implements Runnable
{
public void onSuccess(List<SyncStat> stats)
{
logger.info("[repair #{}] {} is fully synced", session.getId(), desc.columnFamily);
SystemDistributedKeyspace.successfulRepairJob(session.getId(), desc.keyspace, desc.columnFamily);
if (!previewKind.isPreview())
{
logger.info("{} {} is fully synced", previewKind.logPrefix(session.getId()), desc.columnFamily);
SystemDistributedKeyspace.successfulRepairJob(session.getId(), desc.keyspace, desc.columnFamily);
}
set(new RepairResult(desc, stats));
}
@ -160,8 +166,11 @@ public class RepairJob extends AbstractFuture<RepairResult> implements Runnable
*/
public void onFailure(Throwable t)
{
logger.warn("[repair #{}] {} sync failed", session.getId(), desc.columnFamily);
SystemDistributedKeyspace.failedRepairJob(session.getId(), desc.keyspace, desc.columnFamily, t);
if (!previewKind.isPreview())
{
logger.warn("{} {} sync failed", previewKind.logPrefix(session.getId()), desc.columnFamily);
SystemDistributedKeyspace.failedRepairJob(session.getId(), desc.keyspace, desc.columnFamily, t);
}
setException(t);
}
}, taskExecutor);
@ -179,13 +188,13 @@ public class RepairJob extends AbstractFuture<RepairResult> implements Runnable
private ListenableFuture<List<TreeResponse>> sendValidationRequest(Collection<InetAddress> endpoints)
{
String message = String.format("Requesting merkle trees for %s (to %s)", desc.columnFamily, endpoints);
logger.info("[repair #{}] {}", desc.sessionId, message);
logger.info("{} {}", previewKind.logPrefix(desc.sessionId), message);
Tracing.traceRepair(message);
int gcBefore = Keyspace.open(desc.keyspace).getColumnFamilyStore(desc.columnFamily).gcBefore(FBUtilities.nowInSeconds());
List<ListenableFuture<TreeResponse>> tasks = new ArrayList<>(endpoints.size());
for (InetAddress endpoint : endpoints)
{
ValidationTask task = new ValidationTask(desc, endpoint, gcBefore);
ValidationTask task = new ValidationTask(desc, endpoint, gcBefore, previewKind);
tasks.add(task);
session.waitForValidation(Pair.create(desc, endpoint), task);
taskExecutor.execute(task);
@ -199,14 +208,14 @@ public class RepairJob extends AbstractFuture<RepairResult> implements Runnable
private ListenableFuture<List<TreeResponse>> sendSequentialValidationRequest(Collection<InetAddress> endpoints)
{
String message = String.format("Requesting merkle trees for %s (to %s)", desc.columnFamily, endpoints);
logger.info("[repair #{}] {}", desc.sessionId, message);
logger.info("{} {}", previewKind.logPrefix(desc.sessionId), message);
Tracing.traceRepair(message);
int gcBefore = Keyspace.open(desc.keyspace).getColumnFamilyStore(desc.columnFamily).gcBefore(FBUtilities.nowInSeconds());
List<ListenableFuture<TreeResponse>> tasks = new ArrayList<>(endpoints.size());
Queue<InetAddress> requests = new LinkedList<>(endpoints);
InetAddress address = requests.poll();
ValidationTask firstTask = new ValidationTask(desc, address, gcBefore);
ValidationTask firstTask = new ValidationTask(desc, address, gcBefore, previewKind);
logger.info("Validating {}", address);
session.waitForValidation(Pair.create(desc, address), firstTask);
tasks.add(firstTask);
@ -214,7 +223,7 @@ public class RepairJob extends AbstractFuture<RepairResult> implements Runnable
while (requests.size() > 0)
{
final InetAddress nextAddress = requests.poll();
final ValidationTask nextTask = new ValidationTask(desc, nextAddress, gcBefore);
final ValidationTask nextTask = new ValidationTask(desc, nextAddress, gcBefore, previewKind);
tasks.add(nextTask);
Futures.addCallback(currentTask, new FutureCallback<TreeResponse>()
{
@ -241,7 +250,7 @@ public class RepairJob extends AbstractFuture<RepairResult> implements Runnable
private ListenableFuture<List<TreeResponse>> sendDCAwareValidationRequest(Collection<InetAddress> endpoints)
{
String message = String.format("Requesting merkle trees for %s (to %s)", desc.columnFamily, endpoints);
logger.info("[repair #{}] {}", desc.sessionId, message);
logger.info("{} {}", previewKind.logPrefix(desc.sessionId), message);
Tracing.traceRepair(message);
int gcBefore = Keyspace.open(desc.keyspace).getColumnFamilyStore(desc.columnFamily).gcBefore(FBUtilities.nowInSeconds());
List<ListenableFuture<TreeResponse>> tasks = new ArrayList<>(endpoints.size());
@ -263,7 +272,7 @@ public class RepairJob extends AbstractFuture<RepairResult> implements Runnable
{
Queue<InetAddress> requests = entry.getValue();
InetAddress address = requests.poll();
ValidationTask firstTask = new ValidationTask(desc, address, gcBefore);
ValidationTask firstTask = new ValidationTask(desc, address, gcBefore, previewKind);
logger.info("Validating {}", address);
session.waitForValidation(Pair.create(desc, address), firstTask);
tasks.add(firstTask);
@ -271,7 +280,7 @@ public class RepairJob extends AbstractFuture<RepairResult> implements Runnable
while (requests.size() > 0)
{
final InetAddress nextAddress = requests.poll();
final ValidationTask nextTask = new ValidationTask(desc, nextAddress, gcBefore);
final ValidationTask nextTask = new ValidationTask(desc, nextAddress, gcBefore, previewKind);
tasks.add(nextTask);
Futures.addCallback(currentTask, new FutureCallback<TreeResponse>()
{

View File

@ -32,6 +32,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.net.MessagingService;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.utils.UUIDSerializer;
/**
@ -66,6 +67,11 @@ public class RepairJobDesc
return "[repair #" + sessionId + " on " + keyspace + "/" + columnFamily + ", " + ranges + "]";
}
public String toString(PreviewKind previewKind)
{
return '[' + previewKind.logPrefix() + " #" + sessionId + " on " + keyspace + "/" + columnFamily + ", " + ranges + "]";
}
@Override
public boolean equals(Object o)
{

View File

@ -35,6 +35,7 @@ import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.repair.messages.*;
import org.apache.cassandra.schema.TableId;
import org.apache.cassandra.service.ActiveRepairService;
import org.apache.cassandra.streaming.PreviewKind;
/**
* Handles all repair related message.
@ -50,6 +51,12 @@ public class RepairMessageVerbHandler implements IVerbHandler<RepairMessage>
return ActiveRepairService.instance.consistent.local.isSessionInProgress(sessionID);
}
private PreviewKind previewKind(UUID sessionID)
{
ActiveRepairService.ParentRepairSession prs = ActiveRepairService.instance.getParentRepairSession(sessionID);
return prs != null ? prs.previewKind : PreviewKind.NONE;
}
public void doVerb(final MessageIn<RepairMessage> message, final int id)
{
// TODO add cancel/interrupt message
@ -79,7 +86,8 @@ public class RepairMessageVerbHandler implements IVerbHandler<RepairMessage>
prepareMessage.ranges,
prepareMessage.isIncremental,
prepareMessage.timestamp,
prepareMessage.isGlobal);
prepareMessage.isGlobal,
prepareMessage.previewKind);
MessagingService.instance().sendReply(new MessageOut(MessagingService.Verb.INTERNAL_RESPONSE), id, message.from);
break;
@ -127,7 +135,8 @@ public class RepairMessageVerbHandler implements IVerbHandler<RepairMessage>
}
ActiveRepairService.instance.consistent.local.maybeSetRepairing(desc.parentSessionId);
Validator validator = new Validator(desc, message.from, validationRequest.gcBefore, isConsistent(desc.parentSessionId));
Validator validator = new Validator(desc, message.from, validationRequest.gcBefore,
isConsistent(desc.parentSessionId), previewKind(desc.parentSessionId));
CompactionManager.instance.submitValidation(store, validator);
break;
@ -135,7 +144,7 @@ public class RepairMessageVerbHandler implements IVerbHandler<RepairMessage>
// forwarded sync request
SyncRequest request = (SyncRequest) message.payload;
logger.debug("Syncing {}", request);
StreamingRepairTask task = new StreamingRepairTask(desc, request, isConsistent(desc.parentSessionId) ? desc.parentSessionId : null);
StreamingRepairTask task = new StreamingRepairTask(desc, request, isConsistent(desc.parentSessionId) ? desc.parentSessionId : null, request.previewKind);
task.run();
break;

View File

@ -36,6 +36,7 @@ import org.slf4j.LoggerFactory;
import org.apache.cassandra.concurrent.JMXConfigurableThreadPoolExecutor;
import org.apache.cassandra.concurrent.NamedThreadFactory;
import org.apache.cassandra.repair.consistent.SyncStatSummary;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.cql3.QueryOptions;
import org.apache.cassandra.cql3.QueryProcessor;
@ -50,6 +51,7 @@ import org.apache.cassandra.repair.messages.RepairOption;
import org.apache.cassandra.service.ActiveRepairService;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.tracing.TraceKeyspace;
import org.apache.cassandra.tracing.TraceState;
import org.apache.cassandra.tracing.Tracing;
@ -218,7 +220,11 @@ public class RepairRunnable extends WrappedRunnable implements ProgressEventNoti
cfnames[i] = columnFamilyStores.get(i).name;
}
SystemDistributedKeyspace.startParentRepair(parentSession, keyspace, cfnames, options);
if (!options.isPreview())
{
SystemDistributedKeyspace.startParentRepair(parentSession, keyspace, cfnames, options);
}
long repairedAt;
try
{
@ -228,12 +234,19 @@ public class RepairRunnable extends WrappedRunnable implements ProgressEventNoti
}
catch (Throwable t)
{
SystemDistributedKeyspace.failParentRepair(parentSession, t);
if (!options.isPreview())
{
SystemDistributedKeyspace.failParentRepair(parentSession, t);
}
fireErrorAndComplete(tag, progress.get(), totalProgress, t.getMessage());
return;
}
if (options.isIncremental())
if (options.isPreview())
{
previewRepair(parentSession, repairedAt, startTime, traceState, allNeighbors, commonRanges, cfnames);
}
else if (options.isIncremental())
{
consistentRepair(parentSession, repairedAt, startTime, traceState, allNeighbors, commonRanges, cfnames);
}
@ -311,6 +324,76 @@ public class RepairRunnable extends WrappedRunnable implements ProgressEventNoti
Futures.addCallback(repairResult, new RepairCompleteCallback(parentSession, ranges, startTime, traceState, hasFailure, executor));
}
private void previewRepair(UUID parentSession,
long repairedAt,
long startTime,
TraceState traceState,
Set<InetAddress> allNeighbors,
List<Pair<Set<InetAddress>, ? extends Collection<Range<Token>>>> commonRanges,
String... cfnames)
{
logger.debug("Starting preview repair for {}", parentSession);
// Set up RepairJob executor for this repair command.
ListeningExecutorService executor = createExecutor();
final ListenableFuture<List<RepairSessionResult>> allSessions = submitRepairSessions(parentSession, false, executor, commonRanges, cfnames);
Futures.addCallback(allSessions, new FutureCallback<List<RepairSessionResult>>()
{
public void onSuccess(List<RepairSessionResult> results)
{
try
{
PreviewKind previewKind = options.getPreviewKind();
assert previewKind != PreviewKind.NONE;
SyncStatSummary summary = new SyncStatSummary(true);
summary.consumeSessionResults(results);
if (summary.isEmpty())
{
String message = previewKind == PreviewKind.REPAIRED ? "Repaired data is in sync" : "Previewed data was in sync";
logger.info(message);
fireProgressEvent(tag, new ProgressEvent(ProgressEventType.NOTIFICATION, progress.get(), totalProgress, message));
}
else
{
String message = (previewKind == PreviewKind.REPAIRED ? "Repaired data is inconsistent\n" : "Preview complete\n") + summary.toString();
logger.info(message);
fireProgressEvent(tag, new ProgressEvent(ProgressEventType.NOTIFICATION, progress.get(), totalProgress, message));
}
fireProgressEvent(tag, new ProgressEvent(ProgressEventType.SUCCESS, progress.get(), totalProgress,
"Repair preview completed successfully"));
complete();
}
catch (Throwable t)
{
logger.error("Error completing preview repair", t);
onFailure(t);
}
}
public void onFailure(Throwable t)
{
fireProgressEvent(tag, new ProgressEvent(ProgressEventType.ERROR, progress.get(), totalProgress, t.getMessage()));
logger.error("Error completing preview repair", t);
complete();
}
private void complete()
{
logger.debug("Preview repair {} completed", parentSession);
String duration = DurationFormatUtils.formatDurationWords(System.currentTimeMillis() - startTime,
true, true);
String message = String.format("Repair preview #%d finished in %s", cmd, duration);
fireProgressEvent(tag, new ProgressEvent(ProgressEventType.COMPLETE, progress.get(), totalProgress, message));
executor.shutdownNow();
}
});
}
private ListenableFuture<List<RepairSessionResult>> submitRepairSessions(UUID parentSession,
boolean isConsistent,
ListeningExecutorService executor,
@ -327,6 +410,7 @@ public class RepairRunnable extends WrappedRunnable implements ProgressEventNoti
p.left,
isConsistent,
options.isPullRepair(),
options.getPreviewKind(),
executor,
cfnames);
if (session == null)
@ -405,7 +489,10 @@ public class RepairRunnable extends WrappedRunnable implements ProgressEventNoti
public void onSuccess(Object result)
{
SystemDistributedKeyspace.successfulParentRepair(parentSession, successfulRanges);
if (!options.isPreview())
{
SystemDistributedKeyspace.successfulParentRepair(parentSession, successfulRanges);
}
if (hasFailure.get())
{
fireProgressEvent(tag, new ProgressEvent(ProgressEventType.ERROR, progress.get(), totalProgress,
@ -422,7 +509,10 @@ public class RepairRunnable extends WrappedRunnable implements ProgressEventNoti
public void onFailure(Throwable t)
{
fireProgressEvent(tag, new ProgressEvent(ProgressEventType.ERROR, progress.get(), totalProgress, t.getMessage()));
SystemDistributedKeyspace.failParentRepair(parentSession, t);
if (!options.isPreview())
{
SystemDistributedKeyspace.failParentRepair(parentSession, t);
}
repairComplete();
}

View File

@ -34,6 +34,8 @@ import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.gms.*;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.streaming.SessionSummary;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.MerkleTrees;
@ -91,6 +93,7 @@ public class RepairSession extends AbstractFuture<RepairSessionResult> implement
public final Collection<Range<Token>> ranges;
public final Set<InetAddress> endpoints;
public final boolean isConsistent;
public final PreviewKind previewKind;
private final AtomicBoolean isFailed = new AtomicBoolean(false);
@ -125,6 +128,7 @@ public class RepairSession extends AbstractFuture<RepairSessionResult> implement
Set<InetAddress> endpoints,
boolean isConsistent,
boolean pullRepair,
PreviewKind previewKind,
String... cfnames)
{
assert cfnames.length > 0 : "Repairing no column families seems pointless, doesn't it";
@ -137,6 +141,7 @@ public class RepairSession extends AbstractFuture<RepairSessionResult> implement
this.ranges = ranges;
this.endpoints = endpoints;
this.isConsistent = isConsistent;
this.previewKind = previewKind;
this.pullRepair = pullRepair;
}
@ -177,7 +182,7 @@ public class RepairSession extends AbstractFuture<RepairSessionResult> implement
}
String message = String.format("Received merkle tree for %s from %s", desc.columnFamily, endpoint);
logger.info("[repair #{}] {}", getId(), message);
logger.info("{} {}", previewKind.logPrefix(getId()), message);
Tracing.traceRepair(message);
task.treesReceived(trees);
}
@ -189,7 +194,7 @@ public class RepairSession extends AbstractFuture<RepairSessionResult> implement
* @param nodes nodes that completed sync
* @param success true if sync succeeded
*/
public void syncComplete(RepairJobDesc desc, NodePair nodes, boolean success)
public void syncComplete(RepairJobDesc desc, NodePair nodes, boolean success, List<SessionSummary> summaries)
{
RemoteSyncTask task = syncingTasks.get(Pair.create(desc, nodes));
if (task == null)
@ -198,8 +203,8 @@ public class RepairSession extends AbstractFuture<RepairSessionResult> implement
return;
}
logger.debug("[repair #{}] Repair completed between {} and {} on {}", getId(), nodes.endpoint1, nodes.endpoint2, desc.columnFamily);
task.syncComplete(success);
logger.debug("{} Repair completed between {} and {} on {}", previewKind.logPrefix(getId()), nodes.endpoint1, nodes.endpoint2, desc.columnFamily);
task.syncComplete(success, summaries);
}
private String repairedNodes()
@ -225,16 +230,22 @@ public class RepairSession extends AbstractFuture<RepairSessionResult> implement
if (terminated)
return;
logger.info("[repair #{}] new session: will sync {} on range {} for {}.{}", getId(), repairedNodes(), ranges, keyspace, Arrays.toString(cfnames));
logger.info("{} new session: will sync {} on range {} for {}.{}", previewKind.logPrefix(getId()), repairedNodes(), ranges, keyspace, Arrays.toString(cfnames));
Tracing.traceRepair("Syncing range {}", ranges);
SystemDistributedKeyspace.startRepairs(getId(), parentRepairSession, keyspace, cfnames, ranges, endpoints);
if (!previewKind.isPreview())
{
SystemDistributedKeyspace.startRepairs(getId(), parentRepairSession, keyspace, cfnames, ranges, endpoints);
}
if (endpoints.isEmpty())
{
logger.info("[repair #{}] {}", getId(), message = String.format("No neighbors to repair with on range %s: session completed", ranges));
logger.info("{} {}", previewKind.logPrefix(getId()), message = String.format("No neighbors to repair with on range %s: session completed", ranges));
Tracing.traceRepair(message);
set(new RepairSessionResult(id, keyspace, ranges, Lists.<RepairResult>newArrayList()));
SystemDistributedKeyspace.failRepairs(getId(), keyspace, cfnames, new RuntimeException(message));
if (!previewKind.isPreview())
{
SystemDistributedKeyspace.failRepairs(getId(), keyspace, cfnames, new RuntimeException(message));
}
return;
}
@ -244,10 +255,13 @@ public class RepairSession extends AbstractFuture<RepairSessionResult> implement
if (!FailureDetector.instance.isAlive(endpoint))
{
message = String.format("Cannot proceed on repair because a neighbor (%s) is dead: session failed", endpoint);
logger.error("[repair #{}] {}", getId(), message);
logger.error("{} {}", previewKind.logPrefix(getId()), message);
Exception e = new IOException(message);
setException(e);
SystemDistributedKeyspace.failRepairs(getId(), keyspace, cfnames, e);
if (!previewKind.isPreview())
{
SystemDistributedKeyspace.failRepairs(getId(), keyspace, cfnames, e);
}
return;
}
}
@ -256,7 +270,7 @@ public class RepairSession extends AbstractFuture<RepairSessionResult> implement
List<ListenableFuture<RepairResult>> jobs = new ArrayList<>(cfnames.length);
for (String cfname : cfnames)
{
RepairJob job = new RepairJob(this, cfname, isConsistent);
RepairJob job = new RepairJob(this, cfname, isConsistent, previewKind);
executor.execute(job);
jobs.add(job);
}
@ -267,7 +281,7 @@ public class RepairSession extends AbstractFuture<RepairSessionResult> implement
public void onSuccess(List<RepairResult> results)
{
// this repair session is completed
logger.info("[repair #{}] {}", getId(), "Session completed successfully");
logger.info("{} {}", previewKind.logPrefix(getId()), "Session completed successfully");
Tracing.traceRepair("Completed sync of range {}", ranges);
set(new RepairSessionResult(id, keyspace, ranges, results));
@ -278,7 +292,7 @@ public class RepairSession extends AbstractFuture<RepairSessionResult> implement
public void onFailure(Throwable t)
{
logger.error(String.format("[repair #%s] Session completed with the following error", getId()), t);
logger.error("{} Session completed with the following error", previewKind.logPrefix(getId()), t);
Tracing.traceRepair("Session completed with the following error: {}", t);
forceShutdown(t);
}
@ -335,7 +349,7 @@ public class RepairSession extends AbstractFuture<RepairSessionResult> implement
return;
Exception exception = new IOException(String.format("Endpoint %s died", endpoint));
logger.error(String.format("[repair #%s] session completed with the following error", getId()), exception);
logger.error(String.format("{} session completed with the following error", previewKind.logPrefix(getId())), exception);
// If a node failed, we stop everything (though there could still be some activity in the background)
forceShutdown(exception);
}

View File

@ -19,6 +19,7 @@ package org.apache.cassandra.repair;
import java.net.InetAddress;
import java.util.UUID;
import java.util.Collections;
import com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger;
@ -28,7 +29,7 @@ import org.apache.cassandra.db.SystemKeyspace;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.repair.messages.SyncComplete;
import org.apache.cassandra.repair.messages.SyncRequest;
import org.apache.cassandra.service.ActiveRepairService;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.streaming.StreamEvent;
import org.apache.cassandra.streaming.StreamEventHandler;
import org.apache.cassandra.streaming.StreamPlan;
@ -46,12 +47,14 @@ public class StreamingRepairTask implements Runnable, StreamEventHandler
private final RepairJobDesc desc;
private final SyncRequest request;
private final UUID pendingRepair;
private final PreviewKind previewKind;
public StreamingRepairTask(RepairJobDesc desc, SyncRequest request, UUID pendingRepair)
public StreamingRepairTask(RepairJobDesc desc, SyncRequest request, UUID pendingRepair, PreviewKind previewKind)
{
this.desc = desc;
this.request = request;
this.pendingRepair = pendingRepair;
this.previewKind = previewKind;
}
public void run()
@ -65,7 +68,7 @@ public class StreamingRepairTask implements Runnable, StreamEventHandler
@VisibleForTesting
StreamPlan createStreamPlan(InetAddress dest, InetAddress preferred)
{
return new StreamPlan(StreamOperation.REPAIR, 1, false, false, pendingRepair)
return new StreamPlan(StreamOperation.REPAIR, 1, false, false, pendingRepair, previewKind)
.listeners(this)
.flushBeforeTransfer(pendingRepair == null) // sstables are isolated at the beginning of an incremental repair session, so flushing isn't neccessary
.requestRanges(dest, preferred, desc.keyspace, request.ranges, desc.columnFamily) // request ranges from the remote node
@ -83,8 +86,8 @@ public class StreamingRepairTask implements Runnable, StreamEventHandler
*/
public void onSuccess(StreamState state)
{
logger.info("[repair #{}] streaming task succeed, returning response to {}", desc.sessionId, request.initiator);
MessagingService.instance().sendOneWay(new SyncComplete(desc, request.src, request.dst, true).createMessage(), request.initiator);
logger.info("{} streaming task succeed, returning response to {}", previewKind.logPrefix(desc.sessionId), request.initiator);
MessagingService.instance().sendOneWay(new SyncComplete(desc, request.src, request.dst, true, state.createSummaries()).createMessage(), request.initiator);
}
/**
@ -92,6 +95,6 @@ public class StreamingRepairTask implements Runnable, StreamEventHandler
*/
public void onFailure(Throwable t)
{
MessagingService.instance().sendOneWay(new SyncComplete(desc, request.src, request.dst, false).createMessage(), request.initiator);
MessagingService.instance().sendOneWay(new SyncComplete(desc, request.src, request.dst, false, Collections.emptyList()).createMessage(), request.initiator);
}
}

View File

@ -17,17 +17,33 @@
*/
package org.apache.cassandra.repair;
import java.util.List;
import org.apache.cassandra.streaming.SessionSummary;
/**
* Statistics about synchronizing two replica
*/
public class SyncStat
{
public final NodePair nodes;
public final long numberOfDifferences;
public final long numberOfDifferences; // TODO: revert to Range<Token>
public final List<SessionSummary> summaries;
public SyncStat(NodePair nodes, long numberOfDifferences)
{
this(nodes, numberOfDifferences, null);
}
public SyncStat(NodePair nodes, long numberOfDifferences, List<SessionSummary> summaries)
{
this.nodes = nodes;
this.numberOfDifferences = numberOfDifferences;
this.summaries = summaries;
}
public SyncStat withSummaries(List<SessionSummary> summaries)
{
return new SyncStat(nodes, numberOfDifferences, summaries);
}
}

View File

@ -25,6 +25,7 @@ import org.slf4j.LoggerFactory;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.utils.MerkleTrees;
@ -39,14 +40,16 @@ public abstract class SyncTask extends AbstractFuture<SyncStat> implements Runna
protected final RepairJobDesc desc;
protected final TreeResponse r1;
protected final TreeResponse r2;
protected final PreviewKind previewKind;
protected volatile SyncStat stat;
public SyncTask(RepairJobDesc desc, TreeResponse r1, TreeResponse r2)
public SyncTask(RepairJobDesc desc, TreeResponse r1, TreeResponse r2, PreviewKind previewKind)
{
this.desc = desc;
this.r1 = r1;
this.r2 = r2;
this.previewKind = previewKind;
}
/**
@ -60,7 +63,7 @@ public abstract class SyncTask extends AbstractFuture<SyncStat> implements Runna
stat = new SyncStat(new NodePair(r1.endpoint, r2.endpoint), differences.size());
// choose a repair method based on the significance of the difference
String format = String.format("[repair #%s] Endpoints %s and %s %%s for %s", desc.sessionId, r1.endpoint, r2.endpoint, desc.columnFamily);
String format = String.format("%s Endpoints %s and %s %%s for %s", previewKind.logPrefix(desc.sessionId), r1.endpoint, r2.endpoint, desc.columnFamily);
if (differences.isEmpty())
{
logger.info(String.format(format, "are consistent"));

View File

@ -24,6 +24,7 @@ import com.google.common.util.concurrent.AbstractFuture;
import org.apache.cassandra.exceptions.RepairException;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.repair.messages.ValidationRequest;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.utils.MerkleTrees;
/**
@ -35,12 +36,14 @@ public class ValidationTask extends AbstractFuture<TreeResponse> implements Runn
private final RepairJobDesc desc;
private final InetAddress endpoint;
private final int gcBefore;
private final PreviewKind previewKind;
public ValidationTask(RepairJobDesc desc, InetAddress endpoint, int gcBefore)
public ValidationTask(RepairJobDesc desc, InetAddress endpoint, int gcBefore, PreviewKind previewKind)
{
this.desc = desc;
this.endpoint = endpoint;
this.gcBefore = gcBefore;
this.previewKind = previewKind;
}
/**
@ -61,7 +64,7 @@ public class ValidationTask extends AbstractFuture<TreeResponse> implements Runn
{
if (trees == null)
{
setException(new RepairException(desc, "Validation failed in " + endpoint));
setException(new RepairException(desc, previewKind, "Validation failed in " + endpoint));
}
else
{

View File

@ -37,6 +37,7 @@ import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.repair.messages.ValidationComplete;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.MerkleTree;
@ -71,22 +72,25 @@ public class Validator implements Runnable
// last key seen
private DecoratedKey lastKey;
public Validator(RepairJobDesc desc, InetAddress initiator, int gcBefore)
private final PreviewKind previewKind;
public Validator(RepairJobDesc desc, InetAddress initiator, int gcBefore, PreviewKind previewKind)
{
this(desc, initiator, gcBefore, false, false);
this(desc, initiator, gcBefore, false, false, previewKind);
}
public Validator(RepairJobDesc desc, InetAddress initiator, int gcBefore, boolean isConsistent)
public Validator(RepairJobDesc desc, InetAddress initiator, int gcBefore, boolean isConsistent, PreviewKind previewKind)
{
this(desc, initiator, gcBefore, false, isConsistent);
this(desc, initiator, gcBefore, false, isConsistent, previewKind);
}
public Validator(RepairJobDesc desc, InetAddress initiator, int gcBefore, boolean evenTreeDistribution, boolean isConsistent)
public Validator(RepairJobDesc desc, InetAddress initiator, int gcBefore, boolean evenTreeDistribution, boolean isConsistent, PreviewKind previewKind)
{
this.desc = desc;
this.initiator = initiator;
this.gcBefore = gcBefore;
this.isConsistent = isConsistent;
this.previewKind = previewKind;
validated = 0;
range = null;
ranges = null;
@ -285,7 +289,7 @@ public class Validator implements Runnable
// respond to the request that triggered this validation
if (!initiator.equals(FBUtilities.getBroadcastAddress()))
{
logger.info("[repair #{}] Sending completed merkle tree to {} for {}.{}", desc.sessionId, initiator, desc.keyspace, desc.columnFamily);
logger.info("{} Sending completed merkle tree to {} for {}.{}", previewKind.logPrefix(desc.sessionId), initiator, desc.keyspace, desc.columnFamily);
Tracing.traceRepair("Sending completed merkle tree to {} for {}.{}", initiator, desc.keyspace, desc.columnFamily);
}
MessagingService.instance().sendOneWay(new ValidationComplete(desc, trees).createMessage(), initiator);

View File

@ -0,0 +1,233 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.repair.consistent;
import java.net.InetAddress;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import com.google.common.collect.Lists;
import org.apache.cassandra.repair.RepairResult;
import org.apache.cassandra.repair.RepairSessionResult;
import org.apache.cassandra.repair.SyncStat;
import org.apache.cassandra.streaming.SessionSummary;
import org.apache.cassandra.streaming.StreamSummary;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.Pair;
import static com.google.common.collect.Iterables.filter;
public class SyncStatSummary
{
private static class Session
{
final InetAddress src;
final InetAddress dst;
int files = 0;
long bytes = 0;
long ranges = 0;
Session(InetAddress src, InetAddress dst)
{
this.src = src;
this.dst = dst;
}
void consumeSummary(StreamSummary summary)
{
files += summary.files;
bytes += summary.totalSize;
}
void consumeSummaries(Collection<StreamSummary> summaries, long numRanges)
{
summaries.forEach(this::consumeSummary);
ranges += numRanges;
}
public String toString()
{
return String.format("%s -> %s: %s ranges, %s sstables, %s bytes", src, dst, ranges, files, FBUtilities.prettyPrintMemory(bytes));
}
}
private static class Table
{
final String keyspace;
final String table;
int files = -1;
long bytes = -1;
int ranges = -1;
boolean totalsCalculated = false;
final Map<Pair<InetAddress, InetAddress>, Session> sessions = new HashMap<>();
Table(String keyspace, String table)
{
this.keyspace = keyspace;
this.table = table;
}
Session getOrCreate(InetAddress from, InetAddress to)
{
Pair<InetAddress, InetAddress> k = Pair.create(from, to);
if (!sessions.containsKey(k))
{
sessions.put(k, new Session(from, to));
}
return sessions.get(k);
}
void consumeStat(SyncStat stat)
{
for (SessionSummary summary: stat.summaries)
{
getOrCreate(summary.coordinator, summary.peer).consumeSummaries(summary.sendingSummaries, stat.numberOfDifferences);
getOrCreate(summary.peer, summary.coordinator).consumeSummaries(summary.receivingSummaries, stat.numberOfDifferences);
}
}
void consumeStats(List<SyncStat> stats)
{
filter(stats, s -> s.summaries != null).forEach(this::consumeStat);
}
void calculateTotals()
{
files = 0;
bytes = 0;
ranges = 0;
for (Session session: sessions.values())
{
files += session.files;
bytes += session.bytes;
ranges += session.ranges;
}
totalsCalculated = true;
}
public String toString()
{
if (!totalsCalculated)
{
calculateTotals();
}
StringBuilder output = new StringBuilder();
output.append(String.format("%s.%s - %s ranges, %s sstables, %s bytes\n", keyspace, table, ranges, files, FBUtilities.prettyPrintMemory(bytes)));
for (Session session: sessions.values())
{
output.append(" ").append(session.toString()).append('\n');
}
return output.toString();
}
}
private Map<Pair<String, String>, Table> summaries = new HashMap<>();
private final boolean isEstimate;
private int files = -1;
private long bytes = -1;
private int ranges = -1;
private boolean totalsCalculated = false;
public SyncStatSummary(boolean isEstimate)
{
this.isEstimate = isEstimate;
}
public void consumeRepairResult(RepairResult result)
{
Pair<String, String> cf = Pair.create(result.desc.keyspace, result.desc.columnFamily);
if (!summaries.containsKey(cf))
{
summaries.put(cf, new Table(cf.left, cf.right));
}
summaries.get(cf).consumeStats(result.stats);
}
public void consumeSessionResults(List<RepairSessionResult> results)
{
if (results != null)
{
filter(results, Objects::nonNull).forEach(r -> filter(r.repairJobResults, Objects::nonNull).forEach(this::consumeRepairResult));
}
}
public boolean isEmpty()
{
calculateTotals();
return files == 0 && bytes == 0 && ranges == 0;
}
private void calculateTotals()
{
files = 0;
bytes = 0;
ranges = 0;
summaries.values().forEach(Table::calculateTotals);
for (Table table: summaries.values())
{
table.calculateTotals();
files += table.files;
bytes += table.bytes;
ranges += table.ranges;
}
totalsCalculated = true;
}
public String toString()
{
List<Pair<String, String>> tables = Lists.newArrayList(summaries.keySet());
tables.sort((o1, o2) ->
{
int ks = o1.left.compareTo(o2.left);
return ks != 0 ? ks : o1.right.compareTo(o2.right);
});
calculateTotals();
StringBuilder output = new StringBuilder();
if (isEstimate)
{
output.append(String.format("Total estimated streaming: %s ranges, %s sstables, %s bytes\n", ranges, files, FBUtilities.prettyPrintMemory(bytes)));
}
else
{
output.append(String.format("Total streaming: %s ranges, %s sstables, %s bytes\n", ranges, files, FBUtilities.prettyPrintMemory(bytes)));
}
for (Pair<String, String> tableName: tables)
{
Table table = summaries.get(tableName);
output.append(table.toString()).append('\n');
}
return output.toString();
}
}

View File

@ -31,6 +31,7 @@ import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.schema.TableId;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.utils.UUIDSerializer;
@ -44,8 +45,9 @@ public class PrepareMessage extends RepairMessage
public final boolean isIncremental;
public final long timestamp;
public final boolean isGlobal;
public final PreviewKind previewKind;
public PrepareMessage(UUID parentRepairSession, List<TableId> tableIds, Collection<Range<Token>> ranges, boolean isIncremental, long timestamp, boolean isGlobal)
public PrepareMessage(UUID parentRepairSession, List<TableId> tableIds, Collection<Range<Token>> ranges, boolean isIncremental, long timestamp, boolean isGlobal, PreviewKind previewKind)
{
super(Type.PREPARE_MESSAGE, null);
this.parentRepairSession = parentRepairSession;
@ -54,6 +56,7 @@ public class PrepareMessage extends RepairMessage
this.isIncremental = isIncremental;
this.timestamp = timestamp;
this.isGlobal = isGlobal;
this.previewKind = previewKind;
}
@Override
@ -66,6 +69,7 @@ public class PrepareMessage extends RepairMessage
parentRepairSession.equals(other.parentRepairSession) &&
isIncremental == other.isIncremental &&
isGlobal == other.isGlobal &&
previewKind == other.previewKind &&
timestamp == other.timestamp &&
tableIds.equals(other.tableIds) &&
ranges.equals(other.ranges);
@ -74,7 +78,7 @@ public class PrepareMessage extends RepairMessage
@Override
public int hashCode()
{
return Objects.hash(messageType, parentRepairSession, isGlobal, isIncremental, timestamp, tableIds, ranges);
return Objects.hash(messageType, parentRepairSession, isGlobal, previewKind, isIncremental, timestamp, tableIds, ranges);
}
public static class PrepareMessageSerializer implements MessageSerializer<PrepareMessage>
@ -94,6 +98,7 @@ public class PrepareMessage extends RepairMessage
out.writeBoolean(message.isIncremental);
out.writeLong(message.timestamp);
out.writeBoolean(message.isGlobal);
out.writeInt(message.previewKind.getSerializationVal());
}
public PrepareMessage deserialize(DataInputPlus in, int version) throws IOException
@ -110,7 +115,8 @@ public class PrepareMessage extends RepairMessage
boolean isIncremental = in.readBoolean();
long timestamp = in.readLong();
boolean isGlobal = in.readBoolean();
return new PrepareMessage(parentRepairSession, tableIds, ranges, isIncremental, timestamp, isGlobal);
PreviewKind previewKind = PreviewKind.deserialize(in.readInt());
return new PrepareMessage(parentRepairSession, tableIds, ranges, isIncremental, timestamp, isGlobal, previewKind);
}
public long serializedSize(PrepareMessage message, int version)
@ -126,6 +132,7 @@ public class PrepareMessage extends RepairMessage
size += TypeSizes.sizeof(message.isIncremental);
size += TypeSizes.sizeof(message.timestamp);
size += TypeSizes.sizeof(message.isGlobal);
size += TypeSizes.sizeof(message.previewKind.getSerializationVal());
return size;
}
}

View File

@ -28,6 +28,7 @@ import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.dht.IPartitioner;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.repair.RepairParallelism;
import org.apache.cassandra.utils.FBUtilities;
@ -47,6 +48,7 @@ public class RepairOption
public static final String TRACE_KEY = "trace";
public static final String SUB_RANGE_REPAIR_KEY = "sub_range_repair";
public static final String PULL_REPAIR_KEY = "pullRepair";
public static final String PREVIEW = "previewKind";
// we don't want to push nodes too much for repair
public static final int MAX_JOB_THREADS = 4;
@ -136,6 +138,7 @@ public class RepairOption
RepairParallelism parallelism = RepairParallelism.fromName(options.get(PARALLELISM_KEY));
boolean primaryRange = Boolean.parseBoolean(options.get(PRIMARY_RANGE_KEY));
boolean incremental = Boolean.parseBoolean(options.get(INCREMENTAL_KEY));
PreviewKind previewKind = PreviewKind.valueOf(options.getOrDefault(PREVIEW, PreviewKind.NONE.toString()));
boolean trace = Boolean.parseBoolean(options.get(TRACE_KEY));
boolean pullRepair = Boolean.parseBoolean(options.get(PULL_REPAIR_KEY));
@ -171,7 +174,7 @@ public class RepairOption
}
}
RepairOption option = new RepairOption(parallelism, primaryRange, incremental, trace, jobThreads, ranges, !ranges.isEmpty(), pullRepair);
RepairOption option = new RepairOption(parallelism, primaryRange, incremental, trace, jobThreads, ranges, !ranges.isEmpty(), pullRepair, previewKind);
// data centers
String dataCentersStr = options.get(DATACENTERS_KEY);
@ -252,13 +255,14 @@ public class RepairOption
private final int jobThreads;
private final boolean isSubrangeRepair;
private final boolean pullRepair;
private final PreviewKind previewKind;
private final Collection<String> columnFamilies = new HashSet<>();
private final Collection<String> dataCenters = new HashSet<>();
private final Collection<String> hosts = new HashSet<>();
private final Collection<Range<Token>> ranges = new HashSet<>();
public RepairOption(RepairParallelism parallelism, boolean primaryRange, boolean incremental, boolean trace, int jobThreads, Collection<Range<Token>> ranges, boolean isSubrangeRepair, boolean pullRepair)
public RepairOption(RepairParallelism parallelism, boolean primaryRange, boolean incremental, boolean trace, int jobThreads, Collection<Range<Token>> ranges, boolean isSubrangeRepair, boolean pullRepair, PreviewKind previewKind)
{
if (FBUtilities.isWindows &&
(DatabaseDescriptor.getDiskAccessMode() != Config.DiskAccessMode.standard || DatabaseDescriptor.getIndexAccessMode() != Config.DiskAccessMode.standard) &&
@ -277,6 +281,7 @@ public class RepairOption
this.ranges.addAll(ranges);
this.isSubrangeRepair = isSubrangeRepair;
this.pullRepair = pullRepair;
this.previewKind = previewKind;
}
public RepairParallelism getParallelism()
@ -339,6 +344,16 @@ public class RepairOption
return isSubrangeRepair;
}
public PreviewKind getPreviewKind()
{
return previewKind;
}
public boolean isPreview()
{
return previewKind.isPreview();
}
public boolean isInLocalDCOnly() {
return dataCenters.size() == 1 && dataCenters.contains(DatabaseDescriptor.getLocalDataCenter());
}
@ -347,16 +362,17 @@ public class RepairOption
public String toString()
{
return "repair options (" +
"parallelism: " + parallelism +
", primary range: " + primaryRange +
", incremental: " + incremental +
", job threads: " + jobThreads +
", ColumnFamilies: " + columnFamilies +
", dataCenters: " + dataCenters +
", hosts: " + hosts +
", # of ranges: " + ranges.size() +
", pull repair: " + pullRepair +
')';
"parallelism: " + parallelism +
", primary range: " + primaryRange +
", incremental: " + incremental +
", job threads: " + jobThreads +
", ColumnFamilies: " + columnFamilies +
", dataCenters: " + dataCenters +
", hosts: " + hosts +
", previewKind: " + previewKind +
", # of ranges: " + ranges.size() +
", pull repair: " + pullRepair +
')';
}
public Map<String, String> asMap()
@ -373,6 +389,7 @@ public class RepairOption
options.put(TRACE_KEY, Boolean.toString(trace));
options.put(RANGES_KEY, Joiner.on(",").join(ranges));
options.put(PULL_REPAIR_KEY, Boolean.toString(pullRepair));
options.put(PREVIEW, previewKind.toString());
return options;
}
}

View File

@ -19,6 +19,8 @@ package org.apache.cassandra.repair.messages;
import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.apache.cassandra.db.TypeSizes;
@ -26,6 +28,7 @@ import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.repair.NodePair;
import org.apache.cassandra.repair.RepairJobDesc;
import org.apache.cassandra.streaming.SessionSummary;
/**
*
@ -40,16 +43,20 @@ public class SyncComplete extends RepairMessage
/** true if sync success, false otherwise */
public final boolean success;
public SyncComplete(RepairJobDesc desc, NodePair nodes, boolean success)
public final List<SessionSummary> summaries;
public SyncComplete(RepairJobDesc desc, NodePair nodes, boolean success, List<SessionSummary> summaries)
{
super(Type.SYNC_COMPLETE, desc);
this.nodes = nodes;
this.success = success;
this.summaries = summaries;
}
public SyncComplete(RepairJobDesc desc, InetAddress endpoint1, InetAddress endpoint2, boolean success)
public SyncComplete(RepairJobDesc desc, InetAddress endpoint1, InetAddress endpoint2, boolean success, List<SessionSummary> summaries)
{
super(Type.SYNC_COMPLETE, desc);
this.summaries = summaries;
this.nodes = new NodePair(endpoint1, endpoint2);
this.success = success;
}
@ -63,13 +70,14 @@ public class SyncComplete extends RepairMessage
return messageType == other.messageType &&
desc.equals(other.desc) &&
success == other.success &&
nodes.equals(other.nodes);
nodes.equals(other.nodes) &&
summaries.equals(other.summaries);
}
@Override
public int hashCode()
{
return Objects.hash(messageType, desc, success, nodes);
return Objects.hash(messageType, desc, success, nodes, summaries);
}
private static class SyncCompleteSerializer implements MessageSerializer<SyncComplete>
@ -79,13 +87,28 @@ public class SyncComplete extends RepairMessage
RepairJobDesc.serializer.serialize(message.desc, out, version);
NodePair.serializer.serialize(message.nodes, out, version);
out.writeBoolean(message.success);
out.writeInt(message.summaries.size());
for (SessionSummary summary: message.summaries)
{
SessionSummary.serializer.serialize(summary, out, version);
}
}
public SyncComplete deserialize(DataInputPlus in, int version) throws IOException
{
RepairJobDesc desc = RepairJobDesc.serializer.deserialize(in, version);
NodePair nodes = NodePair.serializer.deserialize(in, version);
return new SyncComplete(desc, nodes, in.readBoolean());
boolean success = in.readBoolean();
int numSummaries = in.readInt();
List<SessionSummary> summaries = new ArrayList<>(numSummaries);
for (int i=0; i<numSummaries; i++)
{
summaries.add(SessionSummary.serializer.deserialize(in, version));
}
return new SyncComplete(desc, nodes, success, summaries);
}
public long serializedSize(SyncComplete message, int version)
@ -93,6 +116,13 @@ public class SyncComplete extends RepairMessage
long size = RepairJobDesc.serializer.serializedSize(message.desc, version);
size += NodePair.serializer.serializedSize(message.nodes, version);
size += TypeSizes.sizeof(message.success);
size += TypeSizes.sizeof(message.summaries.size());
for (SessionSummary summary: message.summaries)
{
size += SessionSummary.serializer.serializedSize(summary, version);
}
return size;
}
}

View File

@ -33,6 +33,7 @@ import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.net.CompactEndpointSerializationHelper;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.repair.RepairJobDesc;
import org.apache.cassandra.streaming.PreviewKind;
/**
* Body part of SYNC_REQUEST repair message.
@ -48,14 +49,16 @@ public class SyncRequest extends RepairMessage
public final InetAddress src;
public final InetAddress dst;
public final Collection<Range<Token>> ranges;
public final PreviewKind previewKind;
public SyncRequest(RepairJobDesc desc, InetAddress initiator, InetAddress src, InetAddress dst, Collection<Range<Token>> ranges)
public SyncRequest(RepairJobDesc desc, InetAddress initiator, InetAddress src, InetAddress dst, Collection<Range<Token>> ranges, PreviewKind previewKind)
{
super(Type.SYNC_REQUEST, desc);
this.initiator = initiator;
this.src = src;
this.dst = dst;
this.ranges = ranges;
this.previewKind = previewKind;
}
@Override
@ -69,13 +72,14 @@ public class SyncRequest extends RepairMessage
initiator.equals(req.initiator) &&
src.equals(req.src) &&
dst.equals(req.dst) &&
ranges.equals(req.ranges);
ranges.equals(req.ranges) &&
previewKind == req.previewKind;
}
@Override
public int hashCode()
{
return Objects.hash(messageType, desc, initiator, src, dst, ranges);
return Objects.hash(messageType, desc, initiator, src, dst, ranges, previewKind);
}
public static class SyncRequestSerializer implements MessageSerializer<SyncRequest>
@ -92,6 +96,7 @@ public class SyncRequest extends RepairMessage
MessagingService.validatePartitioner(range);
AbstractBounds.tokenSerializer.serialize(range, out, version);
}
out.writeInt(message.previewKind.getSerializationVal());
}
public SyncRequest deserialize(DataInputPlus in, int version) throws IOException
@ -104,7 +109,8 @@ public class SyncRequest extends RepairMessage
List<Range<Token>> ranges = new ArrayList<>(rangesCount);
for (int i = 0; i < rangesCount; ++i)
ranges.add((Range<Token>) AbstractBounds.tokenSerializer.deserialize(in, MessagingService.globalPartitioner(), version));
return new SyncRequest(desc, owner, src, dst, ranges);
PreviewKind previewKind = PreviewKind.deserialize(in.readInt());
return new SyncRequest(desc, owner, src, dst, ranges, previewKind);
}
public long serializedSize(SyncRequest message, int version)
@ -114,6 +120,7 @@ public class SyncRequest extends RepairMessage
size += TypeSizes.sizeof(message.ranges.size());
for (Range<Token> range : message.ranges)
size += AbstractBounds.tokenSerializer.serializedSize(range, version);
size += TypeSizes.sizeof(message.previewKind.getSerializationVal());
return size;
}
}
@ -126,6 +133,7 @@ public class SyncRequest extends RepairMessage
", src=" + src +
", dst=" + dst +
", ranges=" + ranges +
", previewKind=" + previewKind +
"} " + super.toString();
}
}

View File

@ -61,6 +61,7 @@ import org.apache.cassandra.net.IAsyncCallbackWithFailure;
import org.apache.cassandra.net.MessageIn;
import org.apache.cassandra.net.MessageOut;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.repair.RepairJobDesc;
import org.apache.cassandra.repair.RepairParallelism;
import org.apache.cassandra.repair.RepairSession;
@ -167,6 +168,7 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai
Set<InetAddress> endpoints,
boolean isConsistent,
boolean pullRepair,
PreviewKind previewKind,
ListeningExecutorService executor,
String... cfnames)
{
@ -176,7 +178,7 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai
if (cfnames.length == 0)
return null;
final RepairSession session = new RepairSession(parentRepairSession, UUIDGen.getTimeUUID(), range, keyspace, parallelismDegree, endpoints, isConsistent, pullRepair, cfnames);
final RepairSession session = new RepairSession(parentRepairSession, UUIDGen.getTimeUUID(), range, keyspace, parallelismDegree, endpoints, isConsistent, pullRepair, previewKind, cfnames);
sessions.put(session.getId(), session);
// register listeners
@ -319,7 +321,7 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai
{
// we only want repairedAt for incremental repairs, for non incremental repairs, UNREPAIRED_SSTABLE will preserve repairedAt on streamed sstables
long repairedAt = options.isIncremental() ? Clock.instance.currentTimeMillis() : ActiveRepairService.UNREPAIRED_SSTABLE;
registerParentRepairSession(parentRepairSession, coordinator, columnFamilyStores, options.getRanges(), options.isIncremental(), repairedAt, options.isGlobal());
registerParentRepairSession(parentRepairSession, coordinator, columnFamilyStores, options.getRanges(), options.isIncremental(), repairedAt, options.isGlobal(), options.getPreviewKind());
final CountDownLatch prepareLatch = new CountDownLatch(endpoints.size());
final AtomicBoolean status = new AtomicBoolean(true);
final Set<String> failedNodes = Collections.synchronizedSet(new HashSet<String>());
@ -351,7 +353,7 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai
{
if (FailureDetector.instance.isAlive(neighbour))
{
PrepareMessage message = new PrepareMessage(parentRepairSession, tableIds, options.getRanges(), options.isIncremental(), repairedAt, options.isGlobal());
PrepareMessage message = new PrepareMessage(parentRepairSession, tableIds, options.getRanges(), options.isIncremental(), repairedAt, options.isGlobal(), options.getPreviewKind());
MessageOut<RepairMessage> msg = message.createMessage();
MessagingService.instance().sendRR(msg, neighbour, callback, DatabaseDescriptor.getRpcTimeout(), true);
}
@ -386,7 +388,7 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai
throw new RuntimeException(errorMsg);
}
public void registerParentRepairSession(UUID parentRepairSession, InetAddress coordinator, List<ColumnFamilyStore> columnFamilyStores, Collection<Range<Token>> ranges, boolean isIncremental, long repairedAt, boolean isGlobal)
public void registerParentRepairSession(UUID parentRepairSession, InetAddress coordinator, List<ColumnFamilyStore> columnFamilyStores, Collection<Range<Token>> ranges, boolean isIncremental, long repairedAt, boolean isGlobal, PreviewKind previewKind)
{
assert isIncremental || repairedAt == ActiveRepairService.UNREPAIRED_SSTABLE;
if (!registeredForEndpointChanges)
@ -396,7 +398,7 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai
registeredForEndpointChanges = true;
}
parentRepairSessions.put(parentRepairSession, new ParentRepairSession(coordinator, columnFamilyStores, ranges, isIncremental, repairedAt, isGlobal));
parentRepairSessions.put(parentRepairSession, new ParentRepairSession(coordinator, columnFamilyStores, ranges, isIncremental, repairedAt, isGlobal, previewKind));
}
public ParentRepairSession getParentRepairSession(UUID parentSessionId)
@ -444,7 +446,7 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai
case SYNC_COMPLETE:
// one of replica is synced.
SyncComplete sync = (SyncComplete) message;
session.syncComplete(desc, sync.nodes, sync.success);
session.syncComplete(desc, sync.nodes, sync.success, sync.summaries);
break;
default:
break;
@ -464,8 +466,9 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai
public final boolean isGlobal;
public final long repairedAt;
public final InetAddress coordinator;
public final PreviewKind previewKind;
public ParentRepairSession(InetAddress coordinator, List<ColumnFamilyStore> columnFamilyStores, Collection<Range<Token>> ranges, boolean isIncremental, long repairedAt, boolean isGlobal)
public ParentRepairSession(InetAddress coordinator, List<ColumnFamilyStore> columnFamilyStores, Collection<Range<Token>> ranges, boolean isIncremental, long repairedAt, boolean isGlobal, PreviewKind previewKind)
{
this.coordinator = coordinator;
for (ColumnFamilyStore cfs : columnFamilyStores)
@ -476,6 +479,27 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai
this.repairedAt = repairedAt;
this.isIncremental = isIncremental;
this.isGlobal = isGlobal;
this.previewKind = previewKind;
}
public boolean isPreview()
{
return previewKind != PreviewKind.NONE;
}
public Predicate<SSTableReader> getPreviewPredicate()
{
switch (previewKind)
{
case ALL:
return (s) -> true;
case REPAIRED:
return (s) -> s.isRepaired();
case UNREPAIRED:
return (s) -> !s.isRepaired();
default:
throw new RuntimeException("Can't get preview predicate for preview kind " + previewKind);
}
}
public synchronized void maybeSnapshot(TableId tableId, UUID parentSessionId)

View File

@ -64,10 +64,12 @@ public class ConnectionHandler
private IncomingMessageHandler incoming;
private OutgoingMessageHandler outgoing;
private final boolean isPreview;
ConnectionHandler(StreamSession session, int incomingSocketTimeout)
ConnectionHandler(StreamSession session, int incomingSocketTimeout, boolean isPreview)
{
this.session = session;
this.isPreview = isPreview;
this.incoming = new IncomingMessageHandler(session, incomingSocketTimeout);
this.outgoing = new OutgoingMessageHandler(session);
}
@ -142,6 +144,9 @@ public class ConnectionHandler
if (outgoing.isClosed())
throw new RuntimeException("Outgoing stream handler has been closed");
if (message.type == StreamMessage.Type.FILE && isPreview)
throw new RuntimeException("Cannot send file messages for preview streaming sessions");
outgoing.enqueue(message);
}
@ -191,14 +196,14 @@ public class ConnectionHandler
@SuppressWarnings("resource")
private void sendInitMessage() throws IOException
{
StreamInitMessage message = new StreamInitMessage(
FBUtilities.getBroadcastAddress(),
session.sessionIndex(),
session.planId(),
session.streamOperation(),
!isOutgoingHandler,
session.keepSSTableLevel(),
session.getPendingRepair());
StreamInitMessage message = new StreamInitMessage(FBUtilities.getBroadcastAddress(),
session.sessionIndex(),
session.planId(),
session.streamOperation(),
!isOutgoingHandler,
session.keepSSTableLevel(),
session.getPendingRepair(),
session.getPreviewKind());
ByteBuffer messageBuf = message.createMessage(false, protocolVersion);
DataOutputStreamPlus out = getWriteChannel(socket);
out.write(messageBuf);

View File

@ -0,0 +1,76 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.streaming;
import java.util.UUID;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import org.apache.cassandra.io.sstable.format.SSTableReader;
public enum PreviewKind
{
NONE(0, null),
ALL(1, Predicates.alwaysTrue()),
UNREPAIRED(2, Predicates.not(SSTableReader::isRepaired)),
REPAIRED(3, SSTableReader::isRepaired);
private final int serializationVal;
private final Predicate<SSTableReader> streamingPredicate;
PreviewKind(int serializationVal, Predicate<SSTableReader> streamingPredicate)
{
assert ordinal() == serializationVal;
this.serializationVal = serializationVal;
this.streamingPredicate = streamingPredicate;
}
public int getSerializationVal()
{
return serializationVal;
}
public static PreviewKind deserialize(int serializationVal)
{
return values()[serializationVal];
}
public Predicate<SSTableReader> getStreamingPredicate()
{
return streamingPredicate;
}
public boolean isPreview()
{
return this != NONE;
}
public String logPrefix()
{
return isPreview() ? "preview repair" : "repair";
}
public String logPrefix(UUID sessionId)
{
return '[' + logPrefix() + " #" + sessionId.toString() + ']';
}
}

View File

@ -27,6 +27,8 @@ import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import org.apache.cassandra.utils.FBUtilities;
/**
* Stream session info.
*/
@ -190,4 +192,9 @@ public final class SessionInfo implements Serializable
});
return Iterables.size(completed);
}
public SessionSummary createSummary()
{
return new SessionSummary(FBUtilities.getBroadcastAddress(), peer, receivingSummaries, sendingSummaries);
}
}

View File

@ -0,0 +1,141 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.streaming;
import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.cassandra.db.TypeSizes;
import org.apache.cassandra.io.IVersionedSerializer;
import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.serializers.InetAddressSerializer;
import org.apache.cassandra.utils.ByteBufferUtil;
public class SessionSummary
{
public final InetAddress coordinator;
public final InetAddress peer;
/** Immutable collection of receiving summaries */
public final Collection<StreamSummary> receivingSummaries;
/** Immutable collection of sending summaries*/
public final Collection<StreamSummary> sendingSummaries;
public SessionSummary(InetAddress coordinator, InetAddress peer,
Collection<StreamSummary> receivingSummaries,
Collection<StreamSummary> sendingSummaries)
{
assert coordinator != null;
assert peer != null;
assert receivingSummaries != null;
assert sendingSummaries != null;
this.coordinator = coordinator;
this.peer = peer;
this.receivingSummaries = receivingSummaries;
this.sendingSummaries = sendingSummaries;
}
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SessionSummary summary = (SessionSummary) o;
if (!coordinator.equals(summary.coordinator)) return false;
if (!peer.equals(summary.peer)) return false;
if (!receivingSummaries.equals(summary.receivingSummaries)) return false;
return sendingSummaries.equals(summary.sendingSummaries);
}
public int hashCode()
{
int result = coordinator.hashCode();
result = 31 * result + peer.hashCode();
result = 31 * result + receivingSummaries.hashCode();
result = 31 * result + sendingSummaries.hashCode();
return result;
}
public static IVersionedSerializer<SessionSummary> serializer = new IVersionedSerializer<SessionSummary>()
{
public void serialize(SessionSummary summary, DataOutputPlus out, int version) throws IOException
{
ByteBufferUtil.writeWithLength(InetAddressSerializer.instance.serialize(summary.coordinator), out);
ByteBufferUtil.writeWithLength(InetAddressSerializer.instance.serialize(summary.peer), out);
out.writeInt(summary.receivingSummaries.size());
for (StreamSummary streamSummary: summary.receivingSummaries)
{
StreamSummary.serializer.serialize(streamSummary, out, version);
}
out.writeInt(summary.sendingSummaries.size());
for (StreamSummary streamSummary: summary.sendingSummaries)
{
StreamSummary.serializer.serialize(streamSummary, out, version);
}
}
public SessionSummary deserialize(DataInputPlus in, int version) throws IOException
{
InetAddress coordinator = InetAddressSerializer.instance.deserialize(ByteBufferUtil.readWithLength(in));
InetAddress peer = InetAddressSerializer.instance.deserialize(ByteBufferUtil.readWithLength(in));
int numRcvd = in.readInt();
List<StreamSummary> receivingSummaries = new ArrayList<>(numRcvd);
for (int i=0; i<numRcvd; i++)
{
receivingSummaries.add(StreamSummary.serializer.deserialize(in, version));
}
int numSent = in.readInt();
List<StreamSummary> sendingSummaries = new ArrayList<>(numRcvd);
for (int i=0; i<numSent; i++)
{
sendingSummaries.add(StreamSummary.serializer.deserialize(in, version));
}
return new SessionSummary(coordinator, peer, receivingSummaries, sendingSummaries);
}
public long serializedSize(SessionSummary summary, int version)
{
long size = 0;
size += ByteBufferUtil.serializedSizeWithLength(InetAddressSerializer.instance.serialize(summary.coordinator));
size += ByteBufferUtil.serializedSizeWithLength(InetAddressSerializer.instance.serialize(summary.peer));
size += TypeSizes.sizeof(summary.receivingSummaries.size());
for (StreamSummary streamSummary: summary.receivingSummaries)
{
size += StreamSummary.serializer.serializedSize(streamSummary, version);
}
size += TypeSizes.sizeof(summary.sendingSummaries.size());
for (StreamSummary streamSummary: summary.sendingSummaries)
{
size += StreamSummary.serializer.serializedSize(streamSummary, version);
}
return size;
}
};
}

View File

@ -49,15 +49,17 @@ public class StreamCoordinator
private final boolean keepSSTableLevel;
private Iterator<StreamSession> sessionsToConnect = null;
private final UUID pendingRepair;
private final PreviewKind previewKind;
public StreamCoordinator(int connectionsPerHost, boolean keepSSTableLevel, StreamConnectionFactory factory,
boolean connectSequentially, UUID pendingRepair)
boolean connectSequentially, UUID pendingRepair, PreviewKind previewKind)
{
this.connectionsPerHost = connectionsPerHost;
this.factory = factory;
this.keepSSTableLevel = keepSSTableLevel;
this.connectSequentially = connectSequentially;
this.pendingRepair = pendingRepair;
this.previewKind = previewKind;
}
public void setConnectionFactory(StreamConnectionFactory factory)
@ -293,7 +295,7 @@ public class StreamCoordinator
// create
if (streamSessions.size() < connectionsPerHost)
{
StreamSession session = new StreamSession(peer, connecting, factory, streamSessions.size(), keepSSTableLevel, pendingRepair);
StreamSession session = new StreamSession(peer, connecting, factory, streamSessions.size(), keepSSTableLevel, pendingRepair, previewKind);
streamSessions.put(++lastReturned, session);
return session;
}
@ -325,7 +327,7 @@ public class StreamCoordinator
StreamSession session = streamSessions.get(id);
if (session == null)
{
session = new StreamSession(peer, connecting, factory, id, keepSSTableLevel, pendingRepair);
session = new StreamSession(peer, connecting, factory, id, keepSSTableLevel, pendingRepair, previewKind);
streamSessions.put(id, session);
}
return session;

View File

@ -22,9 +22,10 @@ import java.util.*;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.service.ActiveRepairService;
import org.apache.cassandra.utils.UUIDGen;
import static org.apache.cassandra.service.ActiveRepairService.NO_PENDING_REPAIR;
/**
* {@link StreamPlan} is a helper class that builds StreamOperation of given configuration.
*
@ -47,20 +48,20 @@ public class StreamPlan
*/
public StreamPlan(StreamOperation streamOperation)
{
this(streamOperation, 1, false, false, null);
this(streamOperation, 1, false, false, NO_PENDING_REPAIR, PreviewKind.NONE);
}
public StreamPlan(StreamOperation streamOperation, boolean keepSSTableLevels, boolean connectSequentially)
{
this(streamOperation, 1, keepSSTableLevels, connectSequentially, null);
this(streamOperation, 1, keepSSTableLevels, connectSequentially, NO_PENDING_REPAIR, PreviewKind.NONE);
}
public StreamPlan(StreamOperation streamOperation, int connectionsPerHost, boolean keepSSTableLevels,
boolean connectSequentially, UUID pendingRepair)
boolean connectSequentially, UUID pendingRepair, PreviewKind previewKind)
{
this.streamOperation = streamOperation;
this.coordinator = new StreamCoordinator(connectionsPerHost, keepSSTableLevels, new DefaultConnectionFactory(),
connectSequentially, pendingRepair);
connectSequentially, pendingRepair, previewKind);
}
/**

View File

@ -24,6 +24,7 @@ import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -92,6 +93,8 @@ public class StreamReceiveTask extends StreamTask
*/
public synchronized void received(SSTableMultiWriter sstable)
{
Preconditions.checkState(!session.isPreview(), "we should never receive sstables when previewing");
if (done)
{
logger.warn("[{}] Received sstable {} on already finished stream received task. Aborting sstable.", session.planId(),

View File

@ -71,9 +71,9 @@ public final class StreamResultFuture extends AbstractFuture<StreamState>
set(getCurrentState());
}
private StreamResultFuture(UUID planId, StreamOperation streamOperation, boolean keepSSTableLevels, UUID pendingRepair)
private StreamResultFuture(UUID planId, StreamOperation streamOperation, boolean keepSSTableLevels, UUID pendingRepair, PreviewKind previewKind)
{
this(planId, streamOperation, new StreamCoordinator(0, keepSSTableLevels, new DefaultConnectionFactory(), false, pendingRepair));
this(planId, streamOperation, new StreamCoordinator(0, keepSSTableLevels, new DefaultConnectionFactory(), false, pendingRepair, previewKind));
}
static StreamResultFuture init(UUID planId, StreamOperation streamOperation, Collection<StreamEventHandler> listeners,
@ -107,7 +107,8 @@ public final class StreamResultFuture extends AbstractFuture<StreamState>
boolean isForOutgoing,
int version,
boolean keepSSTableLevel,
UUID pendingRepair) throws IOException
UUID pendingRepair,
PreviewKind previewKind) throws IOException
{
StreamResultFuture future = StreamManager.instance.getReceivingStream(planId);
if (future == null)
@ -115,7 +116,7 @@ public final class StreamResultFuture extends AbstractFuture<StreamState>
logger.info("[Stream #{} ID#{}] Creating new streaming plan for {}", planId, sessionIndex, streamOperation.getDescription());
// The main reason we create a StreamResultFuture on the receiving side is for JMX exposure.
future = new StreamResultFuture(planId, streamOperation, keepSSTableLevel, pendingRepair);
future = new StreamResultFuture(planId, streamOperation, keepSSTableLevel, pendingRepair, previewKind);
StreamManager.instance.registerReceiving(future);
}
future.attachConnection(from, sessionIndex, connection, isForOutgoing, version);

View File

@ -165,6 +165,7 @@ public class StreamSession implements IEndpointStateChangeSubscriber
private final boolean keepSSTableLevel;
private ScheduledFuture<?> keepAliveFuture = null;
private final UUID pendingRepair;
private final PreviewKind previewKind;
public static enum State
{
@ -181,12 +182,11 @@ public class StreamSession implements IEndpointStateChangeSubscriber
/**
* Create new streaming session with the peer.
*
* @param peer Address of streaming peer
* @param peer Address of streaming peer
* @param connecting Actual connecting address
* @param factory is used for establishing connection
*/
public StreamSession(InetAddress peer, InetAddress connecting, StreamConnectionFactory factory, int index, boolean keepSSTableLevel, UUID pendingRepair)
public StreamSession(InetAddress peer, InetAddress connecting, StreamConnectionFactory factory, int index, boolean keepSSTableLevel, UUID pendingRepair, PreviewKind previewKind)
{
this.peer = peer;
this.connecting = connecting;
@ -194,10 +194,11 @@ public class StreamSession implements IEndpointStateChangeSubscriber
this.factory = factory;
this.handler = new ConnectionHandler(this, isKeepAliveSupported()?
(int)TimeUnit.SECONDS.toMillis(2 * DatabaseDescriptor.getStreamingKeepAlivePeriod()) :
DatabaseDescriptor.getStreamingSocketTimeout());
DatabaseDescriptor.getStreamingSocketTimeout(), previewKind.isPreview());
this.metrics = StreamingMetrics.get(connecting);
this.keepSSTableLevel = keepSSTableLevel;
this.pendingRepair = pendingRepair;
this.previewKind = previewKind;
}
public UUID planId()
@ -225,6 +226,16 @@ public class StreamSession implements IEndpointStateChangeSubscriber
return pendingRepair;
}
public boolean isPreview()
{
return previewKind.isPreview();
}
public PreviewKind getPreviewKind()
{
return previewKind;
}
public LifecycleTransaction getTransaction(TableId tableId)
{
assert receivers.containsKey(tableId);
@ -314,7 +325,7 @@ public class StreamSession implements IEndpointStateChangeSubscriber
flushSSTables(stores);
List<Range<Token>> normalizedRanges = Range.normalize(ranges);
List<SSTableStreamingSections> sections = getSSTableSectionsForRanges(normalizedRanges, stores, pendingRepair);
List<SSTableStreamingSections> sections = getSSTableSectionsForRanges(normalizedRanges, stores, pendingRepair, previewKind);
try
{
addTransferFiles(sections);
@ -356,7 +367,7 @@ public class StreamSession implements IEndpointStateChangeSubscriber
}
@VisibleForTesting
public static List<SSTableStreamingSections> getSSTableSectionsForRanges(Collection<Range<Token>> ranges, Collection<ColumnFamilyStore> stores, UUID pendingRepair)
public static List<SSTableStreamingSections> getSSTableSectionsForRanges(Collection<Range<Token>> ranges, Collection<ColumnFamilyStore> stores, UUID pendingRepair, PreviewKind previewKind)
{
Refs<SSTableReader> refs = new Refs<>();
try
@ -370,7 +381,11 @@ public class StreamSession implements IEndpointStateChangeSubscriber
Set<SSTableReader> sstables = Sets.newHashSet();
SSTableIntervalTree intervalTree = SSTableIntervalTree.build(view.select(SSTableSet.CANONICAL));
Predicate<SSTableReader> predicate;
if (pendingRepair == ActiveRepairService.NO_PENDING_REPAIR)
if (previewKind.isPreview())
{
predicate = previewKind.getStreamingPredicate();
}
else if (pendingRepair == ActiveRepairService.NO_PENDING_REPAIR)
{
predicate = Predicates.alwaysTrue();
}
@ -620,6 +635,12 @@ public class StreamSession implements IEndpointStateChangeSubscriber
handler.sendMessage(prepare);
}
if (isPreview())
{
completePreview();
return;
}
// if there are files to stream
if (!maybeCompleted())
startStreamingFiles();
@ -650,6 +671,11 @@ public class StreamSession implements IEndpointStateChangeSubscriber
*/
public void receive(IncomingFileMessage message)
{
if (isPreview())
{
throw new RuntimeException("Cannot receive files for preview session");
}
long headerSize = message.header.size();
StreamingMetrics.totalIncomingBytes.inc(headerSize);
metrics.incomingBytes.inc(headerSize);
@ -753,6 +779,22 @@ public class StreamSession implements IEndpointStateChangeSubscriber
closeSession(State.FAILED);
}
private void completePreview()
{
try
{
state(State.WAIT_COMPLETE);
closeSession(State.COMPLETE);
}
finally
{
// aborting the tasks here needs to be the last thing we do so that we
// accurately report expected streaming, but don't leak any sstable refs
for (StreamTask task : Iterables.concat(receivers.values(), transfers.values()))
task.abort();
}
}
private boolean maybeCompleted()
{
boolean completed = receivers.isEmpty() && transfers.isEmpty();
@ -803,6 +845,7 @@ public class StreamSession implements IEndpointStateChangeSubscriber
streamResult.handleSessionPrepared(this);
state(State.STREAMING);
for (StreamTransferTask task : transfers.values())
{
Collection<OutgoingFileMessage> messages = task.getFileMessages();

View File

@ -18,11 +18,13 @@
package org.apache.cassandra.streaming;
import java.io.Serializable;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
/**
* Current snapshot of streaming progress.
@ -50,4 +52,9 @@ public class StreamState implements Serializable
}
});
}
public List<SessionSummary> createSummaries()
{
return Lists.newArrayList(Iterables.transform(sessions, SessionInfo::createSummary));
}
}

View File

@ -31,6 +31,7 @@ import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.net.CompactEndpointSerializationHelper;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.streaming.StreamOperation;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.utils.UUIDSerializer;
/**
@ -50,8 +51,9 @@ public class StreamInitMessage
public final boolean isForOutgoing;
public final boolean keepSSTableLevel;
public final UUID pendingRepair;
public final PreviewKind previewKind;
public StreamInitMessage(InetAddress from, int sessionIndex, UUID planId, StreamOperation streamOperation, boolean isForOutgoing, boolean keepSSTableLevel, UUID pendingRepair)
public StreamInitMessage(InetAddress from, int sessionIndex, UUID planId, StreamOperation streamOperation, boolean isForOutgoing, boolean keepSSTableLevel, UUID pendingRepair, PreviewKind previewKind)
{
this.from = from;
this.sessionIndex = sessionIndex;
@ -60,6 +62,7 @@ public class StreamInitMessage
this.isForOutgoing = isForOutgoing;
this.keepSSTableLevel = keepSSTableLevel;
this.pendingRepair = pendingRepair;
this.previewKind = previewKind;
}
/**
@ -120,6 +123,7 @@ public class StreamInitMessage
{
UUIDSerializer.serializer.serialize(message.pendingRepair, out, MessagingService.current_version);
}
out.writeInt(message.previewKind.getSerializationVal());
}
public StreamInitMessage deserialize(DataInputPlus in, int version) throws IOException
@ -132,7 +136,8 @@ public class StreamInitMessage
boolean keepSSTableLevel = in.readBoolean();
UUID pendingRepair = in.readBoolean() ? UUIDSerializer.serializer.deserialize(in, version) : null;
return new StreamInitMessage(from, sessionIndex, planId, StreamOperation.fromString(description), sentByInitiator, keepSSTableLevel, pendingRepair);
PreviewKind previewKind = PreviewKind.deserialize(in.readInt());
return new StreamInitMessage(from, sessionIndex, planId, StreamOperation.fromString(description), sentByInitiator, keepSSTableLevel, pendingRepair, previewKind);
}
public long serializedSize(StreamInitMessage message, int version)
@ -148,6 +153,7 @@ public class StreamInitMessage
{
size += UUIDSerializer.serializer.serializedSize(message.pendingRepair, MessagingService.current_version);
}
size += TypeSizes.sizeof(message.previewKind.getSerializationVal());
return size;
}
}

View File

@ -32,6 +32,7 @@ import java.util.Set;
import com.google.common.collect.Sets;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.repair.RepairParallelism;
import org.apache.cassandra.repair.messages.RepairOption;
import org.apache.cassandra.tools.NodeProbe;
@ -73,6 +74,12 @@ public class Repair extends NodeToolCmd
@Option(title = "full", name = {"-full", "--full"}, description = "Use -full to issue a full repair.")
private boolean fullRepair = false;
@Option(title = "preview", name = {"-prv", "--preview"}, description = "Determine ranges and amount of data to be streamed, but don't actually perform repair")
private boolean preview = false;
@Option(title = "validate", name = {"-vd", "--validate"}, description = "Checks that repaired data is in sync between nodes. Out of sync repaired data indicates a full repair should be run.")
private boolean validate = false;
@Option(title = "job_threads", name = {"-j", "--job-threads"}, description = "Number of threads to run repair jobs. " +
"Usually this means number of CFs to repair concurrently. " +
"WARNING: increasing this puts more load on repairing nodes, so be careful. (default: 1, max: 4)")
@ -84,6 +91,26 @@ public class Repair extends NodeToolCmd
@Option(title = "pull_repair", name = {"-pl", "--pull"}, description = "Use --pull to perform a one way repair where data is only streamed from a remote node to this node.")
private boolean pullRepair = false;
private PreviewKind getPreviewKind()
{
if (validate)
{
return PreviewKind.REPAIRED;
}
else if (preview && fullRepair)
{
return PreviewKind.ALL;
}
else if (preview)
{
return PreviewKind.UNREPAIRED;
}
else
{
return PreviewKind.NONE;
}
}
@Override
public void execute(NodeProbe probe)
{
@ -112,6 +139,8 @@ public class Repair extends NodeToolCmd
options.put(RepairOption.TRACE_KEY, Boolean.toString(trace));
options.put(RepairOption.COLUMNFAMILIES_KEY, StringUtils.join(cfnames, ","));
options.put(RepairOption.PULL_REPAIR_KEY, Boolean.toString(pullRepair));
options.put(RepairOption.PREVIEW, getPreviewKind().toString());
if (!startToken.isEmpty() || !endToken.isEmpty())
{
options.put(RepairOption.RANGES_KEY, startToken + ":" + endToken);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -36,10 +36,11 @@ import java.util.Map;
public class AbstractSerializationsTester
{
protected static final String CUR_VER = System.getProperty("cassandra.version", "3.0");
protected static final String CUR_VER = System.getProperty("cassandra.version", "4.0");
protected static final Map<String, Integer> VERSION_MAP = new HashMap<String, Integer> ()
{{
put("3.0", MessagingService.VERSION_30);
put("4.0", MessagingService.VERSION_40);
}};
protected static final boolean EXECUTE_WRITES = Boolean.getBoolean("cassandra.test-serialization-writes");

View File

@ -41,6 +41,7 @@ import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.repair.RepairJobDesc;
import org.apache.cassandra.repair.Validator;
import org.apache.cassandra.schema.KeyspaceParams;
@ -106,7 +107,8 @@ public class CompactionManagerGetSSTablesForValidationTest
Sets.newHashSet(range),
incremental,
incremental ? System.currentTimeMillis() : ActiveRepairService.UNREPAIRED_SSTABLE,
true);
true,
PreviewKind.NONE);
desc = new RepairJobDesc(sessionID, UUIDGen.getTimeUUID(), ks, tbl, Collections.singleton(range));
}
@ -135,7 +137,7 @@ public class CompactionManagerGetSSTablesForValidationTest
modifySSTables();
// get sstables for repair
Validator validator = new Validator(desc, coordinator, FBUtilities.nowInSeconds(), true);
Validator validator = new Validator(desc, coordinator, FBUtilities.nowInSeconds(), true, PreviewKind.NONE);
Set<SSTableReader> sstables = Sets.newHashSet(CompactionManager.instance.getSSTablesToValidate(cfs, validator));
Assert.assertNotNull(sstables);
Assert.assertEquals(1, sstables.size());
@ -150,7 +152,7 @@ public class CompactionManagerGetSSTablesForValidationTest
modifySSTables();
// get sstables for repair
Validator validator = new Validator(desc, coordinator, FBUtilities.nowInSeconds(), false);
Validator validator = new Validator(desc, coordinator, FBUtilities.nowInSeconds(), false, PreviewKind.NONE);
Set<SSTableReader> sstables = Sets.newHashSet(CompactionManager.instance.getSSTablesToValidate(cfs, validator));
Assert.assertNotNull(sstables);
Assert.assertEquals(2, sstables.size());
@ -166,7 +168,7 @@ public class CompactionManagerGetSSTablesForValidationTest
modifySSTables();
// get sstables for repair
Validator validator = new Validator(desc, coordinator, FBUtilities.nowInSeconds(), false);
Validator validator = new Validator(desc, coordinator, FBUtilities.nowInSeconds(), false, PreviewKind.NONE);
Set<SSTableReader> sstables = Sets.newHashSet(CompactionManager.instance.getSSTablesToValidate(cfs, validator));
Assert.assertNotNull(sstables);
Assert.assertEquals(3, sstables.size());

View File

@ -51,6 +51,7 @@ import org.apache.cassandra.io.sstable.ISSTableScanner;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.notifications.SSTableAddedNotification;
import org.apache.cassandra.notifications.SSTableRepairStatusChanged;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.repair.RepairJobDesc;
import org.apache.cassandra.repair.Validator;
import org.apache.cassandra.schema.CompactionParams;
@ -193,9 +194,16 @@ public class LeveledCompactionStrategyTest
Range<Token> range = new Range<>(Util.token(""), Util.token(""));
int gcBefore = keyspace.getColumnFamilyStore(CF_STANDARDDLEVELED).gcBefore(FBUtilities.nowInSeconds());
UUID parentRepSession = UUID.randomUUID();
ActiveRepairService.instance.registerParentRepairSession(parentRepSession, FBUtilities.getBroadcastAddress(), Arrays.asList(cfs), Arrays.asList(range), false, ActiveRepairService.UNREPAIRED_SSTABLE, true);
ActiveRepairService.instance.registerParentRepairSession(parentRepSession,
FBUtilities.getBroadcastAddress(),
Arrays.asList(cfs),
Arrays.asList(range),
false,
ActiveRepairService.UNREPAIRED_SSTABLE,
true,
PreviewKind.NONE);
RepairJobDesc desc = new RepairJobDesc(parentRepSession, UUID.randomUUID(), KEYSPACE1, CF_STANDARDDLEVELED, Arrays.asList(range));
Validator validator = new Validator(desc, FBUtilities.getBroadcastAddress(), gcBefore);
Validator validator = new Validator(desc, FBUtilities.getBroadcastAddress(), gcBefore, PreviewKind.NONE);
CompactionManager.instance.submitValidation(cfs, validator).get();
}

View File

@ -25,6 +25,7 @@ import org.junit.Test;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.streaming.DefaultConnectionFactory;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.streaming.StreamEvent;
import org.apache.cassandra.streaming.StreamSession;
import org.apache.cassandra.utils.FBUtilities;
@ -50,7 +51,7 @@ public class StreamStateStoreTest
Range<Token> range = new Range<>(factory.fromString("0"), factory.fromString("100"));
InetAddress local = FBUtilities.getBroadcastAddress();
StreamSession session = new StreamSession(local, local, new DefaultConnectionFactory(), 0, true, null);
StreamSession session = new StreamSession(local, local, new DefaultConnectionFactory(), 0, true, null, PreviewKind.NONE);
session.addStreamRequest("keyspace1", Collections.singleton(range), Collections.singleton("cf"));
StreamStateStore store = new StreamStateStore();
@ -71,7 +72,7 @@ public class StreamStateStoreTest
// add different range within the same keyspace
Range<Token> range2 = new Range<>(factory.fromString("100"), factory.fromString("200"));
session = new StreamSession(local, local, new DefaultConnectionFactory(), 0, true, null);
session = new StreamSession(local, local, new DefaultConnectionFactory(), 0, true, null, PreviewKind.NONE);
session.addStreamRequest("keyspace1", Collections.singleton(range2), Collections.singleton("cf"));
session.state(StreamSession.State.COMPLETE);
store.handleStreamEvent(new StreamEvent.SessionCompleteEvent(session));

View File

@ -56,6 +56,7 @@ import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.db.lifecycle.LifecycleTransaction;
import org.apache.cassandra.metrics.StorageMetrics;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.streaming.StreamSession;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
@ -789,7 +790,7 @@ public class SSTableRewriterTest extends SSTableWriterTestBase
List<StreamSession.SSTableStreamingSections> sectionsBeforeRewrite = StreamSession.getSSTableSectionsForRanges(
Collections.singleton(new Range<Token>(firstToken, firstToken)),
Collections.singleton(cfs), null);
Collections.singleton(cfs), null, PreviewKind.NONE);
assertEquals(1, sectionsBeforeRewrite.size());
for (StreamSession.SSTableStreamingSections section : sectionsBeforeRewrite)
section.ref.release();
@ -804,7 +805,7 @@ public class SSTableRewriterTest extends SSTableWriterTestBase
while (!done.get())
{
Set<Range<Token>> range = Collections.singleton(new Range<Token>(firstToken, firstToken));
List<StreamSession.SSTableStreamingSections> sections = StreamSession.getSSTableSectionsForRanges(range, Collections.singleton(cfs), null);
List<StreamSession.SSTableStreamingSections> sections = StreamSession.getSSTableSectionsForRanges(range, Collections.singleton(cfs), null, PreviewKind.NONE);
if (sections.size() != 1)
failed.set(true);
for (StreamSession.SSTableStreamingSections section : sections)

View File

@ -34,6 +34,7 @@ import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.service.ActiveRepairService;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.UUIDGen;
@ -85,7 +86,8 @@ public abstract class AbstractRepairTest
Sets.newHashSet(RANGE1, RANGE2, RANGE3),
isIncremental,
repairedAt,
isGlobal);
isGlobal,
PreviewKind.NONE);
return sessionId;
}
}

View File

@ -40,6 +40,7 @@ import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.schema.TableId;
import org.apache.cassandra.service.ActiveRepairService;
import org.apache.cassandra.streaming.StreamPlan;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.MerkleTree;
import org.apache.cassandra.utils.MerkleTrees;
@ -91,7 +92,7 @@ public class LocalSyncTaskTest extends AbstractRepairTest
// note: we reuse the same endpoint which is bogus in theory but fine here
TreeResponse r1 = new TreeResponse(ep1, tree1);
TreeResponse r2 = new TreeResponse(ep2, tree2);
LocalSyncTask task = new LocalSyncTask(desc, r1, r2, null, false);
LocalSyncTask task = new LocalSyncTask(desc, r1, r2, NO_PENDING_REPAIR, false, PreviewKind.NONE);
task.run();
assertEquals(0, task.get().numberOfDifferences);
@ -105,9 +106,10 @@ public class LocalSyncTaskTest extends AbstractRepairTest
Keyspace keyspace = Keyspace.open(KEYSPACE1);
ColumnFamilyStore cfs = keyspace.getColumnFamilyStore("Standard1");
ActiveRepairService.instance.registerParentRepairSession(parentRepairSession, FBUtilities.getBroadcastAddress(),
ActiveRepairService.instance.registerParentRepairSession(parentRepairSession, FBUtilities.getBroadcastAddress(),
Arrays.asList(cfs), Arrays.asList(range), false,
ActiveRepairService.UNREPAIRED_SSTABLE, false);
ActiveRepairService.UNREPAIRED_SSTABLE, false,
PreviewKind.NONE);
RepairJobDesc desc = new RepairJobDesc(parentRepairSession, UUID.randomUUID(), KEYSPACE1, "Standard1", Arrays.asList(range));
@ -128,7 +130,7 @@ public class LocalSyncTaskTest extends AbstractRepairTest
// note: we reuse the same endpoint which is bogus in theory but fine here
TreeResponse r1 = new TreeResponse(InetAddress.getByName("127.0.0.1"), tree1);
TreeResponse r2 = new TreeResponse(InetAddress.getByName("127.0.0.2"), tree2);
LocalSyncTask task = new LocalSyncTask(desc, r1, r2, null, false);
LocalSyncTask task = new LocalSyncTask(desc, r1, r2, NO_PENDING_REPAIR, false, PreviewKind.NONE);
task.run();
// ensure that the changed range was recorded
@ -145,7 +147,7 @@ public class LocalSyncTaskTest extends AbstractRepairTest
TreeResponse r1 = new TreeResponse(PARTICIPANT1, createInitialTree(desc, DatabaseDescriptor.getPartitioner()));
TreeResponse r2 = new TreeResponse(PARTICIPANT2, createInitialTree(desc, DatabaseDescriptor.getPartitioner()));
LocalSyncTask task = new LocalSyncTask(desc, r1, r2, NO_PENDING_REPAIR, false);
LocalSyncTask task = new LocalSyncTask(desc, r1, r2, NO_PENDING_REPAIR, false, PreviewKind.NONE);
StreamPlan plan = task.createStreamPlan(PARTICIPANT1, PARTICIPANT2, Lists.newArrayList(RANGE1));
assertEquals(NO_PENDING_REPAIR, plan.getPendingRepair());
@ -162,7 +164,7 @@ public class LocalSyncTaskTest extends AbstractRepairTest
TreeResponse r1 = new TreeResponse(PARTICIPANT1, createInitialTree(desc, DatabaseDescriptor.getPartitioner()));
TreeResponse r2 = new TreeResponse(PARTICIPANT2, createInitialTree(desc, DatabaseDescriptor.getPartitioner()));
LocalSyncTask task = new LocalSyncTask(desc, r1, r2, desc.parentSessionId, false);
LocalSyncTask task = new LocalSyncTask(desc, r1, r2, desc.parentSessionId, false, PreviewKind.NONE);
StreamPlan plan = task.createStreamPlan(PARTICIPANT1, PARTICIPANT2, Lists.newArrayList(RANGE1));
assertEquals(desc.parentSessionId, plan.getPendingRepair());

View File

@ -36,6 +36,7 @@ import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.gms.Gossiper;
import org.apache.cassandra.service.ActiveRepairService;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.UUIDGen;
@ -62,7 +63,10 @@ public class RepairSessionTest
IPartitioner p = Murmur3Partitioner.instance;
Range<Token> repairRange = new Range<>(p.getToken(ByteBufferUtil.bytes(0)), p.getToken(ByteBufferUtil.bytes(100)));
Set<InetAddress> endpoints = Sets.newHashSet(remote);
RepairSession session = new RepairSession(parentSessionId, sessionId, Arrays.asList(repairRange), "Keyspace1", RepairParallelism.SEQUENTIAL, endpoints, false, false, "Standard1");
RepairSession session = new RepairSession(parentSessionId, sessionId, Arrays.asList(repairRange),
"Keyspace1", RepairParallelism.SEQUENTIAL,
endpoints, false, false,
PreviewKind.NONE, "Standard1");
// perform convict
session.convict(remote, Double.MAX_VALUE);

View File

@ -33,6 +33,7 @@ import org.apache.cassandra.schema.KeyspaceParams;
import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.ActiveRepairService;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.streaming.StreamPlan;
import org.apache.cassandra.utils.UUIDGen;
@ -64,8 +65,8 @@ public class StreamingRepairTaskTest extends AbstractRepairTest
UUID sessionID = registerSession(cfs, true, true);
ActiveRepairService.ParentRepairSession prs = ActiveRepairService.instance.getParentRepairSession(sessionID);
RepairJobDesc desc = new RepairJobDesc(sessionID, UUIDGen.getTimeUUID(), ks, tbl, prs.getRanges());
SyncRequest request = new SyncRequest(desc, PARTICIPANT1, PARTICIPANT2, PARTICIPANT3, prs.getRanges());
StreamingRepairTask task = new StreamingRepairTask(desc, request, desc.sessionId);
SyncRequest request = new SyncRequest(desc, PARTICIPANT1, PARTICIPANT2, PARTICIPANT3, prs.getRanges(), PreviewKind.NONE);
StreamingRepairTask task = new StreamingRepairTask(desc, request, desc.sessionId, PreviewKind.NONE);
StreamPlan plan = task.createStreamPlan(request.src, request.dst);
Assert.assertFalse(plan.getFlushBeforeTransfer());
@ -77,8 +78,8 @@ public class StreamingRepairTaskTest extends AbstractRepairTest
UUID sessionID = registerSession(cfs, false, true);
ActiveRepairService.ParentRepairSession prs = ActiveRepairService.instance.getParentRepairSession(sessionID);
RepairJobDesc desc = new RepairJobDesc(sessionID, UUIDGen.getTimeUUID(), ks, tbl, prs.getRanges());
SyncRequest request = new SyncRequest(desc, PARTICIPANT1, PARTICIPANT2, PARTICIPANT3, prs.getRanges());
StreamingRepairTask task = new StreamingRepairTask(desc, request, null);
SyncRequest request = new SyncRequest(desc, PARTICIPANT1, PARTICIPANT2, PARTICIPANT3, prs.getRanges(), PreviewKind.NONE);
StreamingRepairTask task = new StreamingRepairTask(desc, request, null, PreviewKind.NONE);
StreamPlan plan = task.createStreamPlan(request.src, request.dst);
Assert.assertTrue(plan.getFlushBeforeTransfer());

View File

@ -50,6 +50,7 @@ import org.apache.cassandra.repair.messages.RepairMessage;
import org.apache.cassandra.repair.messages.ValidationComplete;
import org.apache.cassandra.schema.KeyspaceParams;
import org.apache.cassandra.service.ActiveRepairService;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.MerkleTree;
import org.apache.cassandra.utils.MerkleTrees;
@ -98,7 +99,7 @@ public class ValidatorTest
ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(columnFamily);
Validator validator = new Validator(desc, remote, 0);
Validator validator = new Validator(desc, remote, 0, PreviewKind.NONE);
MerkleTrees tree = new MerkleTrees(partitioner);
tree.addMerkleTrees((int) Math.pow(2, 15), validator.desc.ranges);
validator.prepare(cfs, tree);
@ -135,7 +136,7 @@ public class ValidatorTest
InetAddress remote = InetAddress.getByName("127.0.0.2");
Validator validator = new Validator(desc, remote, 0);
Validator validator = new Validator(desc, remote, 0, PreviewKind.NONE);
validator.fail();
MessageOut message = outgoingMessageSink.get(TEST_TIMEOUT, TimeUnit.SECONDS);
@ -190,10 +191,10 @@ public class ValidatorTest
ActiveRepairService.instance.registerParentRepairSession(repairSessionId, FBUtilities.getBroadcastAddress(),
Collections.singletonList(cfs), desc.ranges, false, ActiveRepairService.UNREPAIRED_SSTABLE,
false);
false, PreviewKind.NONE);
final CompletableFuture<MessageOut> outgoingMessageSink = registerOutgoingMessageSink();
Validator validator = new Validator(desc, FBUtilities.getBroadcastAddress(), 0, true, false);
Validator validator = new Validator(desc, FBUtilities.getBroadcastAddress(), 0, true, false, PreviewKind.NONE);
CompactionManager.instance.submitValidation(cfs, validator);
MessageOut message = outgoingMessageSink.get(TEST_TIMEOUT, TimeUnit.SECONDS);

View File

@ -33,6 +33,7 @@ import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.service.ActiveRepairService;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.UUIDGen;
@ -85,7 +86,8 @@ public abstract class AbstractConsistentSessionTest
Sets.newHashSet(RANGE1, RANGE2, RANGE3),
true,
System.currentTimeMillis(),
true);
true,
PreviewKind.NONE);
return sessionId;
}
}

View File

@ -52,6 +52,7 @@ import org.apache.cassandra.dht.ByteOrderedPartitioner;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.schema.KeyspaceParams;
import org.apache.cassandra.service.ActiveRepairService;
import org.apache.cassandra.utils.ByteBufferUtil;
@ -146,7 +147,7 @@ public class PendingAntiCompactionTest
// create a session so the anti compaction can fine it
UUID sessionID = UUIDGen.getTimeUUID();
ActiveRepairService.instance.registerParentRepairSession(sessionID, InetAddress.getLocalHost(), Lists.newArrayList(cfs), ranges, true, 1, true);
ActiveRepairService.instance.registerParentRepairSession(sessionID, InetAddress.getLocalHost(), Lists.newArrayList(cfs), ranges, true, 1, true, PreviewKind.NONE);
PendingAntiCompaction pac;
ExecutorService executor = Executors.newSingleThreadExecutor();

View File

@ -25,6 +25,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import com.google.common.collect.Lists;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
@ -43,10 +44,14 @@ import org.apache.cassandra.io.util.DataOutputBufferFixed;
import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.repair.NodePair;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.repair.RepairJobDesc;
import org.apache.cassandra.schema.TableId;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.streaming.SessionSummary;
import org.apache.cassandra.streaming.StreamSummary;
import org.apache.cassandra.utils.MerkleTrees;
import org.apache.cassandra.utils.UUIDGen;
public class RepairMessageSerializationsTest
{
@ -145,7 +150,7 @@ public class RepairMessageSerializationsTest
InetAddress src = InetAddress.getByName("127.0.0.2");
InetAddress dst = InetAddress.getByName("127.0.0.3");
SyncRequest msg = new SyncRequest(buildRepairJobDesc(), initiator, src, dst, buildTokenRanges());
SyncRequest msg = new SyncRequest(buildRepairJobDesc(), initiator, src, dst, buildTokenRanges(), PreviewKind.NONE);
serializeRoundTrip(msg, SyncRequest.serializer);
}
@ -154,7 +159,12 @@ public class RepairMessageSerializationsTest
{
InetAddress src = InetAddress.getByName("127.0.0.2");
InetAddress dst = InetAddress.getByName("127.0.0.3");
SyncComplete msg = new SyncComplete(buildRepairJobDesc(), new NodePair(src, dst), true);
List<SessionSummary> summaries = new ArrayList<>();
summaries.add(new SessionSummary(src, dst,
Lists.newArrayList(new StreamSummary(TableId.fromUUID(UUIDGen.getTimeUUID()), 5, 100)),
Lists.newArrayList(new StreamSummary(TableId.fromUUID(UUIDGen.getTimeUUID()), 500, 10))
));
SyncComplete msg = new SyncComplete(buildRepairJobDesc(), new NodePair(src, dst), true, summaries);
serializeRoundTrip(msg, SyncComplete.serializer);
}
@ -162,7 +172,8 @@ public class RepairMessageSerializationsTest
public void prepareMessage() throws IOException
{
PrepareMessage msg = new PrepareMessage(UUID.randomUUID(), new ArrayList<TableId>() {{add(TableId.generate());}},
buildTokenRanges(), true, 100000L, false);
buildTokenRanges(), true, 100000L, false,
PreviewKind.NONE);
serializeRoundTrip(msg, PrepareMessage.serializer);
}

View File

@ -20,7 +20,6 @@ package org.apache.cassandra.service;
import java.net.InetAddress;
import java.util.*;
import java.util.concurrent.ExecutionException;
import com.google.common.collect.Sets;
import org.junit.Before;
@ -40,6 +39,7 @@ import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.locator.AbstractReplicationStrategy;
import org.apache.cassandra.locator.TokenMetadata;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.schema.KeyspaceParams;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.concurrent.Refs;
@ -230,11 +230,19 @@ public class ActiveRepairServiceTest
ColumnFamilyStore store = prepareColumnFamilyStore();
UUID prsId = UUID.randomUUID();
Set<SSTableReader> original = Sets.newHashSet(store.select(View.select(SSTableSet.CANONICAL, (s) -> !s.isRepaired())).sstables);
ActiveRepairService.instance.registerParentRepairSession(prsId, FBUtilities.getBroadcastAddress(), Collections.singletonList(store), Collections.singleton(new Range<>(store.getPartitioner().getMinimumToken(), store.getPartitioner().getMinimumToken())), true, System.currentTimeMillis(), true);
ActiveRepairService.instance.registerParentRepairSession(prsId, FBUtilities.getBroadcastAddress(), Collections.singletonList(store),
Collections.singleton(new Range<>(store.getPartitioner().getMinimumToken(),
store.getPartitioner().getMinimumToken())),
true, System.currentTimeMillis(), true, PreviewKind.NONE);
ActiveRepairService.instance.getParentRepairSession(prsId).maybeSnapshot(store.metadata.id, prsId);
UUID prsId2 = UUID.randomUUID();
ActiveRepairService.instance.registerParentRepairSession(prsId2, FBUtilities.getBroadcastAddress(), Collections.singletonList(store), Collections.singleton(new Range<>(store.getPartitioner().getMinimumToken(), store.getPartitioner().getMinimumToken())), true, System.currentTimeMillis(), true);
ActiveRepairService.instance.registerParentRepairSession(prsId2, FBUtilities.getBroadcastAddress(),
Collections.singletonList(store),
Collections.singleton(new Range<>(store.getPartitioner().getMinimumToken(),
store.getPartitioner().getMinimumToken())),
true, System.currentTimeMillis(),
true, PreviewKind.NONE);
createSSTables(store, 2);
ActiveRepairService.instance.getParentRepairSession(prsId).maybeSnapshot(store.metadata.id, prsId);
try (Refs<SSTableReader> refs = store.getSnapshotSSTableReaders(prsId.toString()))

View File

@ -20,10 +20,13 @@ package org.apache.cassandra.service;
import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import com.google.common.collect.Lists;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
@ -43,8 +46,13 @@ import org.apache.cassandra.repair.NodePair;
import org.apache.cassandra.repair.RepairJobDesc;
import org.apache.cassandra.repair.Validator;
import org.apache.cassandra.repair.messages.*;
import org.apache.cassandra.schema.TableId;
import org.apache.cassandra.streaming.PreviewKind;
import org.apache.cassandra.streaming.SessionSummary;
import org.apache.cassandra.streaming.StreamSummary;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.MerkleTrees;
import org.apache.cassandra.utils.UUIDGen;
public class SerializationsTest extends AbstractSerializationsTester
{
@ -115,7 +123,7 @@ public class SerializationsTest extends AbstractSerializationsTester
// empty validation
mt.addMerkleTree((int) Math.pow(2, 15), FULL_RANGE);
Validator v0 = new Validator(DESC, FBUtilities.getBroadcastAddress(), -1);
Validator v0 = new Validator(DESC, FBUtilities.getBroadcastAddress(), -1, PreviewKind.NONE);
ValidationComplete c0 = new ValidationComplete(DESC, mt);
// validation with a tree
@ -123,7 +131,7 @@ public class SerializationsTest extends AbstractSerializationsTester
mt.addMerkleTree(Integer.MAX_VALUE, FULL_RANGE);
for (int i = 0; i < 10; i++)
mt.split(p.getRandomToken());
Validator v1 = new Validator(DESC, FBUtilities.getBroadcastAddress(), -1);
Validator v1 = new Validator(DESC, FBUtilities.getBroadcastAddress(), -1, PreviewKind.NONE);
ValidationComplete c1 = new ValidationComplete(DESC, mt);
// validation failed
@ -175,8 +183,8 @@ public class SerializationsTest extends AbstractSerializationsTester
InetAddress local = InetAddress.getByAddress(new byte[]{127, 0, 0, 1});
InetAddress src = InetAddress.getByAddress(new byte[]{127, 0, 0, 2});
InetAddress dest = InetAddress.getByAddress(new byte[]{127, 0, 0, 3});
SyncRequest message = new SyncRequest(DESC, local, src, dest, Collections.singleton(FULL_RANGE));
SyncRequest message = new SyncRequest(DESC, local, src, dest, Collections.singleton(FULL_RANGE), PreviewKind.NONE);
testRepairMessageWrite("service.SyncRequest.bin", message);
}
@ -209,9 +217,14 @@ public class SerializationsTest extends AbstractSerializationsTester
InetAddress src = InetAddress.getByAddress(new byte[]{127, 0, 0, 2});
InetAddress dest = InetAddress.getByAddress(new byte[]{127, 0, 0, 3});
// sync success
SyncComplete success = new SyncComplete(DESC, src, dest, true);
List<SessionSummary> summaries = new ArrayList<>();
summaries.add(new SessionSummary(src, dest,
Lists.newArrayList(new StreamSummary(TableId.fromUUID(UUIDGen.getTimeUUID()), 5, 100)),
Lists.newArrayList(new StreamSummary(TableId.fromUUID(UUIDGen.getTimeUUID()), 500, 10))
));
SyncComplete success = new SyncComplete(DESC, src, dest, true, summaries);
// sync fail
SyncComplete fail = new SyncComplete(DESC, src, dest, false);
SyncComplete fail = new SyncComplete(DESC, src, dest, false, Collections.emptyList());
testRepairMessageWrite("service.SyncComplete.bin", success, fail);
}

View File

@ -97,7 +97,8 @@ public class StreamSessionTest
Collection<Range<Token>> ranges = Lists.newArrayList(new Range<Token>(partitioner.getMinimumToken(), partitioner.getMinimumToken()));
List<StreamSession.SSTableStreamingSections> sections = StreamSession.getSSTableSectionsForRanges(ranges,
Lists.newArrayList(cfs),
pendingRepair);
pendingRepair,
PreviewKind.NONE);
Set<SSTableReader> sstables = new HashSet<>();
for (StreamSession.SSTableStreamingSections section: sections)
{

View File

@ -74,7 +74,7 @@ public class StreamTransferTaskTest
public void testScheduleTimeout() throws Exception
{
InetAddress peer = FBUtilities.getBroadcastAddress();
StreamSession session = new StreamSession(peer, peer, null, 0, true, null);
StreamSession session = new StreamSession(peer, peer, null, 0, true, null, PreviewKind.NONE);
ColumnFamilyStore cfs = Keyspace.open(KEYSPACE1).getColumnFamilyStore(CF_STANDARD);
// create two sstables
@ -120,9 +120,9 @@ public class StreamTransferTaskTest
public void testFailSessionDuringTransferShouldNotReleaseReferences() throws Exception
{
InetAddress peer = FBUtilities.getBroadcastAddress();
StreamCoordinator streamCoordinator = new StreamCoordinator(1, true, null, false, null);
StreamCoordinator streamCoordinator = new StreamCoordinator(1, true, null, false, null, PreviewKind.NONE);
StreamResultFuture future = StreamResultFuture.init(UUID.randomUUID(), StreamOperation.OTHER, Collections.<StreamEventHandler>emptyList(), streamCoordinator);
StreamSession session = new StreamSession(peer, peer, null, 0, true, null);
StreamSession session = new StreamSession(peer, peer, null, 0, true, null, PreviewKind.NONE);
session.init(future);
ColumnFamilyStore cfs = Keyspace.open(KEYSPACE1).getColumnFamilyStore(CF_STANDARD);