Use UUID as node identifiers in counters instead of IP addresses

Patch by Sylvain Lebresne; Reviewed by T Jake Luciani for CASSANDRA-1938



git-svn-id: https://svn.apache.org/repos/asf/cassandra/trunk@1080345 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
T Jake Luciani 2011-03-10 20:54:50 +00:00
parent 1a5d09c356
commit ab5a8c77e4
42 changed files with 1800 additions and 809 deletions

View File

@ -397,6 +397,7 @@ struct CfDef {
22: optional i32 memtable_throughput_in_mb,
23: optional double memtable_operations_in_millions,
24: optional bool replicate_on_write=0,
25: optional double merge_shards_chance,
}
/* describes a keyspace. */

View File

@ -72,6 +72,7 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
private static final TField MEMTABLE_THROUGHPUT_IN_MB_FIELD_DESC = new TField("memtable_throughput_in_mb", TType.I32, (short)22);
private static final TField MEMTABLE_OPERATIONS_IN_MILLIONS_FIELD_DESC = new TField("memtable_operations_in_millions", TType.DOUBLE, (short)23);
private static final TField REPLICATE_ON_WRITE_FIELD_DESC = new TField("replicate_on_write", TType.BOOL, (short)24);
private static final TField MERGE_SHARDS_CHANCE_FIELD_DESC = new TField("merge_shards_chance", TType.DOUBLE, (short)25);
public String keyspace;
public String name;
@ -94,6 +95,7 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
public int memtable_throughput_in_mb;
public double memtable_operations_in_millions;
public boolean replicate_on_write;
public double merge_shards_chance;
/** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
public enum _Fields implements TFieldIdEnum {
@ -117,7 +119,8 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
MEMTABLE_FLUSH_AFTER_MINS((short)21, "memtable_flush_after_mins"),
MEMTABLE_THROUGHPUT_IN_MB((short)22, "memtable_throughput_in_mb"),
MEMTABLE_OPERATIONS_IN_MILLIONS((short)23, "memtable_operations_in_millions"),
REPLICATE_ON_WRITE((short)24, "replicate_on_write");
REPLICATE_ON_WRITE((short)24, "replicate_on_write"),
MERGE_SHARDS_CHANCE((short)25, "merge_shards_chance");
private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
@ -174,6 +177,8 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
return MEMTABLE_OPERATIONS_IN_MILLIONS;
case 24: // REPLICATE_ON_WRITE
return REPLICATE_ON_WRITE;
case 25: // MERGE_SHARDS_CHANCE
return MERGE_SHARDS_CHANCE;
default:
return null;
}
@ -227,7 +232,8 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
private static final int __MEMTABLE_THROUGHPUT_IN_MB_ISSET_ID = 10;
private static final int __MEMTABLE_OPERATIONS_IN_MILLIONS_ISSET_ID = 11;
private static final int __REPLICATE_ON_WRITE_ISSET_ID = 12;
private BitSet __isset_bit_vector = new BitSet(13);
private static final int __MERGE_SHARDS_CHANCE_ISSET_ID = 13;
private BitSet __isset_bit_vector = new BitSet(14);
public static final Map<_Fields, FieldMetaData> metaDataMap;
static {
@ -275,6 +281,8 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
new FieldValueMetaData(TType.DOUBLE)));
tmpMap.put(_Fields.REPLICATE_ON_WRITE, new FieldMetaData("replicate_on_write", TFieldRequirementType.OPTIONAL,
new FieldValueMetaData(TType.BOOL)));
tmpMap.put(_Fields.MERGE_SHARDS_CHANCE, new FieldMetaData("merge_shards_chance", TFieldRequirementType.OPTIONAL,
new FieldValueMetaData(TType.DOUBLE)));
metaDataMap = Collections.unmodifiableMap(tmpMap);
FieldMetaData.addStructMetaDataMap(CfDef.class, metaDataMap);
}
@ -350,6 +358,7 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
this.memtable_throughput_in_mb = other.memtable_throughput_in_mb;
this.memtable_operations_in_millions = other.memtable_operations_in_millions;
this.replicate_on_write = other.replicate_on_write;
this.merge_shards_chance = other.merge_shards_chance;
}
public CfDef deepCopy() {
@ -394,6 +403,8 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
this.memtable_operations_in_millions = 0.0;
this.replicate_on_write = false;
setMerge_shards_chanceIsSet(false);
this.merge_shards_chance = 0.0;
}
public String getKeyspace() {
@ -902,6 +913,29 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
__isset_bit_vector.set(__REPLICATE_ON_WRITE_ISSET_ID, value);
}
public double getMerge_shards_chance() {
return this.merge_shards_chance;
}
public CfDef setMerge_shards_chance(double merge_shards_chance) {
this.merge_shards_chance = merge_shards_chance;
setMerge_shards_chanceIsSet(true);
return this;
}
public void unsetMerge_shards_chance() {
__isset_bit_vector.clear(__MERGE_SHARDS_CHANCE_ISSET_ID);
}
/** Returns true if field merge_shards_chance is set (has been asigned a value) and false otherwise */
public boolean isSetMerge_shards_chance() {
return __isset_bit_vector.get(__MERGE_SHARDS_CHANCE_ISSET_ID);
}
public void setMerge_shards_chanceIsSet(boolean value) {
__isset_bit_vector.set(__MERGE_SHARDS_CHANCE_ISSET_ID, value);
}
public void setFieldValue(_Fields field, Object value) {
switch (field) {
case KEYSPACE:
@ -1072,6 +1106,14 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
}
break;
case MERGE_SHARDS_CHANCE:
if (value == null) {
unsetMerge_shards_chance();
} else {
setMerge_shards_chance((Double)value);
}
break;
}
}
@ -1140,6 +1182,9 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
case REPLICATE_ON_WRITE:
return new Boolean(isReplicate_on_write());
case MERGE_SHARDS_CHANCE:
return new Double(getMerge_shards_chance());
}
throw new IllegalStateException();
}
@ -1193,6 +1238,8 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
return isSetMemtable_operations_in_millions();
case REPLICATE_ON_WRITE:
return isSetReplicate_on_write();
case MERGE_SHARDS_CHANCE:
return isSetMerge_shards_chance();
}
throw new IllegalStateException();
}
@ -1399,6 +1446,15 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
return false;
}
boolean this_present_merge_shards_chance = true && this.isSetMerge_shards_chance();
boolean that_present_merge_shards_chance = true && that.isSetMerge_shards_chance();
if (this_present_merge_shards_chance || that_present_merge_shards_chance) {
if (!(this_present_merge_shards_chance && that_present_merge_shards_chance))
return false;
if (this.merge_shards_chance != that.merge_shards_chance)
return false;
}
return true;
}
@ -1511,6 +1567,11 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
if (present_replicate_on_write)
builder.append(replicate_on_write);
boolean present_merge_shards_chance = true && (isSetMerge_shards_chance());
builder.append(present_merge_shards_chance);
if (present_merge_shards_chance)
builder.append(merge_shards_chance);
return builder.toHashCode();
}
@ -1732,6 +1793,16 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
return lastComparison;
}
}
lastComparison = Boolean.valueOf(isSetMerge_shards_chance()).compareTo(typedOther.isSetMerge_shards_chance());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetMerge_shards_chance()) {
lastComparison = TBaseHelper.compareTo(this.merge_shards_chance, typedOther.merge_shards_chance);
if (lastComparison != 0) {
return lastComparison;
}
}
return 0;
}
@ -1920,6 +1991,14 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
TProtocolUtil.skip(iprot, field.type);
}
break;
case 25: // MERGE_SHARDS_CHANCE
if (field.type == TType.DOUBLE) {
this.merge_shards_chance = iprot.readDouble();
setMerge_shards_chanceIsSet(true);
} else {
TProtocolUtil.skip(iprot, field.type);
}
break;
default:
TProtocolUtil.skip(iprot, field.type);
}
@ -2059,6 +2138,11 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
oprot.writeBool(this.replicate_on_write);
oprot.writeFieldEnd();
}
if (isSetMerge_shards_chance()) {
oprot.writeFieldBegin(MERGE_SHARDS_CHANCE_FIELD_DESC);
oprot.writeDouble(this.merge_shards_chance);
oprot.writeFieldEnd();
}
oprot.writeFieldStop();
oprot.writeStructEnd();
}
@ -2221,6 +2305,12 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
sb.append(this.replicate_on_write);
first = false;
}
if (isSetMerge_shards_chance()) {
if (!first) sb.append(", ");
sb.append("merge_shards_chance:");
sb.append(this.merge_shards_chance);
first = false;
}
sb.append(")");
return sb.toString();
}

View File

@ -59,6 +59,7 @@ protocol InterNode {
union { int, null } memtable_flush_after_mins = 60;
union { null, int } memtable_throughput_in_mb = null;
union { null, double} memtable_operations_in_millions = null;
union { null, double} merge_shards_chance = null;
union { int, null } id;
union { array<ColumnDef>, null } column_metadata;
}

View File

@ -60,6 +60,7 @@ public final class CFMetaData
public final static int DEFAULT_MEMTABLE_LIFETIME_IN_MINS = 60 * 24;
public final static int DEFAULT_MEMTABLE_THROUGHPUT_IN_MB = sizeMemtableThroughput();
public final static double DEFAULT_MEMTABLE_OPERATIONS_IN_MILLIONS = sizeMemtableOperations(DEFAULT_MEMTABLE_THROUGHPUT_IN_MB);
public final static double DEFAULT_MERGE_SHARDS_CHANCE = 0.1;
private static final int MIN_CF_ID = 1000;
@ -72,6 +73,7 @@ public final class CFMetaData
public static final CFMetaData MigrationsCf = newSystemTable(Migration.MIGRATIONS_CF, 2, "individual schema mutations", TimeUUIDType.instance, null, DEFAULT_SYSTEM_MEMTABLE_THROUGHPUT_IN_MB);
public static final CFMetaData SchemaCf = newSystemTable(Migration.SCHEMA_CF, 3, "current state of the schema", UTF8Type.instance, null, DEFAULT_SYSTEM_MEMTABLE_THROUGHPUT_IN_MB);
public static final CFMetaData IndexCf = newSystemTable(SystemTable.INDEX_CF, 5, "indexes that have been completed", UTF8Type.instance, null, DEFAULT_SYSTEM_MEMTABLE_THROUGHPUT_IN_MB);
public static final CFMetaData NodeIdCf = newSystemTable(SystemTable.NODE_ID_CF, 6, "nodeId and their metadata", TimeUUIDType.instance, null, DEFAULT_SYSTEM_MEMTABLE_THROUGHPUT_IN_MB);
private static CFMetaData newSystemTable(String cfName, int cfId, String comment, AbstractType comparator, AbstractType subComparator, int memtableThroughPutInMB)
{
@ -94,6 +96,7 @@ public final class CFMetaData
DEFAULT_MEMTABLE_LIFETIME_IN_MINS,
memtableThroughPutInMB,
sizeMemtableOperations(memtableThroughPutInMB),
0,
cfId,
Collections.<ByteBuffer, ColumnDefinition>emptyMap());
}
@ -160,6 +163,7 @@ public final class CFMetaData
private int memtableFlushAfterMins; // default 60
private int memtableThroughputInMb; // default based on heap size
private double memtableOperationsInMillions; // default based on throughput
private double mergeShardsChance; // default 0.1, chance [0.0, 1.0] of merging old shards during replication
// NOTE: if you find yourself adding members to this class, make sure you keep the convert methods in lockstep.
private final Map<ByteBuffer, ColumnDefinition> column_metadata;
@ -183,6 +187,7 @@ public final class CFMetaData
int memtableFlushAfterMins,
Integer memtableThroughputInMb,
Double memtableOperationsInMillions,
double mergeShardsChance,
Integer cfId,
Map<ByteBuffer, ColumnDefinition> column_metadata)
@ -216,6 +221,7 @@ public final class CFMetaData
this.memtableOperationsInMillions = memtableOperationsInMillions == null
? DEFAULT_MEMTABLE_OPERATIONS_IN_MILLIONS
: memtableOperationsInMillions;
this.mergeShardsChance = mergeShardsChance;
this.cfId = cfId;
this.column_metadata = new HashMap<ByteBuffer, ColumnDefinition>(column_metadata);
}
@ -251,6 +257,7 @@ public final class CFMetaData
int memTime,
Integer memSize,
Double memOps,
double mergeShardsChance,
//This constructor generates the id!
Map<ByteBuffer, ColumnDefinition> column_metadata)
{
@ -273,6 +280,7 @@ public final class CFMetaData
memTime,
memSize,
memOps,
mergeShardsChance,
nextId(),
column_metadata);
}
@ -298,6 +306,7 @@ public final class CFMetaData
DEFAULT_MEMTABLE_LIFETIME_IN_MINS,
DEFAULT_MEMTABLE_THROUGHPUT_IN_MB,
DEFAULT_MEMTABLE_OPERATIONS_IN_MILLIONS,
0,
Collections.<ByteBuffer, ColumnDefinition>emptyMap());
}
@ -323,6 +332,7 @@ public final class CFMetaData
cfm.memtableFlushAfterMins,
cfm.memtableThroughputInMb,
cfm.memtableOperationsInMillions,
cfm.mergeShardsChance,
cfm.cfId,
cfm.column_metadata);
}
@ -349,6 +359,7 @@ public final class CFMetaData
cfm.memtableFlushAfterMins,
cfm.memtableThroughputInMb,
cfm.memtableOperationsInMillions,
cfm.mergeShardsChance,
cfm.cfId,
cfm.column_metadata);
}
@ -389,6 +400,7 @@ public final class CFMetaData
cf.memtable_flush_after_mins = memtableFlushAfterMins;
cf.memtable_throughput_in_mb = memtableThroughputInMb;
cf.memtable_operations_in_millions = memtableOperationsInMillions;
cf.merge_shards_chance = mergeShardsChance;
cf.column_metadata = SerDeUtils.createArray(column_metadata.size(),
org.apache.cassandra.db.migration.avro.ColumnDef.SCHEMA$);
for (ColumnDefinition cd : column_metadata.values())
@ -429,6 +441,7 @@ public final class CFMetaData
Integer memtable_flush_after_mins = cf.memtable_flush_after_mins == null ? DEFAULT_MEMTABLE_LIFETIME_IN_MINS : cf.memtable_flush_after_mins;
Integer memtable_throughput_in_mb = cf.memtable_throughput_in_mb == null ? DEFAULT_MEMTABLE_THROUGHPUT_IN_MB : cf.memtable_throughput_in_mb;
Double memtable_operations_in_millions = cf.memtable_operations_in_millions == null ? DEFAULT_MEMTABLE_OPERATIONS_IN_MILLIONS : cf.memtable_operations_in_millions;
double merge_shards_chance = cf.merge_shards_chance == null ? DEFAULT_MERGE_SHARDS_CHANCE : cf.merge_shards_chance;
return new CFMetaData(cf.keyspace.toString(),
cf.name.toString(),
@ -449,6 +462,7 @@ public final class CFMetaData
memtable_flush_after_mins,
memtable_throughput_in_mb,
memtable_operations_in_millions,
merge_shards_chance,
cf.id,
column_metadata);
}
@ -473,6 +487,11 @@ public final class CFMetaData
return readRepairChance;
}
public double getMergeShardsChance()
{
return readRepairChance;
}
public boolean getReplicateOnWrite()
{
return replicateOnWrite;
@ -561,6 +580,7 @@ public final class CFMetaData
.append(memtableFlushAfterMins, rhs.memtableFlushAfterMins)
.append(memtableThroughputInMb, rhs.memtableThroughputInMb)
.append(memtableOperationsInMillions, rhs.memtableOperationsInMillions)
.append(mergeShardsChance, rhs.mergeShardsChance)
.isEquals();
}
@ -588,6 +608,7 @@ public final class CFMetaData
.append(memtableFlushAfterMins)
.append(memtableThroughputInMb)
.append(memtableOperationsInMillions)
.append(mergeShardsChance)
.toHashCode();
}
@ -622,6 +643,8 @@ public final class CFMetaData
cf_def.memtable_throughput_in_mb = CFMetaData.DEFAULT_MEMTABLE_THROUGHPUT_IN_MB;
if (cf_def.memtable_operations_in_millions == null)
cf_def.memtable_operations_in_millions = CFMetaData.DEFAULT_MEMTABLE_OPERATIONS_IN_MILLIONS;
if (cf_def.merge_shards_chance == null)
cf_def.merge_shards_chance = CFMetaData.DEFAULT_MERGE_SHARDS_CHANCE;
}
/** applies implicit defaults to cf definition. useful in updates */
@ -641,6 +664,8 @@ public final class CFMetaData
cf_def.setMemtable_throughput_in_mb(CFMetaData.DEFAULT_MEMTABLE_THROUGHPUT_IN_MB);
if (!cf_def.isSetMemtable_operations_in_millions())
cf_def.setMemtable_operations_in_millions(CFMetaData.DEFAULT_MEMTABLE_OPERATIONS_IN_MILLIONS);
if (!cf_def.isSetMerge_shards_chance())
cf_def.setMerge_shards_chance(CFMetaData.DEFAULT_MERGE_SHARDS_CHANCE);
}
// merges some final fields from this CFM with modifiable fields from CfDef into a new CFMetaData.
@ -683,6 +708,7 @@ public final class CFMetaData
memtableFlushAfterMins = cf_def.memtable_flush_after_mins;
memtableThroughputInMb = cf_def.memtable_throughput_in_mb;
memtableOperationsInMillions = cf_def.memtable_operations_in_millions;
mergeShardsChance = cf_def.merge_shards_chance;
// adjust secondary indexes. figure out who is coming and going.
Set<ByteBuffer> toRemove = new HashSet<ByteBuffer>();
@ -746,6 +772,7 @@ public final class CFMetaData
def.setMemtable_flush_after_mins(cfm.memtableFlushAfterMins);
def.setMemtable_throughput_in_mb(cfm.memtableThroughputInMb);
def.setMemtable_operations_in_millions(cfm.memtableOperationsInMillions);
def.setMerge_shards_chance(cfm.mergeShardsChance);
List<org.apache.cassandra.thrift.ColumnDef> column_meta = new ArrayList< org.apache.cassandra.thrift.ColumnDef>(cfm.column_metadata.size());
for (ColumnDefinition cd : cfm.column_metadata.values())
{
@ -788,6 +815,7 @@ public final class CFMetaData
def.memtable_flush_after_mins = cfm.memtableFlushAfterMins;
def.memtable_throughput_in_mb = cfm.memtableThroughputInMb;
def.memtable_operations_in_millions = cfm.memtableOperationsInMillions;
def.merge_shards_chance = cfm.mergeShardsChance;
List<org.apache.cassandra.db.migration.avro.ColumnDef> column_meta = new ArrayList<org.apache.cassandra.db.migration.avro.ColumnDef>(cfm.column_metadata.size());
for (ColumnDefinition cd : cfm.column_metadata.values())
{
@ -825,7 +853,8 @@ public final class CFMetaData
newDef.row_cache_save_period_in_seconds = def.getRow_cache_save_period_in_seconds();
newDef.row_cache_size = def.getRow_cache_size();
newDef.subcomparator_type = def.getSubcomparator_type();
newDef.merge_shards_chance = def.getMerge_shards_chance();
List<org.apache.cassandra.db.migration.avro.ColumnDef> columnMeta = new ArrayList<org.apache.cassandra.db.migration.avro.ColumnDef>();
if (def.isSetColumn_metadata())
{
@ -947,6 +976,7 @@ public final class CFMetaData
.append("memtableFlushAfterMins", memtableFlushAfterMins)
.append("memtableThroughputInMb", memtableThroughputInMb)
.append("memtableOperationsInMillions", memtableOperationsInMillions)
.append("mergeShardsChance", mergeShardsChance)
.append("column_metadata", column_metadata)
.toString();
}

View File

@ -377,12 +377,14 @@ public class DatabaseDescriptor
CFMetaData.HintsCf,
CFMetaData.MigrationsCf,
CFMetaData.SchemaCf,
CFMetaData.IndexCf);
CFMetaData.IndexCf,
CFMetaData.NodeIdCf);
CFMetaData.map(CFMetaData.StatusCf);
CFMetaData.map(CFMetaData.HintsCf);
CFMetaData.map(CFMetaData.MigrationsCf);
CFMetaData.map(CFMetaData.SchemaCf);
CFMetaData.map(CFMetaData.IndexCf);
CFMetaData.map(CFMetaData.NodeIdCf);
tables.put(Table.SYSTEM_TABLE, systemMeta);
/* Load the seeds for node contact points */
@ -605,6 +607,11 @@ public class DatabaseDescriptor
throw new ConfigurationException("memtable_operations_in_millions must be a positive double");
}
if (cf.merge_shards_chance < 0.0 || cf.merge_shards_chance > 1.0)
{
throw new ConfigurationException("merge_shards_chance must be between 0.0 and 1.0 (0% and 100%)");
}
Map<ByteBuffer, ColumnDefinition> metadata = new TreeMap<ByteBuffer, ColumnDefinition>();
for (RawColumnDefinition rcd : cf.column_metadata)
@ -646,6 +653,7 @@ public class DatabaseDescriptor
cf.memtable_flush_after_mins,
cf.memtable_throughput_in_mb,
cf.memtable_operations_in_millions,
cf.merge_shards_chance,
metadata);
}
defs.add(new KSMetaData(keyspace.name,

View File

@ -47,4 +47,5 @@ public class RawColumnFamily
public int memtable_flush_after_mins = CFMetaData.DEFAULT_MEMTABLE_LIFETIME_IN_MINS;
public Integer memtable_throughput_in_mb;
public Double memtable_operations_in_millions;
public double merge_shards_chance = CFMetaData.DEFAULT_MERGE_SHARDS_CHANCE;
}

View File

@ -226,6 +226,7 @@ public class CreateColumnFamilyStatement
getPropertyInt(KW_MEMTABLEFLUSHINMINS, CFMetaData.DEFAULT_MEMTABLE_LIFETIME_IN_MINS),
getPropertyInt(KW_MEMTABLESIZEINMB, CFMetaData.DEFAULT_MEMTABLE_THROUGHPUT_IN_MB),
getPropertyDouble(KW_MEMTABLEOPSINMILLIONS, CFMetaData.DEFAULT_MEMTABLE_OPERATIONS_IN_MILLIONS),
0,
getColumns(comparator));
}
catch (ConfigurationException e)

