mirror of https://github.com/apache/cassandra
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:
parent
1a5d09c356
commit
ab5a8c77e4
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"726f7741": [["636f6c4141", "7f0000010000000000000001000000000000002a", 1294532915068, "c", 0]]
|
||||
"726f7741": [["636f6c4141", "000100008c619170467411e00000fe8ebeead9ee0000000000000001000000000000002a", 1294532915068, "c", 0]]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue