blob: 621821b7574f06e0dbdfcf3df4d2dbc91b15366a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.db.transform;
import java.net.InetAddress;
import java.util.Collections;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.compaction.OperationType;
import org.apache.cassandra.db.partitions.PartitionIterator;
import org.apache.cassandra.db.partitions.UnfilteredPartitionIterator;
import org.apache.cassandra.db.rows.*;
import org.apache.cassandra.utils.DiagnosticSnapshotService;
import org.apache.cassandra.utils.FBUtilities;
public class DuplicateRowChecker extends Transformation<BaseRowIterator<?>>
{
private static final Logger logger = LoggerFactory.getLogger(DuplicateRowChecker.class);
Clustering previous = null;
int duplicatesDetected = 0;
boolean hadNonEqualDuplicates = false;
final String stage;
final List<InetAddress> replicas;
final CFMetaData metadata;
final DecoratedKey key;
final boolean snapshotOnDuplicate;
DuplicateRowChecker(final DecoratedKey key,
final CFMetaData metadata,
final String stage,
final boolean snapshotOnDuplicate,
final List<InetAddress> replicas)
{
this.key = key;
this.metadata = metadata;
this.stage = stage;
this.snapshotOnDuplicate = snapshotOnDuplicate;
this.replicas = replicas;
}
protected DeletionTime applyToDeletion(DeletionTime deletionTime)
{
return deletionTime;
}
protected RangeTombstoneMarker applyToMarker(RangeTombstoneMarker marker)
{
return marker;
}
protected Row applyToStatic(Row row)
{
return row;
}
protected Row applyToRow(Row row)
{
if (null != previous && metadata.comparator.compare(row.clustering(), previous) == 0)
{
duplicatesDetected++;
hadNonEqualDuplicates |= !row.clustering().equals(previous);
}
previous = row.clustering();
return row;
}
protected void onPartitionClose()
{
if (duplicatesDetected > 0)
{
logger.warn("Detected {} duplicate rows for {} during {}.{}",
duplicatesDetected,
metadata.getKeyValidator().getString(key.getKey()),
stage,
hadNonEqualDuplicates ? " Some duplicates had different byte representation." : "");
if (snapshotOnDuplicate)
DiagnosticSnapshotService.duplicateRows(metadata, replicas);
}
duplicatesDetected = 0;
previous = null;
super.onPartitionClose();
}
public static UnfilteredPartitionIterator duringCompaction(final UnfilteredPartitionIterator iterator, OperationType type)
{
if (!DatabaseDescriptor.checkForDuplicateRowsDuringCompaction())
return iterator;
final List<InetAddress> address = Collections.singletonList(FBUtilities.getBroadcastAddress());
final boolean snapshot = DatabaseDescriptor.snapshotOnDuplicateRowDetection();
return Transformation.apply(iterator, new Transformation<UnfilteredRowIterator>()
{
protected UnfilteredRowIterator applyToPartition(UnfilteredRowIterator partition)
{
return Transformation.apply(partition, new DuplicateRowChecker(partition.partitionKey(),
partition.metadata(),
type.toString(),
snapshot,
address));
}
});
}
public static PartitionIterator duringRead(final PartitionIterator iterator, final List<InetAddress> replicas)
{
if (!DatabaseDescriptor.checkForDuplicateRowsDuringReads())
return iterator;
final boolean snapshot = DatabaseDescriptor.snapshotOnDuplicateRowDetection();
return Transformation.apply(iterator, new Transformation<RowIterator>()
{
protected RowIterator applyToPartition(RowIterator partition)
{
return Transformation.apply(partition, new DuplicateRowChecker(partition.partitionKey(),
partition.metadata(),
"Read",
snapshot,
replicas));
}
});
}
}