View File

@ -134,15 +134,12 @@ public class ColumnFamily implements IColumnContainer, IIterableColumns
delete(cf);
}
/**
* FIXME: Gross.
*/
public IColumnSerializer getColumnSerializer()
{
return columnSerializer;
}
int getColumnCount()
public int getColumnCount()
{
return columns.size();
}

View File

@ -108,10 +108,10 @@ public class ColumnFamilySerializer implements ICompactSerializer2<ColumnFamily>
public ColumnFamily deserialize(DataInput dis) throws IOException
{
return deserialize(dis, false);
return deserialize(dis, false, false);
}
public ColumnFamily deserialize(DataInput dis, boolean intern) throws IOException
public ColumnFamily deserialize(DataInput dis, boolean intern, boolean fromRemote) throws IOException
{
if (!dis.readBoolean())
return null;
@ -122,23 +122,23 @@ public class ColumnFamilySerializer implements ICompactSerializer2<ColumnFamily>
throw new UnserializableColumnFamilyException("Couldn't find cfId=" + cfId, cfId);
ColumnFamily cf = ColumnFamily.create(cfId);
deserializeFromSSTableNoColumns(cf, dis);
deserializeColumns(dis, cf, intern);
deserializeColumns(dis, cf, intern, fromRemote);
return cf;
}
public void deserializeColumns(DataInput dis, ColumnFamily cf, boolean intern) throws IOException
public void deserializeColumns(DataInput dis, ColumnFamily cf, boolean intern, boolean fromRemote) throws IOException
{
int size = dis.readInt();
ColumnFamilyStore interner = intern ? Table.open(CFMetaData.getCF(cf.id()).left).getColumnFamilyStore(cf.id()) : null;
for (int i = 0; i < size; ++i)
{
IColumn column = cf.getColumnSerializer().deserialize(dis, interner);
IColumn column = cf.getColumnSerializer().deserialize(dis, interner, fromRemote);
cf.addColumn(column);
}
}
public ColumnFamily deserializeFromSSTableNoColumns(ColumnFamily cf, DataInput input) throws IOException
{
{
cf.delete(input.readInt(), input.readLong());
return cf;
}

View File

@ -1030,9 +1030,9 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
return maxFile;
}
public void forceCleanup() throws ExecutionException, InterruptedException
public void forceCleanup(NodeId.OneShotRenewer renewer) throws ExecutionException, InterruptedException
{
CompactionManager.instance.performCleanup(ColumnFamilyStore.this);
CompactionManager.instance.performCleanup(ColumnFamilyStore.this, renewer);
}
public void scrub() throws ExecutionException, InterruptedException

View File

@ -31,6 +31,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.io.IColumnSerializer;
import org.apache.cassandra.db.context.CounterContext;
import org.apache.cassandra.io.ICompactSerializer2;
import org.apache.cassandra.utils.ByteBufferUtil;
@ -70,10 +71,15 @@ public class ColumnSerializer implements IColumnSerializer
public Column deserialize(DataInput dis) throws IOException
{
return deserialize(dis, null);
return deserialize(dis, null, false);
}
public Column deserialize(DataInput dis, ColumnFamilyStore interner) throws IOException
/*
* For counter columns, we must know when we deserialize them if what we
* deserialize comes from a remote host. If it does, then we must clear
* the delta.
*/
public Column deserialize(DataInput dis, ColumnFamilyStore interner, boolean fromRemote) throws IOException
{
ByteBuffer name = ByteBufferUtil.readWithShortLength(dis);
if (name.remaining() <= 0)
@ -87,6 +93,8 @@ public class ColumnSerializer implements IColumnSerializer
long timestampOfLastDelete = dis.readLong();
long ts = dis.readLong();
ByteBuffer value = ByteBufferUtil.readWithLength(dis);
if (fromRemote)
value = CounterContext.instance().clearAllDelta(value);
return new CounterColumn(name, value, ts, timestampOfLastDelete);
}
else if ((b & EXPIRATION_MASK) != 0)

View File

@ -52,6 +52,7 @@ import org.apache.cassandra.streaming.OperationType;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.Pair;
import org.apache.cassandra.utils.WrappedRunnable;
import org.apache.cassandra.utils.NodeId;
import org.cliffc.high_scale_lib.NonBlockingHashMap;
public class CompactionManager implements CompactionManagerMBean
@ -161,7 +162,7 @@ public class CompactionManager implements CompactionManagerMBean
}
}
public void performCleanup(final ColumnFamilyStore cfStore) throws InterruptedException, ExecutionException
public void performCleanup(final ColumnFamilyStore cfStore, final NodeId.OneShotRenewer renewer) throws InterruptedException, ExecutionException
{
Callable<Object> runnable = new Callable<Object>()
{
@ -171,7 +172,7 @@ public class CompactionManager implements CompactionManagerMBean
try
{
if (!cfStore.isInvalid())
doCleanupCompaction(cfStore);
doCleanupCompaction(cfStore, renewer);
return this;
}
finally
@ -678,11 +679,12 @@ public class CompactionManager implements CompactionManagerMBean
*
* @throws IOException
*/
private void doCleanupCompaction(ColumnFamilyStore cfs) throws IOException
private void doCleanupCompaction(ColumnFamilyStore cfs, NodeId.OneShotRenewer renewer) throws IOException
{
assert !cfs.isIndex();
Table table = cfs.table;
Collection<Range> ranges = StorageService.instance.getLocalRanges(table.name);
boolean isCommutative = cfs.metadata.getDefaultValidator().isCommutative();
for (SSTableReader sstable : cfs.getSSTables())
{
@ -718,11 +720,16 @@ public class CompactionManager implements CompactionManagerMBean
}
else
{
while (row.hasNext())
if (!indexedColumns.isEmpty() || isCommutative)
{
IColumn column = row.next();
if (indexedColumns.contains(column.name()))
Table.cleanupIndexEntry(cfs, row.getKey().key, column);
while (row.hasNext())
{
IColumn column = row.next();
if (column instanceof CounterColumn)
renewer.maybeRenew((CounterColumn)column);
if (indexedColumns.contains(column.name()))
Table.cleanupIndexEntry(cfs, row.getKey().key, column);
}
}
}
}

View File

@ -18,29 +18,33 @@
package org.apache.cassandra.db;
import java.io.IOException;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Map;
import org.apache.log4j.Logger;
import org.apache.cassandra.db.context.CounterContext;
import org.apache.cassandra.db.context.IContext.ContextRelationship;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.io.util.DataOutputBuffer;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.NodeId;
/**
* A column that represents a partitioned counter.
*/
public class CounterColumn extends Column
{
private static Logger logger = Logger.getLogger(CounterColumn.class);
private static final Logger logger = Logger.getLogger(CounterColumn.class);
private static CounterContext contextManager = CounterContext.instance();
protected static final CounterContext contextManager = CounterContext.instance();
protected final long timestampOfLastDelete;
private final long timestampOfLastDelete;
public CounterColumn(ByteBuffer name, long value, long timestamp)
{
@ -98,11 +102,30 @@ public class CounterColumn extends Column
return null;
}
/*
* We have to special case digest creation for counter column because
* we don't want to include the information about which shard of the
* context is a delta or not, since this information differs from node to
* node.
*/
@Override
public void updateDigest(MessageDigest digest)
{
super.updateDigest(digest);
digest.update(ByteBufferUtil.bytes(timestampOfLastDelete));
digest.update(name.duplicate());
// We don't take the deltas into account in a digest
contextManager.updateDigest(digest, value);
DataOutputBuffer buffer = new DataOutputBuffer();
try
{
buffer.writeLong(timestamp);
buffer.writeByte(serializationFlags());
buffer.writeLong(timestampOfLastDelete);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
digest.update(buffer.getData(), 0, buffer.getLength());
}
@Override
@ -181,17 +204,69 @@ public class CounterColumn extends Column
return ColumnSerializer.COUNTER_MASK;
}
public CounterColumn cleanNodeCounts(InetAddress node)
/**
* Check if a given nodeId is found in this CounterColumn context.
*/
public boolean hasNodeId(NodeId id)
{
// use cases:
// 1) AES post-stream
// 2) RRR, after CF.cloneMe()
// 3) RRR, after CF.diff() which creates a new CF
ByteBuffer cleanedValue = contextManager.cleanNodeCounts(value, node);
if (cleanedValue == value) // reference equality is enough
return this;
if (0 == value.remaining())
return contextManager.hasNodeId(value(), id);
}
public CounterColumn computeOldShardMerger()
{
ByteBuffer bb = contextManager.computeOldShardMerger(value(), NodeId.getOldLocalNodeIds());
if (bb == null)
return null;
return new CounterColumn(name, cleanedValue, timestamp, timestampOfLastDelete);
else
return new CounterColumn(name(), bb, timestamp(), timestampOfLastDelete);
}
private CounterColumn removeOldShards(int gcBefore)
{
ByteBuffer bb = contextManager.removeOldShards(value(), gcBefore);
if (bb == value())
return this;
else
{
return new CounterColumn(name(), bb, timestamp(), timestampOfLastDelete);
}
}
public static void removeOldShards(ColumnFamily cf, int gcBefore)
{
if (!cf.isSuper())
{
for (Map.Entry<ByteBuffer, IColumn> entry : cf.getColumnsMap().entrySet())
{
ByteBuffer cname = entry.getKey();
IColumn c = entry.getValue();
if (!(c instanceof CounterColumn))
continue;
CounterColumn cleaned = ((CounterColumn) c).removeOldShards(gcBefore);
if (cleaned != c)
{
cf.remove(cname);
cf.addColumn(cleaned);
}
}
}
else
{
for (Map.Entry<ByteBuffer, IColumn> entry : cf.getColumnsMap().entrySet())
{
SuperColumn c = (SuperColumn) entry.getValue();
for (IColumn subColumn : c.getSubColumns())
{
if (!(subColumn instanceof CounterColumn))
continue;
CounterColumn cleaned = ((CounterColumn) subColumn).removeOldShards(gcBefore);
if (cleaned != subColumn)
{
c.remove(subColumn.name());
c.addColumn(cleaned);
}
}
}
}
}
}

View File

@ -22,12 +22,14 @@ import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.ThreadLocal;
import java.nio.ByteBuffer;
import java.net.InetAddress;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.LinkedList;
import java.util.Random;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -50,6 +52,15 @@ public class CounterMutation implements IMutation
private final RowMutation rowMutation;
private final ConsistencyLevel consistency;
private static final ThreadLocal<Random> random = new ThreadLocal<Random>()
{
@Override
protected Random initialValue()
{
return new Random();
}
};
public CounterMutation(RowMutation rowMutation, ConsistencyLevel consistency)
{
this.rowMutation = rowMutation;
@ -88,16 +99,34 @@ public class CounterMutation implements IMutation
{
if (!columnFamily.metadata().getReplicateOnWrite())
continue;
addReadCommandFromColumnFamily(rowMutation.getTable(), rowMutation.key(), columnFamily, readCommands);
}
// CF type: regular
if (!columnFamily.isSuper())
{
QueryPath queryPath = new QueryPath(columnFamily.metadata().cfName);
ReadCommand readCommand = new SliceByNamesReadCommand(rowMutation.getTable(), rowMutation.key(), queryPath, columnFamily.getColumnNames());
readCommands.add(readCommand);
// create a replication RowMutation
RowMutation replicationMutation = new RowMutation(rowMutation.getTable(), rowMutation.key());
for (ReadCommand readCommand : readCommands)
{
Table table = Table.open(readCommand.table);
Row row = readCommand.getRow(table);
if (row == null || row.cf == null)
continue;
}
row = mergeOldShards(readCommand.table, row);
replicationMutation.add(row.cf);
}
return replicationMutation;
}
private void addReadCommandFromColumnFamily(String table, ByteBuffer key, ColumnFamily columnFamily, List<ReadCommand> commands)
{
// CF type: regular
if (!columnFamily.isSuper())
{
QueryPath queryPath = new QueryPath(columnFamily.metadata().cfName);
commands.add(new SliceByNamesReadCommand(table, key, queryPath, columnFamily.getColumnNames()));
}
else
{
// CF type: super
for (IColumn superColumn : columnFamily.getSortedColumns())
{
@ -111,41 +140,76 @@ public class CounterMutation implements IMutation
subColNames.add(subCol.name());
}
ReadCommand readCommand = new SliceByNamesReadCommand(rowMutation.getTable(), rowMutation.key(), queryPath, subColNames);
readCommands.add(readCommand);
commands.add(new SliceByNamesReadCommand(table, key, queryPath, subColNames));
}
}
}
// replicate to non-local replicas
List<InetAddress> foreignReplicas = StorageService.instance.getLiveNaturalEndpoints(rowMutation.getTable(), rowMutation.key());
foreignReplicas.remove(FBUtilities.getLocalAddress()); // remove local replica
// create a replication RowMutation
RowMutation replicationMutation = new RowMutation(rowMutation.getTable(), rowMutation.key());
for (ReadCommand readCommand : readCommands)
private Row mergeOldShards(String table, Row row) throws IOException
{
ColumnFamily cf = row.cf;
// random check for merging to allow lessening the performance impact
if (cf.metadata().getMergeShardsChance() > random.get().nextDouble())
{
Table table = Table.open(readCommand.table);
Row row = readCommand.getRow(table);
if (row == null || row.cf == null)
continue;
AbstractType defaultValidator = row.cf.metadata().getDefaultValidator();
if (defaultValidator.isCommutative())
ColumnFamily merger = computeShardMerger(cf);
if (merger != null)
{
/**
* Clean out contexts for all nodes we're sending the repair to, otherwise,
* we could send a context which is local to one of the foreign replicas,
* which would then incorrectly add that to its own count, because
* local resolution aggregates.
*/
// note: the following logic could be optimized
for (InetAddress foreignNode : foreignReplicas)
RowMutation localMutation = new RowMutation(table, row.key.key);
localMutation.add(merger);
localMutation.apply();
cf.addAll(merger);
}
}
return row;
}
private ColumnFamily computeShardMerger(ColumnFamily cf)
{
ColumnFamily merger = null;
// CF type: regular
if (!cf.isSuper())
{
for (IColumn column : cf.getSortedColumns())
{
if (!(column instanceof CounterColumn))
continue;
IColumn c = ((CounterColumn)column).computeOldShardMerger();
if (c != null)
{
((AbstractCommutativeType)defaultValidator).cleanContext(row.cf, foreignNode);
if (merger == null)
merger = cf.cloneMeShallow();
merger.addColumn(c);
}
}
replicationMutation.add(row.cf);
}
return replicationMutation;
else // CF type: super
{
for (IColumn superColumn : cf.getSortedColumns())
{
IColumn mergerSuper = null;
for (IColumn column : superColumn.getSubColumns())
{
if (!(column instanceof CounterColumn))
continue;
IColumn c = ((CounterColumn)column).computeOldShardMerger();
if (c != null)
{
if (mergerSuper == null)
mergerSuper = ((SuperColumn)superColumn).cloneMeShallow();
mergerSuper.addColumn(c);
}
}
if (mergerSuper != null)
{
if (merger == null)
merger = cf.cloneMeShallow();
merger.addColumn(mergerSuper);
}
}
}
return merger;
}
public Message makeMutationMessage(int version) throws IOException

View File

@ -109,7 +109,8 @@ class ReadResponseSerializer implements ICompactSerializer<ReadResponse>
Row row = null;
if (!isDigest)
{
row = Row.serializer().deserialize(dis, version);
// This is coming from a remote host
row = Row.serializer().deserialize(dis, version, true);
}
return isDigest ? new ReadResponse(ByteBuffer.wrap(digest)) : new ReadResponse(row);

View File

@ -64,9 +64,14 @@ class RowSerializer implements ICompactSerializer<Row>
ColumnFamily.serializer().serialize(row.cf, dos);
}
public Row deserialize(DataInputStream dis, int version) throws IOException
public Row deserialize(DataInputStream dis, int version, boolean fromRemote) throws IOException
{
return new Row(StorageService.getPartitioner().decorateKey(ByteBufferUtil.readWithShortLength(dis)),
ColumnFamily.serializer().deserialize(dis));
ColumnFamily.serializer().deserialize(dis, false, fromRemote));
}
public Row deserialize(DataInputStream dis, int version) throws IOException
{
return deserialize(dis, version, false);
}
}

View File

@ -1,20 +1,20 @@
/**
* 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.
*/
* 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;
@ -53,7 +53,7 @@ public class RowMutation implements IMutation, MessageProducer
private ByteBuffer key_;
// map of column family id to mutations for that column family.
protected Map<Integer, ColumnFamily> modifications_ = new HashMap<Integer, ColumnFamily>();
private Map<Integer, byte[]> preserializedBuffers = new HashMap<Integer, byte[]>();
public RowMutation(String table, ByteBuffer key)
@ -106,7 +106,7 @@ public class RowMutation implements IMutation, MessageProducer
* family object.
* param @ cf - column family name
* param @ columnFamily - the column family.
*/
*/
public void add(ColumnFamily columnFamily)
{
assert columnFamily != null;
@ -133,7 +133,7 @@ public class RowMutation implements IMutation, MessageProducer
* param @ value - value associated with the column
* param @ timestamp - timestamp associated with this data.
* param @ timeToLive - ttl for the column, 0 for standard (non expiring) columns
*/
*/
public void add(QueryPath path, ByteBuffer value, long timestamp, int timeToLive)
{
Integer id = CFMetaData.getId(table_, path.columnFamilyName);
@ -183,15 +183,12 @@ public class RowMutation implements IMutation, MessageProducer
/*
* This is equivalent to calling commit. Applies the changes to
* to the table that is obtained by calling Table.open().
*/
*/
public void apply() throws IOException
{
Table.open(table_).apply(this, true);
}
/**
* Apply without touching the commitlog. For testing.
*/
public void applyUnsafe() throws IOException
{
Table.open(table_).apply(this, false);
@ -200,7 +197,7 @@ public class RowMutation implements IMutation, MessageProducer
/*
* This is equivalent to calling commit. Applies the changes to
* to the table that is obtained by calling Table.open().
*/
*/
void applyBinary() throws IOException, ExecutionException, InterruptedException
{
Table.open(table_).load(this);
@ -364,7 +361,8 @@ public class RowMutation implements IMutation, MessageProducer
for (int i = 0; i < size; ++i)
{
Integer cfid = Integer.valueOf(dis.readInt());
ColumnFamily cf = ColumnFamily.serializer().deserialize(dis, true);
// This is coming from a remote host
ColumnFamily cf = ColumnFamily.serializer().deserialize(dis, true, true);
modifications.put(cfid, cf);
}
return new RowMutation(table, key, modifications);

View File

@ -361,10 +361,15 @@ class SuperColumnSerializer implements IColumnSerializer
public IColumn deserialize(DataInput dis) throws IOException
{
return deserialize(dis, null);
return deserialize(dis, null, false);
}
public IColumn deserialize(DataInput dis, ColumnFamilyStore interner) throws IOException
{
return deserialize(dis, interner, false);
}
public IColumn deserialize(DataInput dis, ColumnFamilyStore interner, boolean fromRemote) throws IOException
{
ByteBuffer name = ByteBufferUtil.readWithShortLength(dis);
int localDeleteTime = dis.readInt();
@ -377,7 +382,7 @@ class SuperColumnSerializer implements IColumnSerializer
/* read the number of columns */
int size = dis.readInt();
ColumnSerializer serializer = Column.serializer();
ColumnSortedMap preSortedMap = new ColumnSortedMap(comparator, serializer, dis, interner, size);
ColumnSortedMap preSortedMap = new ColumnSortedMap(comparator, serializer, dis, interner, size, fromRemote);
SuperColumn superColumn = new SuperColumn(name, new ConcurrentSkipListMap<ByteBuffer,IColumn>(preSortedMap));
if (localDeleteTime != Integer.MIN_VALUE && localDeleteTime <= 0)
{

View File

@ -26,8 +26,11 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.ExecutionException;
import org.slf4j.Logger;
@ -43,6 +46,7 @@ import org.apache.cassandra.dht.Token;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.NodeId;
import static com.google.common.base.Charsets.UTF_8;
@ -51,6 +55,7 @@ public class SystemTable
private static Logger logger = LoggerFactory.getLogger(SystemTable.class);
public static final String STATUS_CF = "LocationInfo"; // keep the old CF string for backwards-compatibility
public static final String INDEX_CF = "IndexInfo";
public static final String NODE_ID_CF = "NodeIdInfo";
private static final ByteBuffer LOCATION_KEY = ByteBufferUtil.bytes("L");
private static final ByteBuffer RING_KEY = ByteBufferUtil.bytes("Ring");
private static final ByteBuffer BOOTSTRAP_KEY = ByteBufferUtil.bytes("Bootstrap");
@ -60,6 +65,8 @@ public class SystemTable
private static final ByteBuffer GENERATION = ByteBufferUtil.bytes("Generation");
private static final ByteBuffer CLUSTERNAME = ByteBufferUtil.bytes("ClusterName");
private static final ByteBuffer PARTITIONER = ByteBufferUtil.bytes("Partioner");
private static final ByteBuffer CURRENT_LOCAL_NODE_ID_KEY = ByteBufferUtil.bytes("CurrentLocal");
private static final ByteBuffer ALL_LOCAL_NODE_ID_KEY = ByteBufferUtil.bytes("Local");
private static DecoratedKey decorate(ByteBuffer key)
{
@ -370,4 +377,88 @@ public class SystemTable
forceBlockingFlush(INDEX_CF);
}
/**
* Read the current local node id from the system table or null if no
* such node id is recorded.
*/
public static NodeId getCurrentLocalNodeId()
{
ByteBuffer id = null;
Table table = Table.open(Table.SYSTEM_TABLE);
QueryFilter filter = QueryFilter.getIdentityFilter(decorate(CURRENT_LOCAL_NODE_ID_KEY),
new QueryPath(NODE_ID_CF));
ColumnFamily cf = table.getColumnFamilyStore(NODE_ID_CF).getColumnFamily(filter);
if (cf != null)
{
assert cf.getColumnCount() <= 1;
if (cf.getColumnCount() > 0)
id = cf.iterator().next().name();
}
if (id != null)
{
return NodeId.wrap(id);
}
else
{
return null;
}
}
/**
* Write a new current local node id to the system table.
*
* @param oldNodeId the previous local node id (that {@code newNodeId}
* replace) or null if no such node id exists (new node or removed system
* table)
* @param newNodeId the new current local node id to record
*/
public static void writeCurrentLocalNodeId(NodeId oldNodeId, NodeId newNodeId)
{
long now = System.currentTimeMillis();
ByteBuffer ip = ByteBuffer.wrap(FBUtilities.getLocalAddress().getAddress());
ColumnFamily cf = ColumnFamily.create(Table.SYSTEM_TABLE, NODE_ID_CF);
cf.addColumn(new Column(newNodeId.bytes(), ip, now));
ColumnFamily cf2 = cf.cloneMe();
if (oldNodeId != null)
{
cf2.addColumn(new DeletedColumn(oldNodeId.bytes(), (int) (now / 1000), now));
}
RowMutation rmCurrent = new RowMutation(Table.SYSTEM_TABLE, CURRENT_LOCAL_NODE_ID_KEY);
RowMutation rmAll = new RowMutation(Table.SYSTEM_TABLE, ALL_LOCAL_NODE_ID_KEY);
rmCurrent.add(cf2);
rmAll.add(cf);
try
{
rmCurrent.apply();
rmAll.apply();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
public static List<NodeId.NodeIdRecord> getOldLocalNodeIds()
{
List<NodeId.NodeIdRecord> l = new ArrayList<NodeId.NodeIdRecord>();
Table table = Table.open(Table.SYSTEM_TABLE);
QueryFilter filter = QueryFilter.getIdentityFilter(decorate(ALL_LOCAL_NODE_ID_KEY),
new QueryPath(NODE_ID_CF));
ColumnFamily cf = table.getColumnFamilyStore(NODE_ID_CF).getColumnFamily(filter);
NodeId previous = null;
for (IColumn c : cf.getReverseSortedColumns())
{
if (previous != null)
l.add(new NodeId.NodeIdRecord(previous, c.timestamp()));
// this will ignore the last column on purpose since it is the
// current local node id
previous = NodeId.wrap(c.name());
}
return l;
}
}

View File

@ -51,6 +51,7 @@ import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.locator.AbstractReplicationStrategy;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.NodeId;
import org.cliffc.high_scale_lib.NonBlockingHashMap;
public class Table
@ -161,6 +162,36 @@ public class Table
return cfs;
}
/**
* Do a cleanup of keys that do not belong locally.
*/
public void forceCleanup(NodeId.OneShotRenewer renewer) throws IOException, ExecutionException, InterruptedException
{
if (name.equals(SYSTEM_TABLE))
throw new UnsupportedOperationException("Cleanup of the system table is neither necessary nor wise");
// Sort the column families in order of SSTable size, so cleanup of smaller CFs
// can free up space for larger ones
List<ColumnFamilyStore> sortedColumnFamilies = new ArrayList<ColumnFamilyStore>(columnFamilyStores.values());
Collections.sort(sortedColumnFamilies, new Comparator<ColumnFamilyStore>()
{
// Compare first on size and, if equal, sort by name (arbitrary & deterministic).
public int compare(ColumnFamilyStore cf1, ColumnFamilyStore cf2)
{
long diff = (cf1.getTotalDiskSpaceUsed() - cf2.getTotalDiskSpaceUsed());
if (diff > 0)
return 1;
if (diff < 0)
return -1;
return cf1.columnFamily.compareTo(cf2.columnFamily);
}
});
// Cleanup in sorted order to free up space for the larger ones
for (ColumnFamilyStore cfs : sortedColumnFamilies)
cfs.forceCleanup(renewer);
}
/**
* Take a snapshot of the entire set of column families with a given timestamp.
*

View File

@ -17,41 +17,56 @@
*/
package org.apache.cassandra.db.context;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import org.apache.commons.lang.ArrayUtils;
import java.security.MessageDigest;
import java.util.*;
import org.apache.cassandra.db.DBConstants;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.NodeId;
/**
* An implementation of a partitioned counter context.
*
* The data structure is:
* a vector of (node id, logical clock, count) tuples.
* A context is primarily a list of tuples (node id, clock, count) -- called
* shard in the following. But with some shard are flagged as delta (with
* special resolution rules in merge()).
*
* On update, the node will:
* 1) increment the logical clock, and
* 2) update the count associated w/ its tuple.
* The data structure has two parts:
* a) a header containing the lists of "delta" (a list of references to the second parts)
* b) a list of shard -- (node id, logical clock, count) tuples -- (the so-called 'body' below)
*
* The logical clock represents the # of operations executed
* by the node. The aggregated count can be determined
* by rolling up all the counts from each tuple.
* The exact layout is:
* | header | body |
* context : |--|------|----------|
* ^ ^
* | list of indices in the body list (2*#elt bytes)
* #elt in rest of header (2 bytes)
*
* NOTE: only a given node id may increment its associated count and
* care must be taken to ensure that tuples are correctly made consistent.
* The body layout being:
*
* body: |----|----|----|----|----|----|....
* ^ ^ ^ ^ ^ ^
* | | count_1 | | count_2
* | clock_1 | clock_2
* nodeid_1 nodeid_2
*
* The rules when merging two shard with the same nodeid are:
* - delta + delta = sum counts (and logical clock)
* - delta + other = keep the delta one
* - other + other = keep the shard with highest logical clock
*/
public class CounterContext implements IContext
{
private static final int idLength;
private static final byte[] localId;
private static final ByteBuffer wrappedLocalId;
private static final int clockLength = DBConstants.longSize_;
private static final int countLength = DBConstants.longSize_;
private static final int stepLength; // length: id + logical clock + count
private static final int HEADER_SIZE_LENGTH = DBConstants.shortSize_;
private static final int HEADER_ELT_LENGTH = DBConstants.shortSize_;
private static final int CLOCK_LENGTH = DBConstants.longSize_;
private static final int COUNT_LENGTH = DBConstants.longSize_;
private static final int STEP_LENGTH = NodeId.LENGTH + CLOCK_LENGTH + COUNT_LENGTH;
// Time in ms since a node id has been renewed before we consider using it
// during a merge
private static final long MIN_MERGE_DELAY = 5 * 60 * 1000; // should be aplenty
// lazy-load singleton
private static class LazyHolder
@ -59,14 +74,6 @@ public class CounterContext implements IContext
private static final CounterContext counterContext = new CounterContext();
}
static
{
localId = FBUtilities.getLocalAddress().getAddress();
wrappedLocalId = ByteBuffer.wrap(localId);
idLength = localId.length;
stepLength = idLength + clockLength + countLength;
}
public static CounterContext instance()
{
return LazyHolder.counterContext;
@ -81,25 +88,43 @@ public class CounterContext implements IContext
*/
public ByteBuffer create(long value)
{
ByteBuffer context = ByteBuffer.allocate(stepLength);
writeElementAtOffset(context, 0, localId, 1L, value);
ByteBuffer context = ByteBuffer.allocate(HEADER_SIZE_LENGTH + HEADER_ELT_LENGTH + STEP_LENGTH);
// The first (and only) elt is a delta
context.putShort(0, (short)1);
context.putShort(HEADER_SIZE_LENGTH, (short)0);
writeElementAtOffset(context, HEADER_SIZE_LENGTH + HEADER_ELT_LENGTH, NodeId.getLocalId(), 1L, value);
return context;
}
// For testing purposes
public ByteBuffer create(byte[] id, long clock, long value)
// Provided for use by unit tests
public ByteBuffer create(NodeId id, long clock, long value, boolean isDelta)
{
ByteBuffer context = ByteBuffer.allocate(stepLength);
writeElementAtOffset(context, 0, id, clock, value);
ByteBuffer context = ByteBuffer.allocate(HEADER_SIZE_LENGTH + (isDelta ? HEADER_ELT_LENGTH : 0) + STEP_LENGTH);
context.putShort(0, (short)(isDelta ? 1 : 0));
if (isDelta)
{
context.putShort(HEADER_SIZE_LENGTH, (short)0);
}
writeElementAtOffset(context, HEADER_SIZE_LENGTH + (isDelta ? HEADER_ELT_LENGTH : 0), id, clock, value);
return context;
}
// write a tuple (node id, clock, count) at offset
protected static void writeElementAtOffset(ByteBuffer context, int offset, byte[] id, long clock, long count)
private static void writeElementAtOffset(ByteBuffer context, int offset, NodeId id, long clock, long count)
{
System.arraycopy(id, 0, context.array(), offset + context.arrayOffset(), idLength);
context.putLong(offset + idLength, clock);
context.putLong(offset + idLength + clockLength, count);
ByteBufferUtil.arrayCopy(id.bytes(), id.bytes().position(), context, offset, NodeId.LENGTH);
context.putLong(offset + NodeId.LENGTH, clock);
context.putLong(offset + NodeId.LENGTH + CLOCK_LENGTH, count);
}
private static int headerLength(ByteBuffer context)
{
return HEADER_SIZE_LENGTH + context.getShort(context.position()) * HEADER_ELT_LENGTH;
}
private static int compareId(ByteBuffer bb1, int pos1, ByteBuffer bb2, int pos2)
{
return ByteBufferUtil.compareSubArrays(bb1, pos1, bb2, pos2, NodeId.LENGTH);
}
/**
@ -110,40 +135,38 @@ public class CounterContext implements IContext
* LESS_THAN: Subset of nodes and every count is equal or less than its corollary.
* DISJOINT: Node sets are not equal and/or counts are not all greater or less than.
*
* Strategy:
* compare node logical clocks (like a version vector).
* Strategy: compare node logical clocks (like a version vector).
*
* @param left
* counter context.
* @param right
* counter context.
* @param left counter context.
* @param right counter context.
* @return the ContextRelationship between the contexts.
*/
public ContextRelationship diff(ByteBuffer left, ByteBuffer right)
{
ContextRelationship relationship = ContextRelationship.EQUAL;
ContextState leftState = new ContextState(left, headerLength(left));
ContextState rightState = new ContextState(right, headerLength(right));
int leftIndex = left.position();
int rightIndex = right.position();
while (leftIndex < left.remaining() && rightIndex < right.remaining())
while (leftState.hasRemaining() && rightState.hasRemaining())
{
// compare id bytes
int compareId = ByteBufferUtil.compareSubArrays(left, leftIndex, right, rightIndex, idLength);
int compareId = leftState.compareIdTo(rightState);
if (compareId == 0)
{
long leftClock = left.getLong(leftIndex + idLength);
long rightClock = right.getLong(rightIndex + idLength);
long leftClock = leftState.getClock();
long rightClock = rightState.getClock();
// advance indexes
leftIndex += stepLength;
rightIndex += stepLength;
// advance
leftState.moveToNext();
rightState.moveToNext();
// process clock comparisons
if (leftClock == rightClock)
{
continue;
}
else if (leftClock > rightClock)
else if ((leftClock >= 0 && rightClock > 0 && leftClock > rightClock)
|| (leftClock < 0 && (rightClock > 0 || leftClock < rightClock)))
{
if (relationship == ContextRelationship.EQUAL)
{
@ -160,7 +183,6 @@ public class CounterContext implements IContext
}
}
else
// leftClock < rightClock
{
if (relationship == ContextRelationship.EQUAL)
{
@ -180,7 +202,7 @@ public class CounterContext implements IContext
else if (compareId > 0)
{
// only advance the right context
rightIndex += stepLength;
rightState.moveToNext();
if (relationship == ContextRelationship.EQUAL)
{
@ -196,11 +218,10 @@ public class CounterContext implements IContext
continue;
}
}
else
else // compareId < 0
{
// compareId < 0
// only advance the left context
leftIndex += stepLength;
leftState.moveToNext();
if (relationship == ContextRelationship.EQUAL)
{
@ -219,7 +240,7 @@ public class CounterContext implements IContext
}
// check final lengths
if (leftIndex < left.remaining())
if (leftState.hasRemaining())
{
if (relationship == ContextRelationship.EQUAL)
{
@ -230,7 +251,7 @@ public class CounterContext implements IContext
return ContextRelationship.DISJOINT;
}
}
else if (rightIndex < right.remaining())
else if (rightState.hasRemaining())
{
if (relationship == ContextRelationship.EQUAL)
{
@ -248,100 +269,108 @@ public class CounterContext implements IContext
/**
* Return a context w/ an aggregated count for each node id.
*
* @param left
* counter context.
* @param right
* counter context.
* @param left counter context.
* @param right counter context.
*/
public ByteBuffer merge(ByteBuffer left, ByteBuffer right)
{
ContextState leftState = new ContextState(left, headerLength(left));
ContextState rightState = new ContextState(right, headerLength(right));
// Compute size of result
int size = 0;
int leftOffset = left.position();
int rightOffset = right.position();
while ((leftOffset < left.limit()) && (rightOffset < right.limit()))
int mergedHeaderLength = HEADER_SIZE_LENGTH;
int mergedBodyLength = 0;
while (leftState.hasRemaining() && rightState.hasRemaining())
{
int cmp = ByteBufferUtil.compareSubArrays(left, leftOffset, right, rightOffset, idLength);
int cmp = leftState.compareIdTo(rightState);
if (cmp == 0)
{
++size;
rightOffset += stepLength;
leftOffset += stepLength;
mergedBodyLength += STEP_LENGTH;
if (leftState.isDelta() || rightState.isDelta())
mergedHeaderLength += HEADER_ELT_LENGTH;
leftState.moveToNext();
rightState.moveToNext();
}
else if (cmp > 0)
{
++size;
rightOffset += stepLength;
mergedBodyLength += STEP_LENGTH;
if (rightState.isDelta())
mergedHeaderLength += HEADER_ELT_LENGTH;
rightState.moveToNext();
}
else // cmp < 0
{
++size;
leftOffset += stepLength;
mergedBodyLength += STEP_LENGTH;
if (leftState.isDelta())
mergedHeaderLength += HEADER_ELT_LENGTH;
leftState.moveToNext();
}
}
size += (left.limit() - leftOffset) / stepLength;
size += (right.limit() - rightOffset) / stepLength;
mergedHeaderLength += leftState.remainingHeaderLength() + rightState.remainingHeaderLength();
mergedBodyLength += leftState.remainingBodyLength() + rightState.remainingBodyLength();
ByteBuffer merged = ByteBuffer.allocate(size * stepLength);
// Do the actual merge:
// a) local id: sum clocks, counts
// b) remote id: keep highest clock, count (reconcile)
int mergedOffset = merged.position();
leftOffset = left.position();
rightOffset = right.position();
while ((leftOffset < left.limit()) && (rightOffset < right.limit()))
// Do the actual merge
ByteBuffer merged = ByteBuffer.allocate(mergedHeaderLength + mergedBodyLength);
merged.putShort(merged.position(), (short) ((mergedHeaderLength - HEADER_SIZE_LENGTH) / HEADER_ELT_LENGTH));
ContextState mergedState = new ContextState(merged, mergedHeaderLength);
leftState.reset();
rightState.reset();
while (leftState.hasRemaining() && rightState.hasRemaining())
{
int cmp = ByteBufferUtil.compareSubArrays(left, leftOffset, right, rightOffset, idLength);
int cmp = leftState.compareIdTo(rightState);
if (cmp == 0)
{
// sum for local id, keep highest othewise
long leftClock = left.getLong(leftOffset + idLength);
long rightClock = right.getLong(rightOffset + idLength);
if (ByteBufferUtil.compareSubArrays(left, leftOffset, wrappedLocalId, 0, idLength) == 0)
if (leftState.isDelta() || rightState.isDelta())
{
long leftCount = left.getLong(leftOffset + idLength + clockLength);
long rightCount = right.getLong(rightOffset + idLength + clockLength);
writeElementAtOffset(merged, mergedOffset, localId, leftClock + rightClock, leftCount + rightCount);
// Local id and at least one is a delta
if (leftState.isDelta() && rightState.isDelta())
{
// both delta, sum
long clock = leftState.getClock() + rightState.getClock();
long count = leftState.getCount() + rightState.getCount();
mergedState.writeElement(leftState.getNodeId(), clock, count, true);
}
else
{
// Only one have delta, keep that one
(leftState.isDelta() ? leftState : rightState).copyTo(mergedState);
}
}
else
{
if (leftClock >= rightClock)
ByteBufferUtil.arrayCopy(left, leftOffset, merged, mergedOffset, stepLength);
long leftClock = leftState.getClock();
long rightClock = rightState.getClock();
if ((leftClock >= 0 && rightClock > 0 && leftClock >= rightClock)
|| (leftClock < 0 && (rightClock > 0 || leftClock < rightClock)))
leftState.copyTo(mergedState);
else
ByteBufferUtil.arrayCopy(right, rightOffset, merged, mergedOffset, stepLength);
rightState.copyTo(mergedState);
}
mergedOffset += stepLength;
rightOffset += stepLength;
leftOffset += stepLength;
rightState.moveToNext();
leftState.moveToNext();
}
else if (cmp > 0)
{
ByteBufferUtil.arrayCopy(right, rightOffset, merged, mergedOffset, stepLength);
mergedOffset += stepLength;
rightOffset += stepLength;
rightState.copyTo(mergedState);
rightState.moveToNext();
}
else // cmp < 0
{
ByteBufferUtil.arrayCopy(left, leftOffset, merged, mergedOffset, stepLength);
mergedOffset += stepLength;
leftOffset += stepLength;
leftState.copyTo(mergedState);
leftState.moveToNext();
}
}
if (leftOffset < left.limit())
ByteBufferUtil.arrayCopy(
left,
leftOffset,
merged,
mergedOffset,
left.limit() - leftOffset);
if (rightOffset < right.limit())
ByteBufferUtil.arrayCopy(
right,
rightOffset,
merged,
mergedOffset,
right.limit() - rightOffset);
while (leftState.hasRemaining())
{
leftState.copyTo(mergedState);
leftState.moveToNext();
}
while (rightState.hasRemaining())
{
rightState.copyTo(mergedState);
rightState.moveToNext();
}
return merged;
}
@ -349,88 +378,391 @@ public class CounterContext implements IContext
/**
* Human-readable String from context.
*
* @param context
* version context.
* @param context counter context.
* @return a human-readable String of the context.
*/
public String toString(ByteBuffer context)
{
ContextState state = new ContextState(context, headerLength(context));
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int offset = context.position(); offset < context.limit(); offset += stepLength)
while (state.hasRemaining())
{
if (offset > context.position())
if (state.elementIdx() > 0)
{
sb.append(",");
}
sb.append("{");
try
{
int absOffset = context.arrayOffset() + offset;
InetAddress address = InetAddress.getByAddress(
ArrayUtils.subarray(context.array(), absOffset, absOffset + idLength));
sb.append(address.getHostAddress());
}
catch (UnknownHostException uhe)
{
sb.append("?.?.?.?");
}
sb.append(", ");
sb.append(context.getLong(offset + idLength));
sb.append(", ");
sb.append(context.getLong(offset + idLength + clockLength));
sb.append(state.getNodeId().toString()).append(", ");
sb.append(state.getClock()).append(", ");;
sb.append(state.getCount());
sb.append("}");
if (state.isDelta())
{
sb.append("*");
}
state.moveToNext();
}
sb.append("]");
return sb.toString();
}
// return an aggregated count across all node ids
/**
* Returns the aggregated count across all node ids.
*
* @param context a counter context
* @terurn the aggregated count represented by {@code context}
*/
public long total(ByteBuffer context)
{
long total = 0L;
for (int offset = context.position(); offset < context.limit(); offset += stepLength)
// we could use a ContextState but it is easy enough that we avoid the object creation
for (int offset = context.position() + headerLength(context); offset < context.limit(); offset += STEP_LENGTH)
{
long count = context.getLong(offset + idLength + clockLength);
long count = context.getLong(offset + NodeId.LENGTH + CLOCK_LENGTH);
total += count;
}
return total;
}
// remove the count for a given node id
public ByteBuffer cleanNodeCounts(ByteBuffer context, InetAddress node)
/**
* Remove all the delta of a context (i.e, set an empty header).
*
* @param context a counter context
* @return a version of {@code context} where no count are a delta.
*/
public ByteBuffer clearAllDelta(ByteBuffer context)
{
// calculate node id
ByteBuffer nodeId = ByteBuffer.wrap(node.getAddress());
int headerLength = headerLength(context);
if (headerLength == 0)
return context;
// look for this node id
for (int offset = 0; offset < context.remaining(); offset += stepLength)
ByteBuffer cleaned = ByteBuffer.allocate(context.remaining() - headerLength + HEADER_SIZE_LENGTH);
cleaned.putShort(cleaned.position(), (short)0);
ByteBufferUtil.arrayCopy(
context,
context.position() + headerLength,
cleaned,
cleaned.position() + HEADER_SIZE_LENGTH,
context.remaining() - headerLength);
return cleaned;
}
/**
* Update a MessageDigest with the content of a context.
* Note that this skips the header entirely since the header information
* has local meaning only, while digests a meant for comparison across
* nodes. This means in particular that we always have:
* updateDigest(ctx) == updateDigest(clearAllDelta(ctx))
*/
public void updateDigest(MessageDigest message, ByteBuffer context)
{
int hlength = headerLength(context);
ByteBuffer dup = context.duplicate();
dup.position(context.position() + hlength);
message.update(dup);
}
/**
* Checks whether the provided context has a count for the provided
* NodeId.
*
* TODO: since the context is sorted, we could implement a binary search.
* This is however not called in any critical path and contexts will be
* fairly small so it doesn't matter much.
*/
public boolean hasNodeId(ByteBuffer context, NodeId id)
{
// we could use a ContextState but it is easy enough that we avoid the object creation
for (int offset = context.position() + headerLength(context); offset < context.limit(); offset += STEP_LENGTH)
{
int cmp = ByteBufferUtil.compareSubArrays(context, context.position() + offset, nodeId, 0, idLength);
if (cmp < 0)
continue;
else if (cmp == 0)
if (id.equals(NodeId.wrap(context, offset)))
{
// node id found: remove node count
ByteBuffer truncatedContext = ByteBuffer.allocate(context.remaining() - stepLength);
ByteBufferUtil.arrayCopy(context, context.position(), truncatedContext, 0, offset);
ByteBufferUtil.arrayCopy(
context,
context.position() + offset + stepLength,
truncatedContext,
offset,
context.remaining() - (offset + stepLength));
return truncatedContext;
}
else // cmp > 0
{
break; // node id not present
return true;
}
}
return false;
}
return context;
/**
* Compute a new context such that if applied to context yields the same
* total but with the older local node id merged into the second to older one
* (excluding current local node id) if need be.
*/
public ByteBuffer computeOldShardMerger(ByteBuffer context, List<NodeId.NodeIdRecord> oldIds)
{
long now = System.currentTimeMillis();
int hlength = headerLength(context);
// Don't bother if we know we can't find what we are looking for
if (oldIds.size() < 2
|| now - oldIds.get(0).timestamp < MIN_MERGE_DELAY
|| now - oldIds.get(1).timestamp < MIN_MERGE_DELAY
|| context.remaining() - hlength < 2 * STEP_LENGTH)
return null;
Iterator<NodeId.NodeIdRecord> recordIterator = oldIds.iterator();
NodeId.NodeIdRecord currRecord = recordIterator.next();
ContextState state = new ContextState(context, hlength);
ContextState foundState = null;
while (state.hasRemaining() && currRecord != null)
{
if (now - currRecord.timestamp < MIN_MERGE_DELAY)
return context;
int c = state.getNodeId().compareTo(currRecord.id);
if (c == 0)
{
if (foundState == null)
{
// We found a canditate for being merged
if (state.getClock() < 0)
return null;
foundState = state.duplicate();
currRecord = recordIterator.hasNext() ? recordIterator.next() : null;
state.moveToNext();
}
else
{
// Found someone to merge it to
int nbDelta = foundState.isDelta() ? 1 : 0;
nbDelta += state.isDelta() ? 1 : 0;
ContextState merger = ContextState.allocate(2, nbDelta);
long fclock = foundState.getClock();
long fcount = foundState.getCount();
long clock = state.getClock();
long count = state.getCount();
if (foundState.isDelta())
merger.writeElement(foundState.getNodeId(), -now - fclock, -fcount, true);
else
merger.writeElement(foundState.getNodeId(), -now, 0);
if (state.isDelta())
merger.writeElement(state.getNodeId(), fclock + clock, fcount, true);
else
merger.writeElement(state.getNodeId(), fclock + clock, fcount + count);
return merger.context;
}
}
else if (c < 0) // nodeid < record
{
state.moveToNext();
}
else // c > 0, nodeid > record
{
currRecord = recordIterator.hasNext() ? recordIterator.next() : null;
}
}
return null;
}
/**
* Remove shards that have been canceled through computeOldShardMerger
* since a time older than gcBefore.
* Used by compaction to strip context of unecessary information,
* shrinking them.
*/
public ByteBuffer removeOldShards(ByteBuffer context, int gcBefore)
{
int hlength = headerLength(context);
ContextState state = new ContextState(context, hlength);
int removedBodySize = 0, removedHeaderSize = 0;
while (state.hasRemaining())
{
long clock = state.getClock();
if (clock < 0 && -((int)(clock / 1000)) < gcBefore)
{
assert state.getCount() == 0;
removedBodySize += STEP_LENGTH;
if (state.isDelta())
removedHeaderSize += HEADER_ELT_LENGTH;
}
state.moveToNext();
}
if (removedBodySize == 0)
return context;
int newSize = context.remaining() - removedHeaderSize - removedBodySize;
int newHlength = hlength - removedHeaderSize;
ByteBuffer cleanedContext = ByteBuffer.allocate(newSize);
cleanedContext.putShort(cleanedContext.position(), (short) ((newHlength - HEADER_SIZE_LENGTH) / HEADER_ELT_LENGTH));
ContextState cleaned = new ContextState(cleanedContext, newHlength);
state.reset();
while (state.hasRemaining())
{
long clock = state.getClock();
if (clock > 0 || -((int)(clock / 1000)) >= gcBefore)
{
state.copyTo(cleaned);
}
state.moveToNext();
}
return cleanedContext;
}
/**
* Helper class to work on contexts (works by iterating over them).
* A context being abstractly a list of tuple (nodeid, clock, count), a
* ContextState encapsulate a context and a position to one of the tuple.
* It also allow to create new context iteratively.
*
* Note: this is intrinsically a private class intended for use by the
* methods of CounterContext only. It is however public because it is
* convenient to create handcrafted context for unit tests.
*/
public static class ContextState
{
public final ByteBuffer context;
public final int headerLength;
private int headerOffset; // offset from context.position()
private int bodyOffset; // offset from context.position()
private boolean currentIsDelta;
public ContextState(ByteBuffer context, int headerLength)
{
this(context, headerLength, HEADER_SIZE_LENGTH, headerLength, false);
updateIsDelta();
}
public ContextState(ByteBuffer context)
{
this(context, headerLength(context));
}
private ContextState(ByteBuffer context, int headerLength, int headerOffset, int bodyOffset, boolean currentIsDelta)
{
this.context = context;
this.headerLength = headerLength;
this.headerOffset = headerOffset;
this.bodyOffset = bodyOffset;
this.currentIsDelta = currentIsDelta;
}
public boolean isDelta()
{
return currentIsDelta;
}
private void updateIsDelta()
{
currentIsDelta = (headerOffset < headerLength) && context.getShort(context.position() + headerOffset) == (short) elementIdx();
}
public boolean hasRemaining()
{
return bodyOffset < context.remaining();
}
public int remainingHeaderLength()
{
return headerLength - headerOffset;
}
public int remainingBodyLength()
{
return context.remaining() - bodyOffset;
}
public void moveToNext()
{
bodyOffset += STEP_LENGTH;
if (currentIsDelta)
{
headerOffset += HEADER_ELT_LENGTH;
}
updateIsDelta();
}
// This advance other to the next position (but not this)
public void copyTo(ContextState other)
{
ByteBufferUtil.arrayCopy(context, context.position() + bodyOffset, other.context, other.context.position() + other.bodyOffset, STEP_LENGTH);
if (currentIsDelta)
{
other.context.putShort(other.context.position() + other.headerOffset, (short) other.elementIdx());
}
other.currentIsDelta = currentIsDelta;
other.moveToNext();
}
public int compareIdTo(ContextState other)
{
return compareId(context, context.position() + bodyOffset, other.context, other.context.position() + other.bodyOffset);
}
public void reset()
{
this.headerOffset = HEADER_SIZE_LENGTH;
this.bodyOffset = headerLength;
updateIsDelta();
}
public NodeId getNodeId()
{
return NodeId.wrap(context, context.position() + bodyOffset);
}
public long getClock()
{
return context.getLong(context.position() + bodyOffset + NodeId.LENGTH);
}
public long getCount()
{
return context.getLong(context.position() + bodyOffset + NodeId.LENGTH + CLOCK_LENGTH);
}
// Advance this to the next position
public void writeElement(NodeId id, long clock, long count, boolean isDelta)
{
writeElementAtOffset(context, context.position() + bodyOffset, id, clock, count);
if (isDelta)
{
context.putShort(context.position() + headerOffset, (short)elementIdx());
}
currentIsDelta = isDelta;
moveToNext();
}
public void writeElement(NodeId id, long clock, long count)
{
writeElement(id, clock, count, false);
}
public int elementIdx()
{
return (bodyOffset - headerLength) / STEP_LENGTH;
}
public ContextState duplicate()
{
return new ContextState(context, headerLength, headerOffset, bodyOffset, currentIsDelta);
}
/*
* Allocate a new context big enough for {@code elementCount} elements
* with {@code deltaCount} of them being delta, and return the initial
* ContextState corresponding.
*/
public static ContextState allocate(int elementCount, int deltaCount)
{
assert deltaCount <= elementCount;
int hlength = HEADER_SIZE_LENGTH + deltaCount * HEADER_ELT_LENGTH;
ByteBuffer context = ByteBuffer.allocate(hlength + elementCount * STEP_LENGTH);
context.putShort(0, (short)deltaCount);
return new ContextState(context, hlength);
}
}
}

View File

@ -43,9 +43,4 @@ public abstract class AbstractCommutativeType extends AbstractType<Long>
* create commutative column
*/
public abstract Column createColumn(ByteBuffer name, ByteBuffer value, long timestamp);
/**
* remove target node from commutative columns
*/
public abstract void cleanContext(IColumnContainer cc, InetAddress node);
}

View File

@ -53,38 +53,6 @@ public class CounterColumnType extends AbstractCommutativeType
return new CounterUpdateColumn(name, value, timestamp);
}
/**
* remove target node from commutative columns
*/
public void cleanContext(IColumnContainer cc, InetAddress node)
{
if ((cc instanceof ColumnFamily) && ((ColumnFamily)cc).isSuper())
{
for (IColumn column : cc.getSortedColumns())
{
SuperColumn supercol = (SuperColumn)column;
cleanContext(supercol, node);
if (0 == supercol.getSubColumns().size())
cc.remove(supercol.name());
}
return;
}
for (IColumn column : cc.getSortedColumns())
{
if (!(column instanceof CounterColumn)) // DeletedColumn
continue;
CounterColumn counterColumn = (CounterColumn)column;
CounterColumn cleanedColumn = counterColumn.cleanNodeCounts(node);
if (cleanedColumn == counterColumn)
continue;
cc.remove(counterColumn.name());
//XXX: on "clean," must copy-and-replace
if (null != cleanedColumn)
cc.addColumn(cleanedColumn);
}
}
public ByteBuffer fromString(String source)
{
return ByteBufferUtil.hexToBytes(source);
@ -96,4 +64,3 @@ public class CounterColumnType extends AbstractCommutativeType
throw new MarshalException(String.format("Expected 8 or 0 byte long (%d)", bytes.remaining()));
}
}

View File

@ -1,4 +1,24 @@
package org.apache.cassandra.io;
/*
*
* 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.
*
*/
import java.io.DataInput;
import java.io.IOException;
@ -8,5 +28,5 @@ import org.apache.cassandra.db.IColumn;
public interface IColumnSerializer extends ICompactSerializer2<IColumn>
{
public IColumn deserialize(DataInput in, ColumnFamilyStore interner) throws IOException;
public IColumn deserialize(DataInput in, ColumnFamilyStore interner, boolean fromRemote) throws IOException;
}

View File

@ -34,6 +34,7 @@ import org.apache.commons.collections.iterators.CollatingIterator;
import org.apache.cassandra.db.ColumnFamily;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.ColumnIndexer;
import org.apache.cassandra.db.CounterColumn;
import org.apache.cassandra.db.IColumn;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.io.sstable.SSTable;
@ -143,17 +144,7 @@ public class LazilyCompactedRow extends AbstractCompactedRow implements IIterabl
Iterator<IColumn> iter = iterator();
while (iter.hasNext())
{
IColumn column = iter.next();
out.reset();
try
{
emptyColumnFamily.getColumnSerializer().serialize(column, out);
}
catch (IOException e)
{
throw new IOError(e);
}
digest.update(out.getData(), 0, out.getLength());
iter.next().updateDigest(digest);
}
}
@ -225,6 +216,10 @@ public class LazilyCompactedRow extends AbstractCompactedRow implements IIterabl
assert container != null;
IColumn reduced = container.iterator().next();
ColumnFamily purged = shouldPurge ? ColumnFamilyStore.removeDeleted(container, gcBefore) : container;
if (purged != null && purged.metadata().getDefaultValidator().isCommutative())
{
CounterColumn.removeOldShards(purged, gcBefore);
}
if (purged == null || !purged.iterator().hasNext())
{
container.clear();

View File

@ -35,6 +35,7 @@ import org.slf4j.LoggerFactory;
import org.apache.cassandra.db.ColumnFamily;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.ColumnIndexer;
import org.apache.cassandra.db.CounterColumn;
import org.apache.cassandra.db.DecoratedKey;
import org.apache.cassandra.io.sstable.SSTable;
import org.apache.cassandra.io.sstable.SSTableIdentityIterator;
@ -47,22 +48,17 @@ public class PrecompactedRow extends AbstractCompactedRow
{
private static Logger logger = LoggerFactory.getLogger(PrecompactedRow.class);
private final DataOutputBuffer buffer;
private final DataOutputBuffer headerBuffer;
private int columnCount = 0;
private final ColumnFamily compactedCf;
public PrecompactedRow(DecoratedKey key, DataOutputBuffer buffer)
public PrecompactedRow(DecoratedKey key, ColumnFamily compacted)
{
super(key);
this.buffer = buffer;
this.headerBuffer = new DataOutputBuffer();
this.compactedCf = compacted;
}
public PrecompactedRow(ColumnFamilyStore cfStore, List<SSTableIdentityIterator> rows, boolean major, int gcBefore, boolean forceDeserialize)
{
super(rows.get(0).getKey());
buffer = new DataOutputBuffer();
headerBuffer = new DataOutputBuffer();
Set<SSTable> sstables = new HashSet<SSTable>();
for (SSTableIdentityIterator row : rows)
@ -71,73 +67,75 @@ public class PrecompactedRow extends AbstractCompactedRow
}
boolean shouldPurge = major || !cfStore.isKeyInRemainingSSTables(key, sstables);
if (rows.size() > 1 || shouldPurge || !rows.get(0).sstable.descriptor.isLatestVersion || forceDeserialize)
ColumnFamily cf = null;
for (SSTableIdentityIterator row : rows)
{
ColumnFamily cf = null;
for (SSTableIdentityIterator row : rows)
{
ColumnFamily thisCF;
try
{
thisCF = row.getColumnFamilyWithColumns();
}
catch (IOException e)
{
logger.error("Skipping row " + key + " in " + row.getPath(), e);
continue;
}
if (cf == null)
{
cf = thisCF;
}
else
{
cf.addAll(thisCF);
}
}
ColumnFamily cfPurged = shouldPurge ? ColumnFamilyStore.removeDeleted(cf, gcBefore) : cf;
if (cfPurged == null)
return;
ColumnIndexer.serialize(cfPurged, headerBuffer);
columnCount = ColumnFamily.serializer().serializeForSSTable(cfPurged, buffer);
}
else
{
assert rows.size() == 1;
ColumnFamily thisCF;
try
{
rows.get(0).echoData(buffer);
columnCount = rows.get(0).columnCount;
thisCF = row.getColumnFamilyWithColumns();
}
catch (IOException e)
{
throw new IOError(e);
logger.error("Skipping row " + key + " in " + row.getPath(), e);
continue;
}
if (cf == null)
{
cf = thisCF;
}
else
{
cf.addAll(thisCF);
}
}
compactedCf = shouldPurge ? ColumnFamilyStore.removeDeleted(cf, gcBefore) : cf;
if (compactedCf != null && compactedCf.metadata().getDefaultValidator().isCommutative())
{
CounterColumn.removeOldShards(compactedCf, gcBefore);
}
}
public void write(DataOutput out) throws IOException
{
long dataSize = headerBuffer.getLength() + buffer.getLength();
assert dataSize > 0;
out.writeLong(dataSize);
out.write(headerBuffer.getData(), 0, headerBuffer.getLength());
out.write(buffer.getData(), 0, buffer.getLength());
if (compactedCf != null)
{
DataOutputBuffer buffer = new DataOutputBuffer();
DataOutputBuffer headerBuffer = new DataOutputBuffer();
ColumnIndexer.serialize(compactedCf, headerBuffer);
ColumnFamily.serializer().serializeForSSTable(compactedCf, buffer);
out.writeLong(headerBuffer.getLength() + buffer.getLength());
out.write(headerBuffer.getData(), 0, headerBuffer.getLength());
out.write(buffer.getData(), 0, buffer.getLength());
}
}
public void update(MessageDigest digest)
{
digest.update(buffer.getData(), 0, buffer.getLength());
if (compactedCf != null)
{
DataOutputBuffer buffer = new DataOutputBuffer();
try
{
ColumnFamily.serializer().serializeCFInfo(compactedCf, buffer);
buffer.writeInt(compactedCf.getColumnCount());
digest.update(buffer.getData(), 0, buffer.getLength());
}
catch (IOException e)
{
throw new RuntimeException(e);
}
compactedCf.updateDigest(digest);
}
}
public boolean isEmpty()
{
return buffer.getLength() == 0;
return compactedCf == null || compactedCf.getColumnCount() == 0;
}
public int columnCount()
{
return columnCount;
return compactedCf == null ? 0 : compactedCf.getColumnCount();
}
}

View File

@ -173,7 +173,7 @@ public class SSTableIdentityIterator implements Comparable<SSTableIdentityIterat
{
file.seek(columnPosition - 4); // seek to before column count int
ColumnFamily cf = columnFamily.cloneMeShallow();
ColumnFamily.serializer().deserializeColumns(file, cf, false);
ColumnFamily.serializer().deserializeColumns(file, cf, false, false);
return cf;
}

View File

@ -255,8 +255,8 @@ public class SSTableWriter extends SSTable
cfs = Table.open(desc.ksname).getColumnFamilyStore(desc.cfname);
try
{
if (OperationType.AES == type && cfs.metadata.getDefaultValidator().isCommutative())
indexer = new AESCommutativeRowIndexer(desc, cfs.metadata);
if (cfs.metadata.getDefaultValidator().isCommutative())
indexer = new CommutativeRowIndexer(desc, cfs.metadata);
else
indexer = new RowIndexer(desc, cfs.metadata);
}
@ -395,9 +395,20 @@ public class SSTableWriter extends SSTable
}
}
static class AESCommutativeRowIndexer extends RowIndexer
/*
* When a sstable for a counter column family is streamed, we must ensure
* that on the receiving node all counter column goes through the
* deserialization from remote code path (i.e, it must be cleared from its
* delta) to maintain the invariant that on a given node, only increments
* that the node originated are delta (and copy of those must not be delta).
*
* Since after streaming row indexation goes through every streamed
* sstable, we use this opportunity to ensure this property. This is the
* goal of this specific CommutativeRowIndexer.
*/
static class CommutativeRowIndexer extends RowIndexer
{
AESCommutativeRowIndexer(Descriptor desc, CFMetaData metadata) throws IOException
CommutativeRowIndexer(Descriptor desc, CFMetaData metadata) throws IOException
{
super(desc, new BufferedRandomAccessFile(new File(desc.filenameFor(SSTable.COMPONENT_DATA)), "rw", 8 * 1024 * 1024, true), metadata);
}
@ -427,21 +438,17 @@ public class SSTableWriter extends SSTable
// deserialize CF
ColumnFamily cf = ColumnFamily.create(desc.ksname, desc.cfname);
ColumnFamily.serializer().deserializeFromSSTableNoColumns(cf, dfile);
ColumnFamily.serializer().deserializeColumns(dfile, cf, false);
// The data is coming from another host
ColumnFamily.serializer().deserializeColumns(dfile, cf, false, true);
rowSizes.add(dataSize);
columnCounts.add(cf.getEstimatedColumnCount());
// remove source node from CF's commutative columns
((AbstractCommutativeType)cf.metadata().getDefaultValidator()).cleanContext(cf, FBUtilities.getLocalAddress());
readRowPosition = dfile.getFilePointer();
// update index writer
key = SSTableReader.decodeKey(StorageService.getPartitioner(), desc, diskKey);
iwriter.afterAppend(key, writeRowPosition);
// write key
dfile.seek(writeRowPosition);
ByteBufferUtil.writeWithShortLength(diskKey, dfile);

View File

@ -39,19 +39,21 @@ import org.apache.cassandra.db.IColumn;
*/
public class ColumnSortedMap implements SortedMap<ByteBuffer, IColumn>
{
private ColumnSerializer serializer;
private DataInput dis;
private Comparator<ByteBuffer> comparator;
private int length;
private ColumnFamilyStore interner;
private final ColumnSerializer serializer;
private final DataInput dis;
private final Comparator<ByteBuffer> comparator;
private final int length;
private final ColumnFamilyStore interner;
private final boolean fromRemote;
public ColumnSortedMap(Comparator<ByteBuffer> comparator, ColumnSerializer serializer, DataInput dis, ColumnFamilyStore interner, int length)
public ColumnSortedMap(Comparator<ByteBuffer> comparator, ColumnSerializer serializer, DataInput dis, ColumnFamilyStore interner, int length, boolean fromRemote)
{
this.comparator = comparator;
this.serializer = serializer;
this.interner = interner;
this.dis = dis;
this.length = length;
this.fromRemote = fromRemote;
}
public int size()
@ -141,23 +143,25 @@ public class ColumnSortedMap implements SortedMap<ByteBuffer, IColumn>
public Set<Map.Entry<ByteBuffer, IColumn>> entrySet()
{
return new ColumnSet(serializer, dis, interner, length);
return new ColumnSet(serializer, dis, interner, length, fromRemote);
}
}
class ColumnSet implements Set<Map.Entry<ByteBuffer, IColumn>>
{
private ColumnSerializer serializer;
private DataInput dis;
private int length;
private ColumnFamilyStore interner;
private final ColumnSerializer serializer;
private final DataInput dis;
private final int length;
private final ColumnFamilyStore interner;
private boolean fromRemote;
public ColumnSet(ColumnSerializer serializer, DataInput dis, ColumnFamilyStore interner, int length)
public ColumnSet(ColumnSerializer serializer, DataInput dis, ColumnFamilyStore interner, int length, boolean fromRemote)
{
this.serializer = serializer;
this.dis = dis;
this.interner = interner;
this.length = length;
this.fromRemote = fromRemote;
}
public int size()
@ -177,7 +181,7 @@ class ColumnSet implements Set<Map.Entry<ByteBuffer, IColumn>>
public Iterator<Entry<ByteBuffer, IColumn>> iterator()
{
return new ColumnIterator(serializer, dis, interner, length);
return new ColumnIterator(serializer, dis, interner, length, fromRemote);
}
public Object[] toArray()
@ -227,18 +231,20 @@ class ColumnSet implements Set<Map.Entry<ByteBuffer, IColumn>>
class ColumnIterator implements Iterator<Map.Entry<ByteBuffer, IColumn>>
{
private ColumnSerializer serializer;
private DataInput dis;
private int length;
private final ColumnSerializer serializer;
private final DataInput dis;
private final int length;
private final boolean fromRemote;
private int count = 0;
private ColumnFamilyStore interner;
public ColumnIterator(ColumnSerializer serializer, DataInput dis, ColumnFamilyStore interner, int length)
public ColumnIterator(ColumnSerializer serializer, DataInput dis, ColumnFamilyStore interner, int length, boolean fromRemote)
{
this.dis = dis;
this.serializer = serializer;
this.interner = interner;
this.length = length;
this.fromRemote = fromRemote;
}
private IColumn deserializeNext()
@ -246,7 +252,7 @@ class ColumnIterator implements Iterator<Map.Entry<ByteBuffer, IColumn>>
try
{
count++;
return serializer.deserialize(dis, interner);
return serializer.deserialize(dis, interner, fromRemote);
}
catch (IOException e)
{

View File

@ -1306,9 +1306,10 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
if (tableName.equals("system"))
throw new RuntimeException("Cleanup of the system table is neither necessary nor wise");
NodeId.OneShotRenewer nodeIdRenewer = new NodeId.OneShotRenewer();
for (ColumnFamilyStore cfStore : getValidColumnFamilies(tableName, columnFamilies))
{
cfStore.forceCleanup();
cfStore.forceCleanup(nodeIdRenewer);
}
}

View File

@ -945,6 +945,7 @@ public class CassandraServer implements Cassandra.Iface
cf_def.isSetMemtable_flush_after_mins() ? cf_def.memtable_flush_after_mins : CFMetaData.DEFAULT_MEMTABLE_LIFETIME_IN_MINS,
cf_def.isSetMemtable_throughput_in_mb() ? cf_def.memtable_throughput_in_mb : CFMetaData.DEFAULT_MEMTABLE_THROUGHPUT_IN_MB,
cf_def.isSetMemtable_operations_in_millions() ? cf_def.memtable_operations_in_millions : CFMetaData.DEFAULT_MEMTABLE_OPERATIONS_IN_MILLIONS,
cf_def.isSetMerge_shards_chance() ? cf_def.merge_shards_chance : CFMetaData.DEFAULT_MERGE_SHARDS_CHANCE,
ColumnDefinition.fromColumnDef(cf_def.column_metadata));
}

View File

@ -0,0 +1,223 @@
/**
* 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.utils;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.CopyOnWriteArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.db.CounterColumn;
import org.apache.cassandra.db.SystemTable;
public class NodeId implements Comparable<NodeId>
{
private static final Logger logger = LoggerFactory.getLogger(NodeId.class);
public static final int LENGTH = 16; // we assume a fixed length size for all NodeIds
private static final LocalNodeIdHistory localIds = new LocalNodeIdHistory();
private ByteBuffer id;
public static NodeId getLocalId()
{
return localIds.current.get();
}
/**
* Renew the local node id.
* To use only when this strictly necessary, as using this will make all
* counter context grow with time.
*/
public static synchronized void renewLocalId()
{
localIds.renewCurrent();
}
/**
* Return the list of old local node id of this node.
* It is guaranteed that the returned list is sorted by growing node id
* (and hence the first item will be the oldest node id for this host)
*/
public static List<NodeIdRecord> getOldLocalNodeIds()
{
return localIds.olds;
}
/**
* Function for test purposes, do not use otherwise.
* Pack an int in a valid NodeId so that the resulting ids respects the
* numerical ordering. Used for creating handcrafted but easy to
* understand contexts in unit tests (see CounterContextTest).
*/
public static NodeId fromInt(int n)
{
long lowBits = 0xC000000000000000L | n;
return new NodeId(ByteBuffer.allocate(16).putLong(0, 0).putLong(8, lowBits));
}
/*
* For performance reasons, this function interns the provided ByteBuffer.
*/
public static NodeId wrap(ByteBuffer id)
{
return new NodeId(id);
}
public static NodeId wrap(ByteBuffer bb, int offset)
{
ByteBuffer dup = bb.duplicate();
dup.position(offset);
dup.limit(dup.position() + LENGTH);
return wrap(dup);
}
private NodeId(ByteBuffer id)
{
if (id.remaining() != LENGTH)
throw new IllegalArgumentException("A NodeId representation is exactly " + LENGTH + " bytes");
this.id = id;
}
public static NodeId generate()
{
return new NodeId(ByteBuffer.wrap(UUIDGen.decompose(UUIDGen.makeType1UUIDFromHost(FBUtilities.getLocalAddress()))));
}
/*
* For performance reasons, this function returns a reference to the internal ByteBuffer. Clients not modify the
* result of this function.
*/
public ByteBuffer bytes()
{
return id;
}
public boolean isLocalId()
{
return equals(getLocalId());
}
public int compareTo(NodeId o)
{
return ByteBufferUtil.compareSubArrays(id, id.position(), o.id, o.id.position(), NodeId.LENGTH);
}
@Override
public String toString()
{
return UUIDGen.getUUID(id).toString();
}
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
NodeId otherId = (NodeId)o;
return id.equals(otherId.id);
}
@Override
public int hashCode()
{
return id.hashCode();
}
public static class OneShotRenewer
{
private boolean renewed;
private final NodeId initialId;
public OneShotRenewer()
{
renewed = false;
initialId = getLocalId();
}
public void maybeRenew(CounterColumn column)
{
if (!renewed && column.hasNodeId(initialId))
{
renewLocalId();
renewed = true;
}
}
}
private static class LocalNodeIdHistory
{
private final AtomicReference<NodeId> current;
private final List<NodeIdRecord> olds;
LocalNodeIdHistory()
{
NodeId id = SystemTable.getCurrentLocalNodeId();
if (id == null)
{
// no recorded local node id, generating a new one and saving it
id = generate();
logger.info("No saved local node id, using newly generated: {}", id);
SystemTable.writeCurrentLocalNodeId(null, id);
current = new AtomicReference<NodeId>(id);
olds = new CopyOnWriteArrayList();
}
else
{
logger.info("Saved local node id: {}", id);
current = new AtomicReference<NodeId>(id);
olds = new CopyOnWriteArrayList(SystemTable.getOldLocalNodeIds());
}
}
synchronized void renewCurrent()
{
NodeId newNodeId = generate();
NodeId old = current.get();
SystemTable.writeCurrentLocalNodeId(old, newNodeId);
current.set(newNodeId);
olds.add(new NodeIdRecord(old));
}
}
public static class NodeIdRecord
{
public final NodeId id;
public final long timestamp;
public NodeIdRecord(NodeId id)
{
this(id, System.currentTimeMillis());
}
public NodeIdRecord(NodeId id, long timestamp)
{
this.id = id;
this.timestamp = timestamp;
}
}
}

View File

@ -1,3 +1,3 @@
{
"726f7741": [["636f6c4141", "7f0000010000000000000001000000000000002a", 1294532915068, "c", 0]]
"726f7741": [["636f6c4141", "000100008c619170467411e00000fe8ebeead9ee0000000000000001000000000000002a", 1294532915068, "c", 0]]
}

View File

@ -41,6 +41,7 @@ import org.apache.cassandra.gms.ApplicationState;
import org.apache.cassandra.gms.VersionedValue;
import org.apache.cassandra.locator.AbstractReplicationStrategy;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.NodeId;
import static com.google.common.base.Charsets.UTF_8;
@ -149,6 +150,11 @@ public class Util
return result;
}
public static boolean equalsNodeId(NodeId n, ByteBuffer context, int offset)
{
return NodeId.wrap(context, context.position() + offset).equals(n);
}
public static ColumnFamily cloneAndRemoveDeleted(ColumnFamily cf, int gcBefore)
{
return ColumnFamilyStore.removeDeleted(cf.cloneMe(), gcBefore);

View File

@ -44,6 +44,7 @@ import org.apache.cassandra.thrift.IndexClause;
import org.apache.cassandra.thrift.IndexExpression;
import org.apache.cassandra.thrift.IndexOperator;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.NodeId;
public class CleanupTest extends CleanupHelper
{
@ -75,7 +76,7 @@ public class CleanupTest extends CleanupHelper
assertEquals(LOOPS, rows.size());
// with one token in the ring, owned by the local node, cleanup should be a no-op
CompactionManager.instance.performCleanup(cfs);
CompactionManager.instance.performCleanup(cfs, new NodeId.OneShotRenewer());
// check data is still there
rows = cfs.getRangeSlice(null, Util.range("", ""), 1000, new IdentityQueryFilter());
@ -113,7 +114,7 @@ public class CleanupTest extends CleanupHelper
tmd.clearUnsafe();
assert StorageService.instance.getLocalRanges(TABLE1).isEmpty();
CompactionManager.instance.performCleanup(cfs);
CompactionManager.instance.performCleanup(cfs, new NodeId.OneShotRenewer());
// row data should be gone
rows = cfs.getRangeSlice(null, Util.range("", ""), 1000, new IdentityQueryFilter());

View File

@ -18,6 +18,8 @@
*/
package org.apache.cassandra.db;
import java.security.MessageDigest;
import static org.junit.Assert.*;
import java.io.ByteArrayInputStream;
@ -35,10 +37,12 @@ import org.junit.Test;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.Util;
import org.apache.cassandra.db.context.CounterContext;
import static org.apache.cassandra.db.context.CounterContext.ContextState;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.io.util.DataOutputBuffer;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.NodeId;
public class CounterColumnTest extends SchemaLoader
{
@ -52,7 +56,7 @@ public class CounterColumnTest extends SchemaLoader
static
{
idLength = 4; // size of int
idLength = NodeId.LENGTH;
clockLength = 8; // size of long
countLength = 8; // size of long
@ -62,18 +66,16 @@ public class CounterColumnTest extends SchemaLoader
@Test
public void testCreate() throws UnknownHostException
{
AbstractCommutativeType type = CounterColumnType.instance;
long delta = 3L;
CounterUpdateColumn cuc = (CounterUpdateColumn)type.createColumn(
ByteBufferUtil.bytes("x"),
ByteBufferUtil.bytes(delta),
1L);
CounterUpdateColumn cuc = new CounterUpdateColumn(ByteBufferUtil.bytes("x"), delta, 1L);
CounterColumn column = cuc.localCopy(Table.open("Keyspace5").getColumnFamilyStore("Counter1"));
assert delta == column.total();
assert Arrays.equals(FBUtilities.getLocalAddress().getAddress(), ArrayUtils.subarray(column.value().array(), 0, idLength));
assert 1L == column.value().getLong(0*stepLength + idLength);
assert delta == column.value().getLong(0*stepLength + idLength + clockLength);
assert 1 == column.value().getShort(0);
assert 0 == column.value().getShort(2);
assert NodeId.wrap(column.value(), 4).isLocalId();
assert 1L == column.value().getLong(4 + 0*stepLength + idLength);
assert delta == column.value().getLong(4 + 0*stepLength + idLength + clockLength);
}
@Test
@ -149,36 +151,36 @@ public class CounterColumnTest extends SchemaLoader
assert ((CounterColumn)reconciled).timestampOfLastDelete() == right.getMarkedForDeleteAt();
// live < live last delete
left = new CounterColumn(ByteBufferUtil.bytes("x"), cc.create(FBUtilities.toByteArray(1), 2L, 3L), 1L, Long.MIN_VALUE);
right = new CounterColumn(ByteBufferUtil.bytes("x"), cc.create(FBUtilities.toByteArray(1), 1L, 1L), 4L, 3L);
left = new CounterColumn(ByteBufferUtil.bytes("x"), cc.create(NodeId.fromInt(1), 2L, 3L, false), 1L, Long.MIN_VALUE);
right = new CounterColumn(ByteBufferUtil.bytes("x"), cc.create(NodeId.fromInt(1), 1L, 1L, false), 4L, 3L);
assert left.reconcile(right) == right;
// live last delete > live
left = new CounterColumn(ByteBufferUtil.bytes("x"), cc.create(FBUtilities.toByteArray(1), 2L, 3L), 6L, 5L);
right = new CounterColumn(ByteBufferUtil.bytes("x"), cc.create(FBUtilities.toByteArray(1), 1L, 1L), 4L, 3L);
left = new CounterColumn(ByteBufferUtil.bytes("x"), cc.create(NodeId.fromInt(1), 2L, 3L, false), 6L, 5L);
right = new CounterColumn(ByteBufferUtil.bytes("x"), cc.create(NodeId.fromInt(1), 1L, 1L, false), 4L, 3L);
assert left.reconcile(right) == left;
// live + live
left = new CounterColumn(ByteBufferUtil.bytes("x"), cc.create(FBUtilities.toByteArray(1), 1L, 1L), 4L, Long.MIN_VALUE);
right = new CounterColumn(ByteBufferUtil.bytes("x"), cc.create(FBUtilities.toByteArray(1), 2L, 3L), 1L, Long.MIN_VALUE);
left = new CounterColumn(ByteBufferUtil.bytes("x"), cc.create(NodeId.fromInt(1), 1L, 1L, false), 4L, Long.MIN_VALUE);
right = new CounterColumn(ByteBufferUtil.bytes("x"), cc.create(NodeId.fromInt(1), 2L, 3L, false), 1L, Long.MIN_VALUE);
reconciled = left.reconcile(right);
assert reconciled.name().equals(left.name());
assert ((CounterColumn)reconciled).total() == 3L;
assert reconciled.timestamp() == 4L;
left = reconciled;
right = new CounterColumn(ByteBufferUtil.bytes("x"), cc.create(FBUtilities.toByteArray(2), 1L, 5L), 2L, Long.MIN_VALUE);
left = reconciled;
right = new CounterColumn(ByteBufferUtil.bytes("x"), cc.create(NodeId.fromInt(2), 1L, 5L, false), 2L, Long.MIN_VALUE);
reconciled = left.reconcile(right);
assert reconciled.name().equals(left.name());
assert ((CounterColumn)reconciled).total() == 8L;
assert reconciled.timestamp() == 4L;
left = reconciled;
right = new CounterColumn(ByteBufferUtil.bytes("x"), cc.create(FBUtilities.toByteArray(2), 2L, 2L), 6L, Long.MIN_VALUE);
left = reconciled;
right = new CounterColumn(ByteBufferUtil.bytes("x"), cc.create(NodeId.fromInt(2), 2L, 2L, false), 6L, Long.MIN_VALUE);
reconciled = left.reconcile(right);
assert reconciled.name().equals(left.name());
@ -186,15 +188,16 @@ public class CounterColumnTest extends SchemaLoader
assert reconciled.timestamp() == 6L;
context = reconciled.value();
assert 2 * stepLength == context.remaining();
int hd = 2; // header
assert hd + 2 * stepLength == context.remaining();
assert 1 == context.getInt(0*stepLength);
assert 2L == context.getLong(0*stepLength + idLength);
assert 3L == context.getLong(0*stepLength + idLength + clockLength);
assert Util.equalsNodeId(NodeId.fromInt(1), context, hd + 0*stepLength);
assert 2L == context.getLong(hd + 0*stepLength + idLength);
assert 3L == context.getLong(hd + 0*stepLength + idLength + clockLength);
assert 2 == context.getInt(1*stepLength);
assert 2L == context.getLong(1*stepLength + idLength);
assert 2L == context.getLong(1*stepLength + idLength + clockLength);
assert Util.equalsNodeId(NodeId.fromInt(2), context, hd + 1*stepLength);
assert 2L == context.getLong(hd + 1*stepLength + idLength);
assert 2L == context.getLong(hd + 1*stepLength + idLength + clockLength);
assert ((CounterColumn)reconciled).timestampOfLastDelete() == Long.MIN_VALUE;
}
@ -202,8 +205,8 @@ public class CounterColumnTest extends SchemaLoader
@Test
public void testDiff() throws UnknownHostException
{
ByteBuffer left;
ByteBuffer right;
ContextState left;
ContextState right;
CounterColumn leftCol;
CounterColumn rightCol;
@ -223,91 +226,97 @@ public class CounterColumnTest extends SchemaLoader
assert null == rightCol.diff(leftCol);
// equality: equal nodes, all counts same
left = ByteBuffer.wrap(Util.concatByteArrays(
FBUtilities.toByteArray(3), FBUtilities.toByteArray(3L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(6), FBUtilities.toByteArray(2L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(9), FBUtilities.toByteArray(1L), FBUtilities.toByteArray(0L)
));
//left = cc.update(left, InetAddress.getByAddress(FBUtilities.toByteArray(3)), 0L);
right = ByteBufferUtil.clone(left);
left = ContextState.allocate(3, 0);
left.writeElement(NodeId.fromInt(3), 3L, 0L);
left.writeElement(NodeId.fromInt(6), 2L, 0L);
left.writeElement(NodeId.fromInt(9), 1L, 0L);
right = new ContextState(ByteBufferUtil.clone(left.context), 2);
leftCol = new CounterColumn(ByteBufferUtil.bytes("x"), left, 1L);
rightCol = new CounterColumn(ByteBufferUtil.bytes("x"), right, 1L);
leftCol = new CounterColumn(ByteBufferUtil.bytes("x"), left.context, 1L);
rightCol = new CounterColumn(ByteBufferUtil.bytes("x"), right.context, 1L);
assert null == leftCol.diff(rightCol);
// greater than: left has superset of nodes (counts equal)
left = ByteBuffer.wrap(Util.concatByteArrays(
FBUtilities.toByteArray(3), FBUtilities.toByteArray(3L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(6), FBUtilities.toByteArray(2L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(9), FBUtilities.toByteArray(1L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(12), FBUtilities.toByteArray(0L), FBUtilities.toByteArray(0L)
));
right = ByteBuffer.wrap(Util.concatByteArrays(
FBUtilities.toByteArray(3), FBUtilities.toByteArray(3L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(6), FBUtilities.toByteArray(2L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(9), FBUtilities.toByteArray(1L), FBUtilities.toByteArray(0L)
));
left = ContextState.allocate(4, 0);
left.writeElement(NodeId.fromInt(3), 3L, 0L);
left.writeElement(NodeId.fromInt(6), 2L, 0L);
left.writeElement(NodeId.fromInt(9), 1L, 0L);
left.writeElement(NodeId.fromInt(12), 0L, 0L);
leftCol = new CounterColumn(ByteBufferUtil.bytes("x"), left, 1L);
rightCol = new CounterColumn(ByteBufferUtil.bytes("x"), right, 1L);
right = ContextState.allocate(3, 0);
right.writeElement(NodeId.fromInt(3), 3L, 0L);
right.writeElement(NodeId.fromInt(6), 2L, 0L);
right.writeElement(NodeId.fromInt(9), 1L, 0L);
leftCol = new CounterColumn(ByteBufferUtil.bytes("x"), left.context, 1L);
rightCol = new CounterColumn(ByteBufferUtil.bytes("x"), right.context, 1L);
assert null == leftCol.diff(rightCol);
// less than: right has subset of nodes (counts equal)
assert leftCol == rightCol.diff(leftCol);
// disjoint: right and left have disjoint node sets
left = ByteBuffer.wrap(Util.concatByteArrays(
FBUtilities.toByteArray(3), FBUtilities.toByteArray(1L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(4), FBUtilities.toByteArray(1L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(9), FBUtilities.toByteArray(1L), FBUtilities.toByteArray(0L)
));
right = ByteBuffer.wrap(Util.concatByteArrays(
FBUtilities.toByteArray(3), FBUtilities.toByteArray(1L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(6), FBUtilities.toByteArray(1L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(9), FBUtilities.toByteArray(1L), FBUtilities.toByteArray(0L)
));
left = ContextState.allocate(3, 0);
left.writeElement(NodeId.fromInt(3), 1L, 0L);
left.writeElement(NodeId.fromInt(4), 1L, 0L);
left.writeElement(NodeId.fromInt(9), 1L, 0L);
leftCol = new CounterColumn(ByteBufferUtil.bytes("x"), left, 1L);
rightCol = new CounterColumn(ByteBufferUtil.bytes("x"), right, 1L);
right = ContextState.allocate(3, 0);
right.writeElement(NodeId.fromInt(3), 1L, 0L);
right.writeElement(NodeId.fromInt(6), 1L, 0L);
right.writeElement(NodeId.fromInt(9), 1L, 0L);
leftCol = new CounterColumn(ByteBufferUtil.bytes("x"), left.context, 1L);
rightCol = new CounterColumn(ByteBufferUtil.bytes("x"), right.context, 1L);
assert rightCol == leftCol.diff(rightCol);
assert leftCol == rightCol.diff(leftCol);
}
@Test
public void testCleanNodeCounts() throws UnknownHostException
public void testSerializeDeserialize() throws IOException
{
ByteBuffer context = ByteBuffer.wrap(Util.concatByteArrays(
FBUtilities.toByteArray(1), FBUtilities.toByteArray(1L), FBUtilities.toByteArray(1L),
FBUtilities.toByteArray(2), FBUtilities.toByteArray(2L), FBUtilities.toByteArray(2L),
FBUtilities.toByteArray(4), FBUtilities.toByteArray(3L), FBUtilities.toByteArray(3L),
FBUtilities.toByteArray(8), FBUtilities.toByteArray(4L), FBUtilities.toByteArray(4L)
));
CounterColumn c = new CounterColumn(ByteBufferUtil.bytes("x"), context, 1L);
CounterContext.ContextState state = CounterContext.ContextState.allocate(4, 2);
state.writeElement(NodeId.fromInt(1), 4L, 4L);
state.writeElement(NodeId.fromInt(2), 4L, 4L, true);
state.writeElement(NodeId.fromInt(3), 4L, 4L);
state.writeElement(NodeId.fromInt(4), 4L, 4L, true);
CounterColumn d = c.cleanNodeCounts(InetAddress.getByAddress(FBUtilities.toByteArray(4)));
CounterColumn original = new CounterColumn(ByteBufferUtil.bytes("x"), state.context, 1L);
DataOutputBuffer bufOut = new DataOutputBuffer();
Column.serializer().serialize(original, bufOut);
byte[] serialized = bufOut.getData();
assertEquals(7L, d.total());
ByteArrayInputStream bufIn = new ByteArrayInputStream(serialized, 0, serialized.length);
CounterColumn deserialized = (CounterColumn)Column.serializer().deserialize(new DataInputStream(bufIn));
assert original.equals(deserialized);
bufIn = new ByteArrayInputStream(serialized, 0, serialized.length);
CounterColumn deserializedOnRemote = (CounterColumn)Column.serializer().deserialize(new DataInputStream(bufIn), null, true);
assert deserializedOnRemote.name().equals(original.name());
assert deserializedOnRemote.total() == original.total();
assert deserializedOnRemote.value().equals(cc.clearAllDelta(original.value()));
assert deserializedOnRemote.timestamp() == deserialized.timestamp();
assert deserializedOnRemote.timestampOfLastDelete() == deserialized.timestampOfLastDelete();
}
@Test
public void testSerializeDeserialize() throws IOException
public void testUpdateDigest() throws Exception
{
ColumnFamily cf;
MessageDigest digest1 = MessageDigest.getInstance("md5");
MessageDigest digest2 = MessageDigest.getInstance("md5");
ByteBuffer context = ByteBuffer.wrap(Util.concatByteArrays(
FBUtilities.toByteArray(1), FBUtilities.toByteArray(1L), FBUtilities.toByteArray(1L),
FBUtilities.toByteArray(2), FBUtilities.toByteArray(2L), FBUtilities.toByteArray(2L),
FBUtilities.toByteArray(4), FBUtilities.toByteArray(3L), FBUtilities.toByteArray(3L),
FBUtilities.toByteArray(8), FBUtilities.toByteArray(4L), FBUtilities.toByteArray(4L)
));
CounterColumn original = new CounterColumn(ByteBufferUtil.bytes("x"), context, 1L);
CounterContext.ContextState state = CounterContext.ContextState.allocate(4, 2);
state.writeElement(NodeId.fromInt(1), 4L, 4L);
state.writeElement(NodeId.fromInt(2), 4L, 4L, true);
state.writeElement(NodeId.fromInt(3), 4L, 4L);
state.writeElement(NodeId.fromInt(4), 4L, 4L, true);
DataOutputBuffer bufOut = new DataOutputBuffer();
Column.serializer().serialize(original, bufOut);
CounterColumn original = new CounterColumn(ByteBufferUtil.bytes("x"), state.context, 1L);
CounterColumn cleared = new CounterColumn(ByteBufferUtil.bytes("x"), cc.clearAllDelta(state.context), 1L);
ByteArrayInputStream bufIn = new ByteArrayInputStream(bufOut.getData(), 0, bufOut.getLength());
CounterColumn deserialized = (CounterColumn)Column.serializer().deserialize(new DataInputStream(bufIn));
original.updateDigest(digest1);
cleared.updateDigest(digest2);
assert original.equals(deserialized);
assert Arrays.equals(digest1.digest(), digest2.digest());
}
}

View File

@ -123,6 +123,7 @@ public class DefsTest extends CleanupHelper
500,
500,
500.0,
0,
indexes);
// we'll be adding this one later. make sure it's not already there.
@ -779,6 +780,7 @@ public class DefsTest extends CleanupHelper
CFMetaData.DEFAULT_MEMTABLE_LIFETIME_IN_MINS,
CFMetaData.DEFAULT_MEMTABLE_THROUGHPUT_IN_MB,
CFMetaData.DEFAULT_MEMTABLE_OPERATIONS_IN_MILLIONS,
0,
Collections.<ByteBuffer, ColumnDefinition>emptyMap());
}
}

View File

@ -26,9 +26,11 @@ import static junit.framework.Assert.assertNull;
import static org.apache.cassandra.Util.getBytes;
import static org.apache.cassandra.Util.concatByteArrays;
import org.apache.cassandra.db.context.CounterContext;
import static org.apache.cassandra.db.context.CounterContext.ContextState;
import org.apache.cassandra.db.marshal.LongType;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.NodeId;
public class SuperColumnTest
{
@ -45,62 +47,58 @@ public class SuperColumnTest
@Test
public void testAddColumnIncrementCounter()
{
byte[] context;
ContextState state;
SuperColumn sc = new SuperColumn(ByteBufferUtil.bytes("sc1"), LongType.instance);
context = concatByteArrays(
FBUtilities.toByteArray(1), FBUtilities.toByteArray(7L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(2), FBUtilities.toByteArray(5L), FBUtilities.toByteArray(7L),
FBUtilities.toByteArray(4), FBUtilities.toByteArray(2L), FBUtilities.toByteArray(9L),
FBUtilities.getLocalAddress().getAddress(), FBUtilities.toByteArray(3L), FBUtilities.toByteArray(3L)
);
sc.addColumn(new CounterColumn(getBytes(1), ByteBuffer.wrap(context), 3L, 0L));
context = concatByteArrays(
FBUtilities.toByteArray(2), FBUtilities.toByteArray(3L), FBUtilities.toByteArray(4L),
FBUtilities.toByteArray(4), FBUtilities.toByteArray(4L), FBUtilities.toByteArray(1L),
FBUtilities.toByteArray(8), FBUtilities.toByteArray(9L), FBUtilities.toByteArray(0L),
FBUtilities.getLocalAddress().getAddress(), FBUtilities.toByteArray(9L), FBUtilities.toByteArray(5L)
);
sc.addColumn(new CounterColumn(getBytes(1), ByteBuffer.wrap(context), 10L, 0L));
state = ContextState.allocate(4, 1);
state.writeElement(NodeId.fromInt(1), 7L, 0L);
state.writeElement(NodeId.fromInt(2), 5L, 7L);
state.writeElement(NodeId.fromInt(4), 2L, 9L);
state.writeElement(NodeId.getLocalId(), 3L, 3L, true);
sc.addColumn(new CounterColumn(getBytes(1), state.context, 3L, 0L));
context = concatByteArrays(
FBUtilities.toByteArray(2), FBUtilities.toByteArray(1L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(3), FBUtilities.toByteArray(6L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(7), FBUtilities.toByteArray(3L), FBUtilities.toByteArray(0L)
);
sc.addColumn(new CounterColumn(getBytes(2), ByteBuffer.wrap(context), 9L, 0L));
state = ContextState.allocate(4, 1);
state.writeElement(NodeId.fromInt(2), 3L, 4L);
state.writeElement(NodeId.fromInt(4), 4L, 1L);
state.writeElement(NodeId.fromInt(8), 9L, 0L);
state.writeElement(NodeId.getLocalId(), 9L, 5L, true);
sc.addColumn(new CounterColumn(getBytes(1), state.context, 10L, 0L));
state = ContextState.allocate(3, 0);
state.writeElement(NodeId.fromInt(2), 1L, 0L);
state.writeElement(NodeId.fromInt(3), 6L, 0L);
state.writeElement(NodeId.fromInt(7), 3L, 0L);
sc.addColumn(new CounterColumn(getBytes(2), state.context, 9L, 0L));
assertNotNull(sc.getSubColumn(getBytes(1)));
assertNull(sc.getSubColumn(getBytes(3)));
// column: 1
byte[] c1 = concatByteArrays(
FBUtilities.toByteArray(1), FBUtilities.toByteArray(7L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(2), FBUtilities.toByteArray(5L), FBUtilities.toByteArray(7L),
FBUtilities.toByteArray(4), FBUtilities.toByteArray(4L), FBUtilities.toByteArray(1L),
FBUtilities.toByteArray(8), FBUtilities.toByteArray(9L), FBUtilities.toByteArray(0L),
FBUtilities.getLocalAddress().getAddress(), FBUtilities.toByteArray(12L), FBUtilities.toByteArray(8L)
);
ContextState c1 = ContextState.allocate(5, 1);
c1.writeElement(NodeId.fromInt(1), 7L, 0L);
c1.writeElement(NodeId.fromInt(2), 5L, 7L);
c1.writeElement(NodeId.fromInt(4), 4L, 1L);
c1.writeElement(NodeId.fromInt(8), 9L, 0L);
c1.writeElement(NodeId.getLocalId(), 12L, 8L, true);
assert 0 == ByteBufferUtil.compareSubArrays(
((CounterColumn)sc.getSubColumn(getBytes(1))).value(),
0,
ByteBuffer.wrap(c1),
c1.context,
0,
c1.length);
c1.context.remaining());
// column: 2
byte[] c2 = concatByteArrays(
FBUtilities.toByteArray(2), FBUtilities.toByteArray(1L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(3), FBUtilities.toByteArray(6L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(7), FBUtilities.toByteArray(3L), FBUtilities.toByteArray(0L)
);
ContextState c2 = ContextState.allocate(3, 0);
c2.writeElement(NodeId.fromInt(2), 1L, 0L);
c2.writeElement(NodeId.fromInt(3), 6L, 0L);
c2.writeElement(NodeId.fromInt(7), 3L, 0L);
assert 0 == ByteBufferUtil.compareSubArrays(
((CounterColumn)sc.getSubColumn(getBytes(2))).value(),
0,
ByteBuffer.wrap(c2),
c2.context,
0,
c2.length);
c2.context.remaining());
assertNotNull(sc.getSubColumn(getBytes(1)));
assertNotNull(sc.getSubColumn(getBytes(2)));

View File

@ -33,315 +33,279 @@ import org.junit.Test;
import org.apache.cassandra.Util;
import org.apache.cassandra.db.context.IContext.ContextRelationship;
import org.apache.cassandra.utils.FBUtilities;
import static org.apache.cassandra.db.context.CounterContext.ContextState;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.NodeId;
/**
* Note: these tests assume IPv4 (4 bytes) is used for id.
* if IPv6 (16 bytes) is used, tests will fail (but the code will work).
* however, it might be pragmatic to modify the code to just use
* the IPv4 portion of the IPv6 address-space.
*/
public class CounterContextTest
{
private static final CounterContext cc = new CounterContext();
private static final InetAddress idAddress;
private static final byte[] id;
private static final int idLength;
private static final int clockLength;
private static final int countLength;
private static final int stepLength;
private static final int defaultEntries;
static
{
idAddress = FBUtilities.getLocalAddress();
id = idAddress.getAddress();
idLength = 4; // size of int
idLength = NodeId.LENGTH; // size of int
clockLength = 8; // size of long
countLength = 8; // size of long
stepLength = idLength + clockLength + countLength;
defaultEntries = 10;
}
@Test
public void testCreate()
{
ByteBuffer context = cc.create(4);
assert context.remaining() == stepLength;
assert context.remaining() == stepLength + 4;
}
@Test
public void testDiff()
{
ByteBuffer left = ByteBuffer.allocate(3 * stepLength);
ByteBuffer right;
ContextState left = ContextState.allocate(3, 0);
ContextState right;
// equality: equal nodes, all counts same
cc.writeElementAtOffset(left, 0 * stepLength, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtOffset(left, 1 * stepLength, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtOffset(left, 2 * stepLength, FBUtilities.toByteArray(9), 1L, 0L);
right = ByteBufferUtil.clone(left);
left.writeElement(NodeId.fromInt(3), 3L, 0L);
left.writeElement(NodeId.fromInt(6), 2L, 0L);
left.writeElement(NodeId.fromInt(9), 1L, 0L);
right = new ContextState(ByteBufferUtil.clone(left.context), left.headerLength);
assert ContextRelationship.EQUAL ==
cc.diff(left, right);
cc.diff(left.context, right.context);
// greater than: left has superset of nodes (counts equal)
left = ByteBuffer.allocate(4 * stepLength);
cc.writeElementAtOffset(left, 0 * stepLength, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtOffset(left, 1 * stepLength, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtOffset(left, 2 * stepLength, FBUtilities.toByteArray(9), 1L, 0L);
cc.writeElementAtOffset(left, 3 * stepLength, FBUtilities.toByteArray(12), 0L, 0L);
left = ContextState.allocate(4, 0);
left.writeElement(NodeId.fromInt(3), 3L, 0L);
left.writeElement(NodeId.fromInt(6), 2L, 0L);
left.writeElement(NodeId.fromInt(9), 1L, 0L);
left.writeElement(NodeId.fromInt(12), 0L, 0L);
right = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(right, 0 * stepLength, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtOffset(right, 1 * stepLength, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtOffset(right, 2 * stepLength, FBUtilities.toByteArray(9), 1L, 0L);
right = ContextState.allocate(3, 0);
right.writeElement(NodeId.fromInt(3), 3L, 0L);
right.writeElement(NodeId.fromInt(6), 2L, 0L);
right.writeElement(NodeId.fromInt(9), 1L, 0L);
assert ContextRelationship.GREATER_THAN ==
cc.diff(left, right);
cc.diff(left.context, right.context);
// less than: left has subset of nodes (counts equal)
left = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(left, 0 * stepLength, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtOffset(left, 1 * stepLength, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtOffset(left, 2 * stepLength, FBUtilities.toByteArray(9), 1L, 0L);
left = ContextState.allocate(3, 0);
left.writeElement(NodeId.fromInt(3), 3L, 0L);
left.writeElement(NodeId.fromInt(6), 2L, 0L);
left.writeElement(NodeId.fromInt(9), 1L, 0L);
right = ByteBuffer.allocate(4 * stepLength);
cc.writeElementAtOffset(right, 0 * stepLength, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtOffset(right, 1 * stepLength, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtOffset(right, 2 * stepLength, FBUtilities.toByteArray(9), 1L, 0L);
cc.writeElementAtOffset(right, 3 * stepLength, FBUtilities.toByteArray(12), 0L, 0L);
right = ContextState.allocate(4, 0);
right.writeElement(NodeId.fromInt(3), 3L, 0L);
right.writeElement(NodeId.fromInt(6), 2L, 0L);
right.writeElement(NodeId.fromInt(9), 1L, 0L);
right.writeElement(NodeId.fromInt(12), 0L, 0L);
assert ContextRelationship.LESS_THAN ==
cc.diff(left, right);
cc.diff(left.context, right.context);
// greater than: equal nodes, but left has higher counts
left = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(left, 0 * stepLength, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtOffset(left, 1 * stepLength, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtOffset(left, 2 * stepLength, FBUtilities.toByteArray(9), 3L, 0L);
left = ContextState.allocate(3, 0);
left.writeElement(NodeId.fromInt(3), 3L, 0L);
left.writeElement(NodeId.fromInt(6), 2L, 0L);
left.writeElement(NodeId.fromInt(9), 3L, 0L);
right = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(right, 0 * stepLength, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtOffset(right, 1 * stepLength, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtOffset(right, 2 * stepLength, FBUtilities.toByteArray(9), 1L, 0L);
right = ContextState.allocate(3, 0);
right.writeElement(NodeId.fromInt(3), 3L, 0L);
right.writeElement(NodeId.fromInt(6), 2L, 0L);
right.writeElement(NodeId.fromInt(9), 1L, 0L);
assert ContextRelationship.GREATER_THAN ==
cc.diff(left, right);
cc.diff(left.context, right.context);
// less than: equal nodes, but right has higher counts
left = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(left, 0 * stepLength, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtOffset(left, 1 * stepLength, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtOffset(left, 2 * stepLength, FBUtilities.toByteArray(9), 3L, 0L);
left = ContextState.allocate(3, 0);
left.writeElement(NodeId.fromInt(3), 3L, 0L);
left.writeElement(NodeId.fromInt(6), 2L, 0L);
left.writeElement(NodeId.fromInt(9), 3L, 0L);
right = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(right, 0 * stepLength, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtOffset(right, 1 * stepLength, FBUtilities.toByteArray(6), 9L, 0L);
cc.writeElementAtOffset(right, 2 * stepLength, FBUtilities.toByteArray(9), 3L, 0L);
right = ContextState.allocate(3, 0);
right.writeElement(NodeId.fromInt(3), 3L, 0L);
right.writeElement(NodeId.fromInt(6), 9L, 0L);
right.writeElement(NodeId.fromInt(9), 3L, 0L);
assert ContextRelationship.LESS_THAN ==
cc.diff(left, right);
cc.diff(left.context, right.context);
// disjoint: right and left have disjoint node sets
left = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(left, 0 * stepLength, FBUtilities.toByteArray(3), 1L, 0L);
cc.writeElementAtOffset(left, 1 * stepLength, FBUtilities.toByteArray(4), 1L, 0L);
cc.writeElementAtOffset(left, 2 * stepLength, FBUtilities.toByteArray(9), 1L, 0L);
left = ContextState.allocate(3, 0);
left.writeElement(NodeId.fromInt(3), 1L, 0L);
left.writeElement(NodeId.fromInt(4), 1L, 0L);
left.writeElement(NodeId.fromInt(9), 1L, 0L);
right = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(right, 0 * stepLength, FBUtilities.toByteArray(3), 1L, 0L);
cc.writeElementAtOffset(right, 1 * stepLength, FBUtilities.toByteArray(6), 1L, 0L);
cc.writeElementAtOffset(right, 2 * stepLength, FBUtilities.toByteArray(9), 1L, 0L);
right = ContextState.allocate(3, 0);
right.writeElement(NodeId.fromInt(3), 1L, 0L);
right.writeElement(NodeId.fromInt(6), 1L, 0L);
right.writeElement(NodeId.fromInt(9), 1L, 0L);
assert ContextRelationship.DISJOINT ==
cc.diff(left, right);
cc.diff(left.context, right.context);
left = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(left, 0 * stepLength, FBUtilities.toByteArray(3), 1L, 0L);
cc.writeElementAtOffset(left, 1 * stepLength, FBUtilities.toByteArray(4), 1L, 0L);
cc.writeElementAtOffset(left, 2 * stepLength, FBUtilities.toByteArray(9), 1L, 0L);
left = ContextState.allocate(3, 0);
left.writeElement(NodeId.fromInt(3), 1L, 0L);
left.writeElement(NodeId.fromInt(4), 1L, 0L);
left.writeElement(NodeId.fromInt(9), 1L, 0L);
right = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(right, 0 * stepLength, FBUtilities.toByteArray(2), 1L, 0L);
cc.writeElementAtOffset(right, 1 * stepLength, FBUtilities.toByteArray(6), 1L, 0L);
cc.writeElementAtOffset(right, 2 * stepLength, FBUtilities.toByteArray(12), 1L, 0L);
right = ContextState.allocate(3, 0);
right.writeElement(NodeId.fromInt(2), 1L, 0L);
right.writeElement(NodeId.fromInt(6), 1L, 0L);
right.writeElement(NodeId.fromInt(12), 1L, 0L);
assert ContextRelationship.DISJOINT ==
cc.diff(left, right);
cc.diff(left.context, right.context);
// disjoint: equal nodes, but right and left have higher counts in differing nodes
left = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(left, 0 * stepLength, FBUtilities.toByteArray(3), 1L, 0L);
cc.writeElementAtOffset(left, 1 * stepLength, FBUtilities.toByteArray(6), 3L, 0L);
cc.writeElementAtOffset(left, 2 * stepLength, FBUtilities.toByteArray(9), 1L, 0L);
left = ContextState.allocate(3, 0);
left.writeElement(NodeId.fromInt(3), 1L, 0L);
left.writeElement(NodeId.fromInt(6), 3L, 0L);
left.writeElement(NodeId.fromInt(9), 1L, 0L);
right = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(right, 0 * stepLength, FBUtilities.toByteArray(3), 1L, 0L);
cc.writeElementAtOffset(right, 1 * stepLength, FBUtilities.toByteArray(6), 1L, 0L);
cc.writeElementAtOffset(right, 2 * stepLength, FBUtilities.toByteArray(9), 5L, 0L);
right = ContextState.allocate(3, 0);
right.writeElement(NodeId.fromInt(3), 1L, 0L);
right.writeElement(NodeId.fromInt(6), 1L, 0L);
right.writeElement(NodeId.fromInt(9), 5L, 0L);
assert ContextRelationship.DISJOINT ==
cc.diff(left, right);
cc.diff(left.context, right.context);
left = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(left, 0 * stepLength, FBUtilities.toByteArray(3), 2L, 0L);
cc.writeElementAtOffset(left, 1 * stepLength, FBUtilities.toByteArray(6), 3L, 0L);
cc.writeElementAtOffset(left, 2 * stepLength, FBUtilities.toByteArray(9), 1L, 0L);
left = ContextState.allocate(3, 0);
left.writeElement(NodeId.fromInt(3), 2L, 0L);
left.writeElement(NodeId.fromInt(6), 3L, 0L);
left.writeElement(NodeId.fromInt(9), 1L, 0L);
right = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(right, 0 * stepLength, FBUtilities.toByteArray(3), 1L, 0L);
cc.writeElementAtOffset(right, 1 * stepLength, FBUtilities.toByteArray(6), 9L, 0L);
cc.writeElementAtOffset(right, 2 * stepLength, FBUtilities.toByteArray(9), 5L, 0L);
right = ContextState.allocate(3, 0);
right.writeElement(NodeId.fromInt(3), 1L, 0L);
right.writeElement(NodeId.fromInt(6), 9L, 0L);
right.writeElement(NodeId.fromInt(9), 5L, 0L);
assert ContextRelationship.DISJOINT ==
cc.diff(left, right);
cc.diff(left.context, right.context);
// disjoint: left has more nodes, but lower counts
left = ByteBuffer.allocate(4 * stepLength);
cc.writeElementAtOffset(left, 0 * stepLength, FBUtilities.toByteArray(3), 2L, 0L);
cc.writeElementAtOffset(left, 1 * stepLength, FBUtilities.toByteArray(6), 3L, 0L);
cc.writeElementAtOffset(left, 2 * stepLength, FBUtilities.toByteArray(9), 1L, 0L);
cc.writeElementAtOffset(left, 3 * stepLength, FBUtilities.toByteArray(12), 1L, 0L);
left = ContextState.allocate(4, 0);
left.writeElement(NodeId.fromInt(3), 2L, 0L);
left.writeElement(NodeId.fromInt(6), 3L, 0L);
left.writeElement(NodeId.fromInt(9), 1L, 0L);
left.writeElement(NodeId.fromInt(12), 1L, 0L);
right = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(right, 0 * stepLength, FBUtilities.toByteArray(3), 4L, 0L);
cc.writeElementAtOffset(right, 1 * stepLength, FBUtilities.toByteArray(6), 9L, 0L);
cc.writeElementAtOffset(right, 2 * stepLength, FBUtilities.toByteArray(9), 5L, 0L);
right = ContextState.allocate(3, 0);
right.writeElement(NodeId.fromInt(3), 4L, 0L);
right.writeElement(NodeId.fromInt(6), 9L, 0L);
right.writeElement(NodeId.fromInt(9), 5L, 0L);
assert ContextRelationship.DISJOINT ==
cc.diff(left, right);
cc.diff(left.context, right.context);
// disjoint: left has less nodes, but higher counts
left = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(left, 0 * stepLength, FBUtilities.toByteArray(3), 5L, 0L);
cc.writeElementAtOffset(left, 1 * stepLength, FBUtilities.toByteArray(6), 3L, 0L);
cc.writeElementAtOffset(left, 2 * stepLength, FBUtilities.toByteArray(9), 2L, 0L);
left = ContextState.allocate(3, 0);
left.writeElement(NodeId.fromInt(3), 5L, 0L);
left.writeElement(NodeId.fromInt(6), 3L, 0L);
left.writeElement(NodeId.fromInt(9), 2L, 0L);
right = ByteBuffer.allocate(4 * stepLength);
cc.writeElementAtOffset(right, 0 * stepLength, FBUtilities.toByteArray(3), 4L, 0L);
cc.writeElementAtOffset(right, 1 * stepLength, FBUtilities.toByteArray(6), 3L, 0L);
cc.writeElementAtOffset(right, 2 * stepLength, FBUtilities.toByteArray(9), 2L, 0L);
cc.writeElementAtOffset(right, 3 * stepLength, FBUtilities.toByteArray(12), 1L, 0L);
right = ContextState.allocate(4, 0);
right.writeElement(NodeId.fromInt(3), 4L, 0L);
right.writeElement(NodeId.fromInt(6), 3L, 0L);
right.writeElement(NodeId.fromInt(9), 2L, 0L);
right.writeElement(NodeId.fromInt(12), 1L, 0L);
assert ContextRelationship.DISJOINT ==
cc.diff(left, right);
cc.diff(left.context, right.context);
// disjoint: mixed nodes and counts
left = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(left, 0 * stepLength, FBUtilities.toByteArray(3), 5L, 0L);
cc.writeElementAtOffset(left, 1 * stepLength, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtOffset(left, 2 * stepLength, FBUtilities.toByteArray(9), 2L, 0L);
left = ContextState.allocate(3, 0);
left.writeElement(NodeId.fromInt(3), 5L, 0L);
left.writeElement(NodeId.fromInt(6), 2L, 0L);
left.writeElement(NodeId.fromInt(9), 2L, 0L);
right = ByteBuffer.allocate(4 * stepLength);
cc.writeElementAtOffset(right, 0 * stepLength, FBUtilities.toByteArray(3), 4L, 0L);
cc.writeElementAtOffset(right, 1 * stepLength, FBUtilities.toByteArray(6), 3L, 0L);
cc.writeElementAtOffset(right, 2 * stepLength, FBUtilities.toByteArray(9), 2L, 0L);
cc.writeElementAtOffset(right, 3 * stepLength, FBUtilities.toByteArray(12), 1L, 0L);
right = ContextState.allocate(4, 0);
right.writeElement(NodeId.fromInt(3), 4L, 0L);
right.writeElement(NodeId.fromInt(6), 3L, 0L);
right.writeElement(NodeId.fromInt(9), 2L, 0L);
right.writeElement(NodeId.fromInt(12), 1L, 0L);
assert ContextRelationship.DISJOINT ==
cc.diff(left, right);
cc.diff(left.context, right.context);
left = ByteBuffer.allocate(4 * stepLength);
cc.writeElementAtOffset(left, 0 * stepLength, FBUtilities.toByteArray(3), 5L, 0L);
cc.writeElementAtOffset(left, 1 * stepLength, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtOffset(left, 2 * stepLength, FBUtilities.toByteArray(7), 2L, 0L);
cc.writeElementAtOffset(left, 3 * stepLength, FBUtilities.toByteArray(9), 2L, 0L);
left = ContextState.allocate(4, 0);
left.writeElement(NodeId.fromInt(3), 5L, 0L);
left.writeElement(NodeId.fromInt(6), 2L, 0L);
left.writeElement(NodeId.fromInt(7), 2L, 0L);
left.writeElement(NodeId.fromInt(9), 2L, 0L);
right = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(right, 0 * stepLength, FBUtilities.toByteArray(3), 4L, 0L);
cc.writeElementAtOffset(right, 1 * stepLength, FBUtilities.toByteArray(6), 3L, 0L);
cc.writeElementAtOffset(right, 2 * stepLength, FBUtilities.toByteArray(9), 2L, 0L);
right = ContextState.allocate(3, 0);
right.writeElement(NodeId.fromInt(3), 4L, 0L);
right.writeElement(NodeId.fromInt(6), 3L, 0L);
right.writeElement(NodeId.fromInt(9), 2L, 0L);
assert ContextRelationship.DISJOINT ==
cc.diff(left, right);
cc.diff(left.context, right.context);
}
@Test
public void testMerge()
{
// note: local counts aggregated; remote counts are reconciled (i.e. take max)
ByteBuffer left = ByteBuffer.allocate(4 * stepLength);
cc.writeElementAtOffset(left, 0 * stepLength, FBUtilities.toByteArray(1), 1L, 1L);
cc.writeElementAtOffset(left, 1 * stepLength, FBUtilities.toByteArray(2), 2L, 2L);
cc.writeElementAtOffset(left, 2 * stepLength, FBUtilities.toByteArray(4), 6L, 3L);
cc.writeElementAtOffset(
left,
3 * stepLength,
FBUtilities.getLocalAddress().getAddress(),
7L,
3L);
ContextState left = ContextState.allocate(4, 1);
left.writeElement(NodeId.fromInt(1), 1L, 1L);
left.writeElement(NodeId.fromInt(2), 2L, 2L);
left.writeElement(NodeId.fromInt(4), 6L, 3L);
left.writeElement(NodeId.getLocalId(), 7L, 3L, true);
ByteBuffer right = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(right, 0 * stepLength, FBUtilities.toByteArray(4), 4L, 4L);
cc.writeElementAtOffset(right, 1 * stepLength, FBUtilities.toByteArray(5), 5L, 5L);
cc.writeElementAtOffset(
right,
2 * stepLength,
FBUtilities.getLocalAddress().getAddress(),
2L,
9L);
ContextState right = ContextState.allocate(3, 1);
right.writeElement(NodeId.fromInt(4), 4L, 4L);
right.writeElement(NodeId.fromInt(5), 5L, 5L);
right.writeElement(NodeId.getLocalId(), 2L, 9L, true);
ByteBuffer merged = cc.merge(left, right);
ByteBuffer merged = cc.merge(left.context, right.context);
int hd = 4;
assertEquals(5 * stepLength, merged.remaining());
assertEquals(hd + 5 * stepLength, merged.remaining());
// local node id's counts are aggregated
assertEquals(0, ByteBufferUtil.compareSubArrays(
ByteBuffer.wrap(FBUtilities.getLocalAddress().getAddress()),
0,
merged,
4*stepLength,
4));
assertEquals( 9L, merged.getLong(4*stepLength + idLength));
assertEquals(12L, merged.getLong(4*stepLength + idLength + clockLength));
assert Util.equalsNodeId(NodeId.getLocalId(), merged, hd + 4*stepLength);
assertEquals( 9L, merged.getLong(hd + 4*stepLength + idLength));
assertEquals(12L, merged.getLong(hd + 4*stepLength + idLength + clockLength));
// remote node id counts are reconciled (i.e. take max)
assertEquals( 4, merged.getInt( 2*stepLength));
assertEquals( 6L, merged.getLong(2*stepLength + idLength));
assertEquals( 3L, merged.getLong(2*stepLength + idLength + clockLength));
assert Util.equalsNodeId(NodeId.fromInt(4), merged, hd + 2*stepLength);
assertEquals( 6L, merged.getLong(hd + 2*stepLength + idLength));
assertEquals( 3L, merged.getLong(hd + 2*stepLength + idLength + clockLength));
assertEquals( 5, merged.getInt( 3*stepLength));
assertEquals( 5L, merged.getLong(3*stepLength + idLength));
assertEquals( 5L, merged.getLong(3*stepLength + idLength + clockLength));
assert Util.equalsNodeId(NodeId.fromInt(5), merged, hd + 3*stepLength);
assertEquals( 5L, merged.getLong(hd + 3*stepLength + idLength));
assertEquals( 5L, merged.getLong(hd + 3*stepLength + idLength + clockLength));
assertEquals( 2, merged.getInt( 1*stepLength));
assertEquals( 2L, merged.getLong(1*stepLength + idLength));
assertEquals( 2L, merged.getLong(1*stepLength + idLength + clockLength));
assert Util.equalsNodeId(NodeId.fromInt(2), merged, hd + 1*stepLength);
assertEquals( 2L, merged.getLong(hd + 1*stepLength + idLength));
assertEquals( 2L, merged.getLong(hd + 1*stepLength + idLength + clockLength));
assertEquals( 1, merged.getInt( 0*stepLength));
assertEquals( 1L, merged.getLong(0*stepLength + idLength));
assertEquals( 1L, merged.getLong(0*stepLength + idLength + clockLength));
assert Util.equalsNodeId(NodeId.fromInt(1), merged, hd + 0*stepLength);
assertEquals( 1L, merged.getLong(hd + 0*stepLength + idLength));
assertEquals( 1L, merged.getLong(hd + 0*stepLength + idLength + clockLength));
}
@Test
public void testTotal()
{
ByteBuffer left = ByteBuffer.allocate(4 * stepLength);
cc.writeElementAtOffset(left, 0 * stepLength, FBUtilities.toByteArray(1), 1L, 1L);
cc.writeElementAtOffset(left, 1 * stepLength, FBUtilities.toByteArray(2), 2L, 2L);
cc.writeElementAtOffset(left, 2 * stepLength, FBUtilities.toByteArray(4), 3L, 3L);
cc.writeElementAtOffset(
left,
3 * stepLength,
FBUtilities.getLocalAddress().getAddress(),
3L,
3L);
ContextState left = ContextState.allocate(4, 1);
left.writeElement(NodeId.fromInt(1), 1L, 1L);
left.writeElement(NodeId.fromInt(2), 2L, 2L);
left.writeElement(NodeId.fromInt(4), 3L, 3L);
left.writeElement(NodeId.getLocalId(), 3L, 3L, true);
ByteBuffer right = ByteBuffer.allocate(3 * stepLength);
cc.writeElementAtOffset(right, 0 * stepLength, FBUtilities.toByteArray(4), 4L, 4L);
cc.writeElementAtOffset(right, 1 * stepLength, FBUtilities.toByteArray(5), 5L, 5L);
cc.writeElementAtOffset(
right,
2 * stepLength,
FBUtilities.getLocalAddress().getAddress(),
9L,
9L);
ContextState right = ContextState.allocate(3, 1);
right.writeElement(NodeId.fromInt(4), 4L, 4L);
right.writeElement(NodeId.fromInt(5), 5L, 5L);
right.writeElement(NodeId.getLocalId(), 9L, 9L, true);
ByteBuffer merged = cc.merge(left, right);
ByteBuffer merged = cc.merge(left.context, right.context);
// 127.0.0.1: 12 (3+9)
// 0.0.0.1: 1
@ -353,30 +317,100 @@ public class CounterContextTest
}
@Test
public void testCleanNodeCounts() throws UnknownHostException
public void testMergeOldShards()
{
ByteBuffer bytes = ByteBuffer.allocate(4 * stepLength);
cc.writeElementAtOffset(bytes, 0 * stepLength, FBUtilities.toByteArray(1), 1L, 1L);
cc.writeElementAtOffset(bytes, 1 * stepLength, FBUtilities.toByteArray(2), 2L, 2L);
cc.writeElementAtOffset(bytes, 2 * stepLength, FBUtilities.toByteArray(4), 3L, 3L);
cc.writeElementAtOffset(bytes, 3 * stepLength, FBUtilities.toByteArray(8), 4L, 4L);
long now = System.currentTimeMillis();
NodeId id1 = NodeId.fromInt(1);
NodeId id3 = NodeId.fromInt(3);
List<NodeId.NodeIdRecord> records = new ArrayList<NodeId.NodeIdRecord>();
records.add(new NodeId.NodeIdRecord(id1, 2L));
records.add(new NodeId.NodeIdRecord(id3, 4L));
assertEquals(4, bytes.getInt( 2*stepLength));
assertEquals(3L, bytes.getLong(2*stepLength + idLength));
// Destination of merge is a delta
ContextState ctx = ContextState.allocate(5, 2);
ctx.writeElement(id1, 1L, 1L);
ctx.writeElement(NodeId.fromInt(2), 2L, 2L);
ctx.writeElement(id3, 3L, 3L, true);
ctx.writeElement(NodeId.fromInt(4), 6L, 3L);
ctx.writeElement(NodeId.fromInt(5), 7L, 3L, true);
bytes = cc.cleanNodeCounts(bytes, InetAddress.getByAddress(FBUtilities.toByteArray(4)));
ByteBuffer merger = cc.computeOldShardMerger(ctx.context, records);
ContextState m = new ContextState(merger);
// node: 0.0.0.4 should be removed
assertEquals(3 * stepLength, bytes.remaining());
assert m.getNodeId().equals(id1);
assert m.getClock() <= -now;
assert m.getCount() == 0;
m.moveToNext();
assert m.getNodeId().equals(id3);
assert m.getClock() == 4L;
assert m.getCount() == 1L;
assert cc.total(ctx.context) == cc.total(cc.merge(ctx.context, merger));
// other nodes should be unaffected
assertEquals(1, bytes.getInt( 0*stepLength));
assertEquals(1L, bytes.getLong(0*stepLength + idLength));
// Source of merge is a delta
ctx = ContextState.allocate(4, 1);
ctx.writeElement(id1, 1L, 1L, true);
ctx.writeElement(NodeId.fromInt(2), 2L, 2L);
ctx.writeElement(id3, 3L, 3L);
ctx.writeElement(NodeId.fromInt(4), 6L, 3L);
assertEquals(2, bytes.getInt( 1*stepLength));
assertEquals(2L, bytes.getLong(1*stepLength + idLength));
merger = cc.computeOldShardMerger(ctx.context, records);
assert cc.total(ctx.context) == cc.total(cc.merge(ctx.context, merger));
// source and destination of merge are deltas
ctx = ContextState.allocate(4, 2);
ctx.writeElement(id1, 1L, 1L, true);
ctx.writeElement(NodeId.fromInt(2), 2L, 2L);
ctx.writeElement(id3, 3L, 3L, true);
ctx.writeElement(NodeId.fromInt(4), 6L, 3L);
merger = cc.computeOldShardMerger(ctx.context, records);
assert cc.total(ctx.context) == cc.total(cc.merge(ctx.context, merger));
// none of source and destination of merge are deltas
ctx = ContextState.allocate(4, 0);
ctx.writeElement(id1, 1L, 1L);
ctx.writeElement(NodeId.fromInt(2), 2L, 2L);
ctx.writeElement(id3, 3L, 3L);
ctx.writeElement(NodeId.fromInt(4), 6L, 3L);
merger = cc.computeOldShardMerger(ctx.context, records);
assert cc.total(ctx.context) == cc.total(cc.merge(ctx.context, merger));
}
@Test
public void testRemoveOldShards()
{
NodeId id1 = NodeId.fromInt(1);
NodeId id3 = NodeId.fromInt(3);
NodeId id6 = NodeId.fromInt(6);
List<NodeId.NodeIdRecord> records = new ArrayList<NodeId.NodeIdRecord>();
records.add(new NodeId.NodeIdRecord(id1, 2L));
records.add(new NodeId.NodeIdRecord(id3, 4L));
records.add(new NodeId.NodeIdRecord(id6, 10L));
ContextState ctx = ContextState.allocate(6, 2);
ctx.writeElement(id1, 1L, 1L);
ctx.writeElement(NodeId.fromInt(2), 2L, 2L);
ctx.writeElement(id3, 3L, 3L, true);
ctx.writeElement(NodeId.fromInt(4), 6L, 3L);
ctx.writeElement(NodeId.fromInt(5), 7L, 3L, true);
ctx.writeElement(id6, 5L, 6L);
ByteBuffer merger = cc.computeOldShardMerger(ctx.context, records);
ByteBuffer merged = cc.merge(ctx.context, merger);
assert cc.total(ctx.context) == cc.total(merged);
ByteBuffer cleaned = cc.removeOldShards(merged, (int)(System.currentTimeMillis() / 1000) + 1);
assert cc.total(ctx.context) == cc.total(cleaned);
assert cleaned.remaining() == ctx.context.remaining() - stepLength;
merger = cc.computeOldShardMerger(cleaned, records);
merged = cc.merge(cleaned, merger);
assert cc.total(ctx.context) == cc.total(merged);
cleaned = cc.removeOldShards(merged, (int)(System.currentTimeMillis() / 1000) + 1);
assert cc.total(ctx.context) == cc.total(cleaned);
assert cleaned.remaining() == ctx.context.remaining() - 2 * stepLength - 2;
assertEquals(8, bytes.getInt( 2*stepLength));
assertEquals(4L, bytes.getLong(2*stepLength + idLength));
}
}

View File

@ -43,16 +43,17 @@ import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.streaming.OperationType;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.NodeId;
import org.junit.Test;
import org.apache.cassandra.utils.ByteBufferUtil;
public class SSTableWriterAESCommutativeTest extends CleanupHelper
public class SSTableWriterCommutativeTest extends CleanupHelper
{
private static final CounterContext cc = new CounterContext();
private static final CounterColumnType ctype = CounterColumnType.instance;
@Test
public void testRecoverAndOpenAESCommutative() throws IOException, ExecutionException, InterruptedException, UnknownHostException
public void testRecoverAndOpenCommutative() throws IOException, ExecutionException, InterruptedException, UnknownHostException
{
String keyspace = "Keyspace1";
String cfname = "Counter1";
@ -63,33 +64,25 @@ public class SSTableWriterAESCommutativeTest extends CleanupHelper
DataOutputBuffer buffer;
ColumnFamily cf = ColumnFamily.create(keyspace, cfname);
byte[] context;
ColumnFamily cfCleaned = ColumnFamily.create(keyspace, cfname);
CounterContext.ContextState state;
// key: k
context = Util.concatByteArrays(
FBUtilities.getLocalAddress().getAddress(),
FBUtilities.toByteArray(9L),
FBUtilities.toByteArray(3L),
FBUtilities.toByteArray(2), FBUtilities.toByteArray(4L), FBUtilities.toByteArray(2L),
FBUtilities.toByteArray(4), FBUtilities.toByteArray(3L), FBUtilities.toByteArray(3L),
FBUtilities.toByteArray(8), FBUtilities.toByteArray(2L), FBUtilities.toByteArray(4L)
);
cf.addColumn(new CounterColumn(
ByteBufferUtil.bytes("x"),
ByteBuffer.wrap(context),
0L));
context = Util.concatByteArrays(
FBUtilities.toByteArray(1), FBUtilities.toByteArray(7L), FBUtilities.toByteArray(12L),
FBUtilities.getLocalAddress().getAddress(),
FBUtilities.toByteArray(5L),
FBUtilities.toByteArray(3L),
FBUtilities.toByteArray(3), FBUtilities.toByteArray(2L), FBUtilities.toByteArray(33L),
FBUtilities.toByteArray(9), FBUtilities.toByteArray(1L), FBUtilities.toByteArray(24L)
);
cf.addColumn(new CounterColumn(
ByteBufferUtil.bytes("y"),
ByteBuffer.wrap(context),
0L));
state = CounterContext.ContextState.allocate(4, 1);
state.writeElement(NodeId.fromInt(2), 9L, 3L, true);
state.writeElement(NodeId.fromInt(4), 4L, 2L);
state.writeElement(NodeId.fromInt(6), 3L, 3L);
state.writeElement(NodeId.fromInt(8), 2L, 4L);
cf.addColumn(new CounterColumn( ByteBufferUtil.bytes("x"), state.context, 0L));
cfCleaned.addColumn(new CounterColumn( ByteBufferUtil.bytes("x"), cc.clearAllDelta(state.context), 0L));
state = CounterContext.ContextState.allocate(4, 1);
state.writeElement(NodeId.fromInt(1), 7L, 12L);
state.writeElement(NodeId.fromInt(2), 5L, 3L, true);
state.writeElement(NodeId.fromInt(3), 2L, 33L);
state.writeElement(NodeId.fromInt(9), 1L, 24L);
cf.addColumn(new CounterColumn( ByteBufferUtil.bytes("y"), state.context, 0L));
cfCleaned.addColumn(new CounterColumn( ByteBufferUtil.bytes("y"), cc.clearAllDelta(state.context), 0L));
buffer = new DataOutputBuffer();
ColumnFamily.serializer().serializeWithIndexes(cf, buffer);
@ -98,38 +91,31 @@ public class SSTableWriterAESCommutativeTest extends CleanupHelper
ByteBuffer.wrap(Arrays.copyOf(buffer.getData(), buffer.getLength()))
);
ctype.cleanContext(cf, FBUtilities.getLocalAddress());
buffer = new DataOutputBuffer();
ColumnFamily.serializer().serializeWithIndexes(cf, buffer);
ColumnFamily.serializer().serializeWithIndexes(cfCleaned, buffer);
cleanedEntries.put(
ByteBufferUtil.bytes("k"),
ByteBuffer.wrap(Arrays.copyOf(buffer.getData(), buffer.getLength()))
);
cf.clear();
cfCleaned.clear();
// key: l
context = Util.concatByteArrays(
FBUtilities.getLocalAddress().getAddress(),
FBUtilities.toByteArray(9L),
FBUtilities.toByteArray(3L),
FBUtilities.toByteArray(2), FBUtilities.toByteArray(4L), FBUtilities.toByteArray(2L),
FBUtilities.toByteArray(4), FBUtilities.toByteArray(3L), FBUtilities.toByteArray(3L),
FBUtilities.toByteArray(8), FBUtilities.toByteArray(2L), FBUtilities.toByteArray(4L)
);
cf.addColumn(new CounterColumn(
ByteBufferUtil.bytes("x"),
ByteBuffer.wrap(context),
0L));
context = Util.concatByteArrays(
FBUtilities.toByteArray(1), FBUtilities.toByteArray(7L), FBUtilities.toByteArray(12L),
FBUtilities.toByteArray(3), FBUtilities.toByteArray(2L), FBUtilities.toByteArray(33L),
FBUtilities.toByteArray(9), FBUtilities.toByteArray(1L), FBUtilities.toByteArray(24L)
);
cf.addColumn(new CounterColumn(
ByteBufferUtil.bytes("y"),
ByteBuffer.wrap(context),
0L));
state = CounterContext.ContextState.allocate(4, 1);
state.writeElement(NodeId.fromInt(2), 9L, 3L, true);
state.writeElement(NodeId.fromInt(4), 4L, 2L);
state.writeElement(NodeId.fromInt(6), 3L, 3L);
state.writeElement(NodeId.fromInt(8), 2L, 4L);
cf.addColumn(new CounterColumn( ByteBufferUtil.bytes("x"), state.context, 0L));
cfCleaned.addColumn(new CounterColumn( ByteBufferUtil.bytes("x"), cc.clearAllDelta(state.context), 0L));
state = CounterContext.ContextState.allocate(3, 0);
state.writeElement(NodeId.fromInt(1), 7L, 12L);
state.writeElement(NodeId.fromInt(3), 2L, 33L);
state.writeElement(NodeId.fromInt(9), 1L, 24L);
cf.addColumn(new CounterColumn( ByteBufferUtil.bytes("y"), state.context, 0L));
cfCleaned.addColumn(new CounterColumn( ByteBufferUtil.bytes("y"), cc.clearAllDelta(state.context), 0L));
buffer = new DataOutputBuffer();
ColumnFamily.serializer().serializeWithIndexes(cf, buffer);
@ -138,15 +124,15 @@ public class SSTableWriterAESCommutativeTest extends CleanupHelper
ByteBuffer.wrap(Arrays.copyOf(buffer.getData(), buffer.getLength()))
);
ctype.cleanContext(cf, FBUtilities.getLocalAddress());
buffer = new DataOutputBuffer();
ColumnFamily.serializer().serializeWithIndexes(cf, buffer);
ColumnFamily.serializer().serializeWithIndexes(cfCleaned, buffer);
cleanedEntries.put(
ByteBufferUtil.bytes("l"),
ByteBuffer.wrap(Arrays.copyOf(buffer.getData(), buffer.getLength()))
);
cf.clear();
cfCleaned.clear();
// write out unmodified CF
SSTableReader orig = SSTableUtils.prepare().ks(keyspace).cf(cfname).generation(0).writeRaw(entries);

View File

@ -137,12 +137,10 @@ public class AntiEntropyServiceTest extends CleanupHelper
validator.prepare(store);
// add a row with the minimum token
validator.add(new PrecompactedRow(new DecoratedKey(min, ByteBufferUtil.bytes("nonsense!")),
new DataOutputBuffer()));
validator.add(new PrecompactedRow(new DecoratedKey(min, ByteBufferUtil.bytes("nonsense!")), null));
// and a row after it
validator.add(new PrecompactedRow(new DecoratedKey(mid, ByteBufferUtil.bytes("inconceivable!")),
new DataOutputBuffer()));
validator.add(new PrecompactedRow(new DecoratedKey(mid, ByteBufferUtil.bytes("inconceivable!")), null));
validator.complete();
// confirm that the tree was validated

View File

@ -151,12 +151,10 @@ public abstract class AntiEntropyServiceTestAbstract extends CleanupHelper
validator.prepare(store);
// add a row with the minimum token
validator.add(new PrecompactedRow(new DecoratedKey(min, ByteBufferUtil.bytes("nonsense!")),
new DataOutputBuffer()));
validator.add(new PrecompactedRow(new DecoratedKey(min, ByteBufferUtil.bytes("nonsense!")), null));
// and a row after it
validator.add(new PrecompactedRow(new DecoratedKey(mid, ByteBufferUtil.bytes("inconceivable!")),
new DataOutputBuffer()));
validator.add(new PrecompactedRow(new DecoratedKey(mid, ByteBufferUtil.bytes("inconceivable!")), null));
validator.complete();
// confirm that the tree was validated