Adds support for columns that act as incr/decr counters. Patch primarily by Kelvin Kakugawa with select parts from Chris Goffinet, Sylvain Lebresne, Rob Coli, Johan Oskarsson, Adam Samet, Jaakko Laine and more. Review by Jonathan Ellis and Sylvain Lebresne. CASSANDRA-1072.

git-svn-id: https://svn.apache.org/repos/asf/cassandra/trunk@1051679 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Johan Oskarsson 2010-12-21 22:17:09 +00:00
parent e9b1acc5f4
commit 2c4ac98c9f
76 changed files with 14382 additions and 387 deletions

View File

@ -1,5 +1,6 @@
0.8-dev
* avoid double RowMutation serialization on write path (CASSANDRA-1800)
* adds support for columns that act as incr/decr counters (CASSANDRA-1072)
0.7-dev

View File

@ -459,3 +459,10 @@ keyspaces:
validator_class: LongType
index_name: birthdate_idx
index_type: KEYS
- name: Counter1
default_validation_class: CounterColumnType
- name: SuperCounter1
column_type: Super
default_validation_class: CounterColumnType

View File

@ -146,6 +146,7 @@ protocol Cassandra {
union { double, null } row_cache_size;
union { double, null } key_cache_size;
union { double, null } read_repair_chance;
union { boolean, null } replicate_on_write;
union { int, null } gc_grace_seconds;
union { null, string } default_validation_class = null;
union { null, int } min_compaction_threshold = null;

View File

@ -189,6 +189,21 @@ struct ColumnPath {
5: optional binary column,
}
struct CounterColumn {
1: required binary name,
2: required i64 value
}
struct CounterSuperColumn {
1: required binary name,
2: required list<CounterColumn> columns
}
struct Counter {
1: optional CounterColumn column,
2: optional CounterSuperColumn super_column
}
/**
A slice range is a structure that stores basic range, ordering and limit information for a query that will return
multiple columns. It could be thought of as Cassandra's version of LIMIT and ORDER BY
@ -298,6 +313,21 @@ struct Mutation {
2: optional Deletion deletion,
}
struct CounterDeletion {
1: optional binary super_column,
2: optional SlicePredicate predicate,
}
/**
A CounterMutation is either an insert, represented by filling counter, or a deletion, represented by filling the deletion attribute.
@param counter. An insert to a counter column or supercolumn
@param deletion. A deletion of a counter column or supercolumn
*/
struct CounterMutation {
1: optional Counter counter,
2: optional CounterDeletion deletion,
}
struct TokenRange {
1: required string start_token,
2: required string end_token,
@ -346,6 +376,7 @@ struct CfDef {
21: optional i32 memtable_flush_after_mins,
22: optional i32 memtable_throughput_in_mb,
23: optional double memtable_operations_in_millions,
24: optional bool replicate_on_write=0,
}
/* describes a keyspace. */
@ -470,6 +501,59 @@ service Cassandra {
*/
void truncate(1:required string cfname)
throws (1: InvalidRequestException ire, 2: UnavailableException ue),
# counter methods
/**
* Increment or decrement a counter.
*/
void add(1:required binary key,
2:required ColumnParent column_parent,
3:required CounterColumn column,
4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
/**
* Batch increment or decrement a counter.
*/
void batch_add(1:required map<binary, map<string, list<CounterMutation>>> update_map,
2:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
/**
* Return the counter at the specified column path.
*/
Counter get_counter(1:required binary key,
2:required ColumnPath path,
3:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
throws (1:InvalidRequestException ire, 2:NotFoundException nfe, 3:UnavailableException ue, 4:TimedOutException te),
/**
* Get a list of counters from the specified columns.
*/
list<Counter> get_counter_slice(1:required binary key,
2:required ColumnParent column_parent,
3:required SlicePredicate predicate,
4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
/**
* Get counter slices from multiple keys.
*/
map<binary,list<Counter>> multiget_counter_slice(1:required list<binary> keys,
2:required ColumnParent column_parent,
3:required SlicePredicate predicate,
4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
/**
* Remove a counter at the specified location.
*/
void remove_counter(1:required binary key,
2:required ColumnPath path,
3:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
// Meta-APIs -- APIs to get information about the node or cluster,
// rather than user data. The nodeprobe program provides usage examples.

View File

@ -314,15 +314,15 @@ public class AuthenticationRequest implements TBase<AuthenticationRequest, Authe
case 1: // CREDENTIALS
if (field.type == TType.MAP) {
{
TMap _map20 = iprot.readMapBegin();
this.credentials = new HashMap<String,String>(2*_map20.size);
for (int _i21 = 0; _i21 < _map20.size; ++_i21)
TMap _map24 = iprot.readMapBegin();
this.credentials = new HashMap<String,String>(2*_map24.size);
for (int _i25 = 0; _i25 < _map24.size; ++_i25)
{
String _key22;
String _val23;
_key22 = iprot.readString();
_val23 = iprot.readString();
this.credentials.put(_key22, _val23);
String _key26;
String _val27;
_key26 = iprot.readString();
_val27 = iprot.readString();
this.credentials.put(_key26, _val27);
}
iprot.readMapEnd();
}
@ -349,10 +349,10 @@ public class AuthenticationRequest implements TBase<AuthenticationRequest, Authe
oprot.writeFieldBegin(CREDENTIALS_FIELD_DESC);
{
oprot.writeMapBegin(new TMap(TType.STRING, TType.STRING, this.credentials.size()));
for (Map.Entry<String, String> _iter24 : this.credentials.entrySet())
for (Map.Entry<String, String> _iter28 : this.credentials.entrySet())
{
oprot.writeString(_iter24.getKey());
oprot.writeString(_iter24.getValue());
oprot.writeString(_iter28.getKey());
oprot.writeString(_iter28.getValue());
}
oprot.writeMapEnd();
}

View File

@ -71,6 +71,7 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
private static final TField MEMTABLE_FLUSH_AFTER_MINS_FIELD_DESC = new TField("memtable_flush_after_mins", TType.I32, (short)21);
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);
public String keyspace;
public String name;
@ -92,6 +93,7 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
public int memtable_flush_after_mins;
public int memtable_throughput_in_mb;
public double memtable_operations_in_millions;
public boolean replicate_on_write;
/** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
public enum _Fields implements TFieldIdEnum {
@ -114,7 +116,8 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
KEY_CACHE_SAVE_PERIOD_IN_SECONDS((short)20, "key_cache_save_period_in_seconds"),
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");
MEMTABLE_OPERATIONS_IN_MILLIONS((short)23, "memtable_operations_in_millions"),
REPLICATE_ON_WRITE((short)24, "replicate_on_write");
private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
@ -169,6 +172,8 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
return MEMTABLE_THROUGHPUT_IN_MB;
case 23: // MEMTABLE_OPERATIONS_IN_MILLIONS
return MEMTABLE_OPERATIONS_IN_MILLIONS;
case 24: // REPLICATE_ON_WRITE
return REPLICATE_ON_WRITE;
default:
return null;
}
@ -221,7 +226,8 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
private static final int __MEMTABLE_FLUSH_AFTER_MINS_ISSET_ID = 9;
private static final int __MEMTABLE_THROUGHPUT_IN_MB_ISSET_ID = 10;
private static final int __MEMTABLE_OPERATIONS_IN_MILLIONS_ISSET_ID = 11;
private BitSet __isset_bit_vector = new BitSet(12);
private static final int __REPLICATE_ON_WRITE_ISSET_ID = 12;
private BitSet __isset_bit_vector = new BitSet(13);
public static final Map<_Fields, FieldMetaData> metaDataMap;
static {
@ -267,6 +273,8 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
new FieldValueMetaData(TType.I32)));
tmpMap.put(_Fields.MEMTABLE_OPERATIONS_IN_MILLIONS, new FieldMetaData("memtable_operations_in_millions", TFieldRequirementType.OPTIONAL,
new FieldValueMetaData(TType.DOUBLE)));
tmpMap.put(_Fields.REPLICATE_ON_WRITE, new FieldMetaData("replicate_on_write", TFieldRequirementType.OPTIONAL,
new FieldValueMetaData(TType.BOOL)));
metaDataMap = Collections.unmodifiableMap(tmpMap);
FieldMetaData.addStructMetaDataMap(CfDef.class, metaDataMap);
}
@ -282,6 +290,8 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
this.read_repair_chance = 1;
this.replicate_on_write = false;
}
public CfDef(
@ -339,6 +349,7 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
this.memtable_flush_after_mins = other.memtable_flush_after_mins;
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;
}
public CfDef deepCopy() {
@ -381,6 +392,8 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
this.memtable_throughput_in_mb = 0;
setMemtable_operations_in_millionsIsSet(false);
this.memtable_operations_in_millions = 0.0;
this.replicate_on_write = false;
}
public String getKeyspace() {
@ -866,6 +879,29 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
__isset_bit_vector.set(__MEMTABLE_OPERATIONS_IN_MILLIONS_ISSET_ID, value);
}
public boolean isReplicate_on_write() {
return this.replicate_on_write;
}
public CfDef setReplicate_on_write(boolean replicate_on_write) {
this.replicate_on_write = replicate_on_write;
setReplicate_on_writeIsSet(true);
return this;
}
public void unsetReplicate_on_write() {
__isset_bit_vector.clear(__REPLICATE_ON_WRITE_ISSET_ID);
}
/** Returns true if field replicate_on_write is set (has been asigned a value) and false otherwise */
public boolean isSetReplicate_on_write() {
return __isset_bit_vector.get(__REPLICATE_ON_WRITE_ISSET_ID);
}
public void setReplicate_on_writeIsSet(boolean value) {
__isset_bit_vector.set(__REPLICATE_ON_WRITE_ISSET_ID, value);
}
public void setFieldValue(_Fields field, Object value) {
switch (field) {
case KEYSPACE:
@ -1028,6 +1064,14 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
}
break;
case REPLICATE_ON_WRITE:
if (value == null) {
unsetReplicate_on_write();
} else {
setReplicate_on_write((Boolean)value);
}
break;
}
}
@ -1093,6 +1137,9 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
case MEMTABLE_OPERATIONS_IN_MILLIONS:
return new Double(getMemtable_operations_in_millions());
case REPLICATE_ON_WRITE:
return new Boolean(isReplicate_on_write());
}
throw new IllegalStateException();
}
@ -1144,6 +1191,8 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
return isSetMemtable_throughput_in_mb();
case MEMTABLE_OPERATIONS_IN_MILLIONS:
return isSetMemtable_operations_in_millions();
case REPLICATE_ON_WRITE:
return isSetReplicate_on_write();
}
throw new IllegalStateException();
}
@ -1341,6 +1390,15 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
return false;
}
boolean this_present_replicate_on_write = true && this.isSetReplicate_on_write();
boolean that_present_replicate_on_write = true && that.isSetReplicate_on_write();
if (this_present_replicate_on_write || that_present_replicate_on_write) {
if (!(this_present_replicate_on_write && that_present_replicate_on_write))
return false;
if (this.replicate_on_write != that.replicate_on_write)
return false;
}
return true;
}
@ -1448,6 +1506,11 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
if (present_memtable_operations_in_millions)
builder.append(memtable_operations_in_millions);
boolean present_replicate_on_write = true && (isSetReplicate_on_write());
builder.append(present_replicate_on_write);
if (present_replicate_on_write)
builder.append(replicate_on_write);
return builder.toHashCode();
}
@ -1659,6 +1722,16 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
return lastComparison;
}
}
lastComparison = Boolean.valueOf(isSetReplicate_on_write()).compareTo(typedOther.isSetReplicate_on_write());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetReplicate_on_write()) {
lastComparison = TBaseHelper.compareTo(this.replicate_on_write, typedOther.replicate_on_write);
if (lastComparison != 0) {
return lastComparison;
}
}
return 0;
}
@ -1745,14 +1818,14 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
case 13: // COLUMN_METADATA
if (field.type == TType.LIST) {
{
TList _list25 = iprot.readListBegin();
this.column_metadata = new ArrayList<ColumnDef>(_list25.size);
for (int _i26 = 0; _i26 < _list25.size; ++_i26)
TList _list29 = iprot.readListBegin();
this.column_metadata = new ArrayList<ColumnDef>(_list29.size);
for (int _i30 = 0; _i30 < _list29.size; ++_i30)
{
ColumnDef _elem27;
_elem27 = new ColumnDef();
_elem27.read(iprot);
this.column_metadata.add(_elem27);
ColumnDef _elem31;
_elem31 = new ColumnDef();
_elem31.read(iprot);
this.column_metadata.add(_elem31);
}
iprot.readListEnd();
}
@ -1839,6 +1912,14 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
TProtocolUtil.skip(iprot, field.type);
}
break;
case 24: // REPLICATE_ON_WRITE
if (field.type == TType.BOOL) {
this.replicate_on_write = iprot.readBool();
setReplicate_on_writeIsSet(true);
} else {
TProtocolUtil.skip(iprot, field.type);
}
break;
default:
TProtocolUtil.skip(iprot, field.type);
}
@ -1912,9 +1993,9 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
oprot.writeFieldBegin(COLUMN_METADATA_FIELD_DESC);
{
oprot.writeListBegin(new TList(TType.STRUCT, this.column_metadata.size()));
for (ColumnDef _iter28 : this.column_metadata)
for (ColumnDef _iter32 : this.column_metadata)
{
_iter28.write(oprot);
_iter32.write(oprot);
}
oprot.writeListEnd();
}
@ -1973,6 +2054,11 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
oprot.writeDouble(this.memtable_operations_in_millions);
oprot.writeFieldEnd();
}
if (isSetReplicate_on_write()) {
oprot.writeFieldBegin(REPLICATE_ON_WRITE_FIELD_DESC);
oprot.writeBool(this.replicate_on_write);
oprot.writeFieldEnd();
}
oprot.writeFieldStop();
oprot.writeStructEnd();
}
@ -2129,6 +2215,12 @@ public class CfDef implements TBase<CfDef, CfDef._Fields>, java.io.Serializable,
sb.append(this.memtable_operations_in_millions);
first = false;
}
if (isSetReplicate_on_write()) {
if (!first) sb.append(", ");
sb.append("replicate_on_write:");
sb.append(this.replicate_on_write);
first = false;
}
sb.append(")");
return sb.toString();
}

View File

@ -0,0 +1,433 @@
/**
* Autogenerated by Thrift
*
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
*/
package org.apache.cassandra.thrift;
/*
*
* 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 org.apache.commons.lang.builder.HashCodeBuilder;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.EnumMap;
import java.util.Set;
import java.util.HashSet;
import java.util.EnumSet;
import java.util.Collections;
import java.util.BitSet;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.thrift.*;
import org.apache.thrift.async.*;
import org.apache.thrift.meta_data.*;
import org.apache.thrift.transport.*;
import org.apache.thrift.protocol.*;
public class Counter implements TBase<Counter, Counter._Fields>, java.io.Serializable, Cloneable {
private static final TStruct STRUCT_DESC = new TStruct("Counter");
private static final TField COLUMN_FIELD_DESC = new TField("column", TType.STRUCT, (short)1);
private static final TField SUPER_COLUMN_FIELD_DESC = new TField("super_column", TType.STRUCT, (short)2);
public CounterColumn column;
public CounterSuperColumn super_column;
/** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
public enum _Fields implements TFieldIdEnum {
COLUMN((short)1, "column"),
SUPER_COLUMN((short)2, "super_column");
private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
static {
for (_Fields field : EnumSet.allOf(_Fields.class)) {
byName.put(field.getFieldName(), field);
}
}
/**
* Find the _Fields constant that matches fieldId, or null if its not found.
*/
public static _Fields findByThriftId(int fieldId) {
switch(fieldId) {
case 1: // COLUMN
return COLUMN;
case 2: // SUPER_COLUMN
return SUPER_COLUMN;
default:
return null;
}
}
/**
* Find the _Fields constant that matches fieldId, throwing an exception
* if it is not found.
*/
public static _Fields findByThriftIdOrThrow(int fieldId) {
_Fields fields = findByThriftId(fieldId);
if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
return fields;
}
/**
* Find the _Fields constant that matches name, or null if its not found.
*/
public static _Fields findByName(String name) {
return byName.get(name);
}
private final short _thriftId;
private final String _fieldName;
_Fields(short thriftId, String fieldName) {
_thriftId = thriftId;
_fieldName = fieldName;
}
public short getThriftFieldId() {
return _thriftId;
}
public String getFieldName() {
return _fieldName;
}
}
// isset id assignments
public static final Map<_Fields, FieldMetaData> metaDataMap;
static {
Map<_Fields, FieldMetaData> tmpMap = new EnumMap<_Fields, FieldMetaData>(_Fields.class);
tmpMap.put(_Fields.COLUMN, new FieldMetaData("column", TFieldRequirementType.OPTIONAL,
new StructMetaData(TType.STRUCT, CounterColumn.class)));
tmpMap.put(_Fields.SUPER_COLUMN, new FieldMetaData("super_column", TFieldRequirementType.OPTIONAL,
new StructMetaData(TType.STRUCT, CounterSuperColumn.class)));
metaDataMap = Collections.unmodifiableMap(tmpMap);
FieldMetaData.addStructMetaDataMap(Counter.class, metaDataMap);
}
public Counter() {
}
/**
* Performs a deep copy on <i>other</i>.
*/
public Counter(Counter other) {
if (other.isSetColumn()) {
this.column = new CounterColumn(other.column);
}
if (other.isSetSuper_column()) {
this.super_column = new CounterSuperColumn(other.super_column);
}
}
public Counter deepCopy() {
return new Counter(this);
}
@Override
public void clear() {
this.column = null;
this.super_column = null;
}
public CounterColumn getColumn() {
return this.column;
}
public Counter setColumn(CounterColumn column) {
this.column = column;
return this;
}
public void unsetColumn() {
this.column = null;
}
/** Returns true if field column is set (has been asigned a value) and false otherwise */
public boolean isSetColumn() {
return this.column != null;
}
public void setColumnIsSet(boolean value) {
if (!value) {
this.column = null;
}
}
public CounterSuperColumn getSuper_column() {
return this.super_column;
}
public Counter setSuper_column(CounterSuperColumn super_column) {
this.super_column = super_column;
return this;
}
public void unsetSuper_column() {
this.super_column = null;
}
/** Returns true if field super_column is set (has been asigned a value) and false otherwise */
public boolean isSetSuper_column() {
return this.super_column != null;
}
public void setSuper_columnIsSet(boolean value) {
if (!value) {
this.super_column = null;
}
}
public void setFieldValue(_Fields field, Object value) {
switch (field) {
case COLUMN:
if (value == null) {
unsetColumn();
} else {
setColumn((CounterColumn)value);
}
break;
case SUPER_COLUMN:
if (value == null) {
unsetSuper_column();
} else {
setSuper_column((CounterSuperColumn)value);
}
break;
}
}
public Object getFieldValue(_Fields field) {
switch (field) {
case COLUMN:
return getColumn();
case SUPER_COLUMN:
return getSuper_column();
}
throw new IllegalStateException();
}
/** Returns true if field corresponding to fieldID is set (has been asigned a value) and false otherwise */
public boolean isSet(_Fields field) {
if (field == null) {
throw new IllegalArgumentException();
}
switch (field) {
case COLUMN:
return isSetColumn();
case SUPER_COLUMN:
return isSetSuper_column();
}
throw new IllegalStateException();
}
@Override
public boolean equals(Object that) {
if (that == null)
return false;
if (that instanceof Counter)
return this.equals((Counter)that);
return false;
}
public boolean equals(Counter that) {
if (that == null)
return false;
boolean this_present_column = true && this.isSetColumn();
boolean that_present_column = true && that.isSetColumn();
if (this_present_column || that_present_column) {
if (!(this_present_column && that_present_column))
return false;
if (!this.column.equals(that.column))
return false;
}
boolean this_present_super_column = true && this.isSetSuper_column();
boolean that_present_super_column = true && that.isSetSuper_column();
if (this_present_super_column || that_present_super_column) {
if (!(this_present_super_column && that_present_super_column))
return false;
if (!this.super_column.equals(that.super_column))
return false;
}
return true;
}
@Override
public int hashCode() {
HashCodeBuilder builder = new HashCodeBuilder();
boolean present_column = true && (isSetColumn());
builder.append(present_column);
if (present_column)
builder.append(column);
boolean present_super_column = true && (isSetSuper_column());
builder.append(present_super_column);
if (present_super_column)
builder.append(super_column);
return builder.toHashCode();
}
public int compareTo(Counter other) {
if (!getClass().equals(other.getClass())) {
return getClass().getName().compareTo(other.getClass().getName());
}
int lastComparison = 0;
Counter typedOther = (Counter)other;
lastComparison = Boolean.valueOf(isSetColumn()).compareTo(typedOther.isSetColumn());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetColumn()) {
lastComparison = TBaseHelper.compareTo(this.column, typedOther.column);
if (lastComparison != 0) {
return lastComparison;
}
}
lastComparison = Boolean.valueOf(isSetSuper_column()).compareTo(typedOther.isSetSuper_column());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetSuper_column()) {
lastComparison = TBaseHelper.compareTo(this.super_column, typedOther.super_column);
if (lastComparison != 0) {
return lastComparison;
}
}
return 0;
}
public _Fields fieldForId(int fieldId) {
return _Fields.findByThriftId(fieldId);
}
public void read(TProtocol iprot) throws TException {
TField field;
iprot.readStructBegin();
while (true)
{
field = iprot.readFieldBegin();
if (field.type == TType.STOP) {
break;
}
switch (field.id) {
case 1: // COLUMN
if (field.type == TType.STRUCT) {
this.column = new CounterColumn();
this.column.read(iprot);
} else {
TProtocolUtil.skip(iprot, field.type);
}
break;
case 2: // SUPER_COLUMN
if (field.type == TType.STRUCT) {
this.super_column = new CounterSuperColumn();
this.super_column.read(iprot);
} else {
TProtocolUtil.skip(iprot, field.type);
}
break;
default:
TProtocolUtil.skip(iprot, field.type);
}
iprot.readFieldEnd();
}
iprot.readStructEnd();
// check for required fields of primitive type, which can't be checked in the validate method
validate();
}
public void write(TProtocol oprot) throws TException {
validate();
oprot.writeStructBegin(STRUCT_DESC);
if (this.column != null) {
if (isSetColumn()) {
oprot.writeFieldBegin(COLUMN_FIELD_DESC);
this.column.write(oprot);
oprot.writeFieldEnd();
}
}
if (this.super_column != null) {
if (isSetSuper_column()) {
oprot.writeFieldBegin(SUPER_COLUMN_FIELD_DESC);
this.super_column.write(oprot);
oprot.writeFieldEnd();
}
}
oprot.writeFieldStop();
oprot.writeStructEnd();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("Counter(");
boolean first = true;
if (isSetColumn()) {
sb.append("column:");
if (this.column == null) {
sb.append("null");
} else {
sb.append(this.column);
}
first = false;
}
if (isSetSuper_column()) {
if (!first) sb.append(", ");
sb.append("super_column:");
if (this.super_column == null) {
sb.append("null");
} else {
sb.append(this.super_column);
}
first = false;
}
sb.append(")");
return sb.toString();
}
public void validate() throws TException {
// check for required fields
}
}

View File

@ -0,0 +1,448 @@
/**
* Autogenerated by Thrift
*
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
*/
package org.apache.cassandra.thrift;
/*
*
* 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 org.apache.commons.lang.builder.HashCodeBuilder;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.EnumMap;
import java.util.Set;
import java.util.HashSet;
import java.util.EnumSet;
import java.util.Collections;
import java.util.BitSet;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.thrift.*;
import org.apache.thrift.async.*;
import org.apache.thrift.meta_data.*;
import org.apache.thrift.transport.*;
import org.apache.thrift.protocol.*;
public class CounterColumn implements TBase<CounterColumn, CounterColumn._Fields>, java.io.Serializable, Cloneable {
private static final TStruct STRUCT_DESC = new TStruct("CounterColumn");
private static final TField NAME_FIELD_DESC = new TField("name", TType.STRING, (short)1);
private static final TField VALUE_FIELD_DESC = new TField("value", TType.I64, (short)2);
public ByteBuffer name;
public long value;
/** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
public enum _Fields implements TFieldIdEnum {
NAME((short)1, "name"),
VALUE((short)2, "value");
private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
static {
for (_Fields field : EnumSet.allOf(_Fields.class)) {
byName.put(field.getFieldName(), field);
}
}
/**
* Find the _Fields constant that matches fieldId, or null if its not found.
*/
public static _Fields findByThriftId(int fieldId) {
switch(fieldId) {
case 1: // NAME
return NAME;
case 2: // VALUE
return VALUE;
default:
return null;
}
}
/**
* Find the _Fields constant that matches fieldId, throwing an exception
* if it is not found.
*/
public static _Fields findByThriftIdOrThrow(int fieldId) {
_Fields fields = findByThriftId(fieldId);
if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
return fields;
}
/**
* Find the _Fields constant that matches name, or null if its not found.
*/
public static _Fields findByName(String name) {
return byName.get(name);
}
private final short _thriftId;
private final String _fieldName;
_Fields(short thriftId, String fieldName) {
_thriftId = thriftId;
_fieldName = fieldName;
}
public short getThriftFieldId() {
return _thriftId;
}
public String getFieldName() {
return _fieldName;
}
}
// isset id assignments
private static final int __VALUE_ISSET_ID = 0;
private BitSet __isset_bit_vector = new BitSet(1);
public static final Map<_Fields, FieldMetaData> metaDataMap;
static {
Map<_Fields, FieldMetaData> tmpMap = new EnumMap<_Fields, FieldMetaData>(_Fields.class);
tmpMap.put(_Fields.NAME, new FieldMetaData("name", TFieldRequirementType.REQUIRED,
new FieldValueMetaData(TType.STRING)));
tmpMap.put(_Fields.VALUE, new FieldMetaData("value", TFieldRequirementType.REQUIRED,
new FieldValueMetaData(TType.I64)));
metaDataMap = Collections.unmodifiableMap(tmpMap);
FieldMetaData.addStructMetaDataMap(CounterColumn.class, metaDataMap);
}
public CounterColumn() {
}
public CounterColumn(
ByteBuffer name,
long value)
{
this();
this.name = name;
this.value = value;
setValueIsSet(true);
}
/**
* Performs a deep copy on <i>other</i>.
*/
public CounterColumn(CounterColumn other) {
__isset_bit_vector.clear();
__isset_bit_vector.or(other.__isset_bit_vector);
if (other.isSetName()) {
this.name = TBaseHelper.copyBinary(other.name);
;
}
this.value = other.value;
}
public CounterColumn deepCopy() {
return new CounterColumn(this);
}
@Override
public void clear() {
this.name = null;
setValueIsSet(false);
this.value = 0;
}
public byte[] getName() {
setName(TBaseHelper.rightSize(name));
return name.array();
}
public ByteBuffer BufferForName() {
return name;
}
public CounterColumn setName(byte[] name) {
setName(ByteBuffer.wrap(name));
return this;
}
public CounterColumn setName(ByteBuffer name) {
this.name = name;
return this;
}
public void unsetName() {
this.name = null;
}
/** Returns true if field name is set (has been asigned a value) and false otherwise */
public boolean isSetName() {
return this.name != null;
}
public void setNameIsSet(boolean value) {
if (!value) {
this.name = null;
}
}
public long getValue() {
return this.value;
}
public CounterColumn setValue(long value) {
this.value = value;
setValueIsSet(true);
return this;
}
public void unsetValue() {
__isset_bit_vector.clear(__VALUE_ISSET_ID);
}
/** Returns true if field value is set (has been asigned a value) and false otherwise */
public boolean isSetValue() {
return __isset_bit_vector.get(__VALUE_ISSET_ID);
}
public void setValueIsSet(boolean value) {
__isset_bit_vector.set(__VALUE_ISSET_ID, value);
}
public void setFieldValue(_Fields field, Object value) {
switch (field) {
case NAME:
if (value == null) {
unsetName();
} else {
setName((ByteBuffer)value);
}
break;
case VALUE:
if (value == null) {
unsetValue();
} else {
setValue((Long)value);
}
break;
}
}
public Object getFieldValue(_Fields field) {
switch (field) {
case NAME:
return getName();
case VALUE:
return new Long(getValue());
}
throw new IllegalStateException();
}
/** Returns true if field corresponding to fieldID is set (has been asigned a value) and false otherwise */
public boolean isSet(_Fields field) {
if (field == null) {
throw new IllegalArgumentException();
}
switch (field) {
case NAME:
return isSetName();
case VALUE:
return isSetValue();
}
throw new IllegalStateException();
}
@Override
public boolean equals(Object that) {
if (that == null)
return false;
if (that instanceof CounterColumn)
return this.equals((CounterColumn)that);
return false;
}
public boolean equals(CounterColumn that) {
if (that == null)
return false;
boolean this_present_name = true && this.isSetName();
boolean that_present_name = true && that.isSetName();
if (this_present_name || that_present_name) {
if (!(this_present_name && that_present_name))
return false;
if (!this.name.equals(that.name))
return false;
}
boolean this_present_value = true;
boolean that_present_value = true;
if (this_present_value || that_present_value) {
if (!(this_present_value && that_present_value))
return false;
if (this.value != that.value)
return false;
}
return true;
}
@Override
public int hashCode() {
HashCodeBuilder builder = new HashCodeBuilder();
boolean present_name = true && (isSetName());
builder.append(present_name);
if (present_name)
builder.append(name);
boolean present_value = true;
builder.append(present_value);
if (present_value)
builder.append(value);
return builder.toHashCode();
}
public int compareTo(CounterColumn other) {
if (!getClass().equals(other.getClass())) {
return getClass().getName().compareTo(other.getClass().getName());
}
int lastComparison = 0;
CounterColumn typedOther = (CounterColumn)other;
lastComparison = Boolean.valueOf(isSetName()).compareTo(typedOther.isSetName());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetName()) {
lastComparison = TBaseHelper.compareTo(this.name, typedOther.name);
if (lastComparison != 0) {
return lastComparison;
}
}
lastComparison = Boolean.valueOf(isSetValue()).compareTo(typedOther.isSetValue());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetValue()) {
lastComparison = TBaseHelper.compareTo(this.value, typedOther.value);
if (lastComparison != 0) {
return lastComparison;
}
}
return 0;
}
public _Fields fieldForId(int fieldId) {
return _Fields.findByThriftId(fieldId);
}
public void read(TProtocol iprot) throws TException {
TField field;
iprot.readStructBegin();
while (true)
{
field = iprot.readFieldBegin();
if (field.type == TType.STOP) {
break;
}
switch (field.id) {
case 1: // NAME
if (field.type == TType.STRING) {
this.name = iprot.readBinary();
} else {
TProtocolUtil.skip(iprot, field.type);
}
break;
case 2: // VALUE
if (field.type == TType.I64) {
this.value = iprot.readI64();
setValueIsSet(true);
} else {
TProtocolUtil.skip(iprot, field.type);
}
break;
default:
TProtocolUtil.skip(iprot, field.type);
}
iprot.readFieldEnd();
}
iprot.readStructEnd();
// check for required fields of primitive type, which can't be checked in the validate method
if (!isSetValue()) {
throw new TProtocolException("Required field 'value' was not found in serialized data! Struct: " + toString());
}
validate();
}
public void write(TProtocol oprot) throws TException {
validate();
oprot.writeStructBegin(STRUCT_DESC);
if (this.name != null) {
oprot.writeFieldBegin(NAME_FIELD_DESC);
oprot.writeBinary(this.name);
oprot.writeFieldEnd();
}
oprot.writeFieldBegin(VALUE_FIELD_DESC);
oprot.writeI64(this.value);
oprot.writeFieldEnd();
oprot.writeFieldStop();
oprot.writeStructEnd();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("CounterColumn(");
boolean first = true;
sb.append("name:");
if (this.name == null) {
sb.append("null");
} else {
TBaseHelper.toString(this.name, sb);
}
first = false;
if (!first) sb.append(", ");
sb.append("value:");
sb.append(this.value);
first = false;
sb.append(")");
return sb.toString();
}
public void validate() throws TException {
// check for required fields
if (name == null) {
throw new TProtocolException("Required field 'name' was not present! Struct: " + toString());
}
// alas, we cannot check 'value' because it's a primitive and you chose the non-beans generator.
}
}

View File

@ -0,0 +1,443 @@
/**
* Autogenerated by Thrift
*
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
*/
package org.apache.cassandra.thrift;
/*
*
* 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 org.apache.commons.lang.builder.HashCodeBuilder;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.EnumMap;
import java.util.Set;
import java.util.HashSet;
import java.util.EnumSet;
import java.util.Collections;
import java.util.BitSet;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.thrift.*;
import org.apache.thrift.async.*;
import org.apache.thrift.meta_data.*;
import org.apache.thrift.transport.*;
import org.apache.thrift.protocol.*;
public class CounterDeletion implements TBase<CounterDeletion, CounterDeletion._Fields>, java.io.Serializable, Cloneable {
private static final TStruct STRUCT_DESC = new TStruct("CounterDeletion");
private static final TField SUPER_COLUMN_FIELD_DESC = new TField("super_column", TType.STRING, (short)1);
private static final TField PREDICATE_FIELD_DESC = new TField("predicate", TType.STRUCT, (short)2);
public ByteBuffer super_column;
public SlicePredicate predicate;
/** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
public enum _Fields implements TFieldIdEnum {
SUPER_COLUMN((short)1, "super_column"),
PREDICATE((short)2, "predicate");
private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
static {
for (_Fields field : EnumSet.allOf(_Fields.class)) {
byName.put(field.getFieldName(), field);
}
}
/**
* Find the _Fields constant that matches fieldId, or null if its not found.
*/
public static _Fields findByThriftId(int fieldId) {
switch(fieldId) {
case 1: // SUPER_COLUMN
return SUPER_COLUMN;
case 2: // PREDICATE
return PREDICATE;
default:
return null;
}
}
/**
* Find the _Fields constant that matches fieldId, throwing an exception
* if it is not found.
*/
public static _Fields findByThriftIdOrThrow(int fieldId) {
_Fields fields = findByThriftId(fieldId);
if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
return fields;
}
/**
* Find the _Fields constant that matches name, or null if its not found.
*/
public static _Fields findByName(String name) {
return byName.get(name);
}
private final short _thriftId;
private final String _fieldName;
_Fields(short thriftId, String fieldName) {
_thriftId = thriftId;
_fieldName = fieldName;
}
public short getThriftFieldId() {
return _thriftId;
}
public String getFieldName() {
return _fieldName;
}
}
// isset id assignments
public static final Map<_Fields, FieldMetaData> metaDataMap;
static {
Map<_Fields, FieldMetaData> tmpMap = new EnumMap<_Fields, FieldMetaData>(_Fields.class);
tmpMap.put(_Fields.SUPER_COLUMN, new FieldMetaData("super_column", TFieldRequirementType.OPTIONAL,
new FieldValueMetaData(TType.STRING)));
tmpMap.put(_Fields.PREDICATE, new FieldMetaData("predicate", TFieldRequirementType.OPTIONAL,
new StructMetaData(TType.STRUCT, SlicePredicate.class)));
metaDataMap = Collections.unmodifiableMap(tmpMap);
FieldMetaData.addStructMetaDataMap(CounterDeletion.class, metaDataMap);
}
public CounterDeletion() {
}
/**
* Performs a deep copy on <i>other</i>.
*/
public CounterDeletion(CounterDeletion other) {
if (other.isSetSuper_column()) {
this.super_column = TBaseHelper.copyBinary(other.super_column);
;
}
if (other.isSetPredicate()) {
this.predicate = new SlicePredicate(other.predicate);
}
}
public CounterDeletion deepCopy() {
return new CounterDeletion(this);
}
@Override
public void clear() {
this.super_column = null;
this.predicate = null;
}
public byte[] getSuper_column() {
setSuper_column(TBaseHelper.rightSize(super_column));
return super_column.array();
}
public ByteBuffer BufferForSuper_column() {
return super_column;
}
public CounterDeletion setSuper_column(byte[] super_column) {
setSuper_column(ByteBuffer.wrap(super_column));
return this;
}
public CounterDeletion setSuper_column(ByteBuffer super_column) {
this.super_column = super_column;
return this;
}
public void unsetSuper_column() {
this.super_column = null;
}
/** Returns true if field super_column is set (has been asigned a value) and false otherwise */
public boolean isSetSuper_column() {
return this.super_column != null;
}
public void setSuper_columnIsSet(boolean value) {
if (!value) {
this.super_column = null;
}
}
public SlicePredicate getPredicate() {
return this.predicate;
}
public CounterDeletion setPredicate(SlicePredicate predicate) {
this.predicate = predicate;
return this;
}
public void unsetPredicate() {
this.predicate = null;
}
/** Returns true if field predicate is set (has been asigned a value) and false otherwise */
public boolean isSetPredicate() {
return this.predicate != null;
}
public void setPredicateIsSet(boolean value) {
if (!value) {
this.predicate = null;
}
}
public void setFieldValue(_Fields field, Object value) {
switch (field) {
case SUPER_COLUMN:
if (value == null) {
unsetSuper_column();
} else {
setSuper_column((ByteBuffer)value);
}
break;
case PREDICATE:
if (value == null) {
unsetPredicate();
} else {
setPredicate((SlicePredicate)value);
}
break;
}
}
public Object getFieldValue(_Fields field) {
switch (field) {
case SUPER_COLUMN:
return getSuper_column();
case PREDICATE:
return getPredicate();
}
throw new IllegalStateException();
}
/** Returns true if field corresponding to fieldID is set (has been asigned a value) and false otherwise */
public boolean isSet(_Fields field) {
if (field == null) {
throw new IllegalArgumentException();
}
switch (field) {
case SUPER_COLUMN:
return isSetSuper_column();
case PREDICATE:
return isSetPredicate();
}
throw new IllegalStateException();
}
@Override
public boolean equals(Object that) {
if (that == null)
return false;
if (that instanceof CounterDeletion)
return this.equals((CounterDeletion)that);
return false;
}
public boolean equals(CounterDeletion that) {
if (that == null)
return false;
boolean this_present_super_column = true && this.isSetSuper_column();
boolean that_present_super_column = true && that.isSetSuper_column();
if (this_present_super_column || that_present_super_column) {
if (!(this_present_super_column && that_present_super_column))
return false;
if (!this.super_column.equals(that.super_column))
return false;
}
boolean this_present_predicate = true && this.isSetPredicate();
boolean that_present_predicate = true && that.isSetPredicate();
if (this_present_predicate || that_present_predicate) {
if (!(this_present_predicate && that_present_predicate))
return false;
if (!this.predicate.equals(that.predicate))
return false;
}
return true;
}
@Override
public int hashCode() {
HashCodeBuilder builder = new HashCodeBuilder();
boolean present_super_column = true && (isSetSuper_column());
builder.append(present_super_column);
if (present_super_column)
builder.append(super_column);
boolean present_predicate = true && (isSetPredicate());
builder.append(present_predicate);
if (present_predicate)
builder.append(predicate);
return builder.toHashCode();
}
public int compareTo(CounterDeletion other) {
if (!getClass().equals(other.getClass())) {
return getClass().getName().compareTo(other.getClass().getName());
}
int lastComparison = 0;
CounterDeletion typedOther = (CounterDeletion)other;
lastComparison = Boolean.valueOf(isSetSuper_column()).compareTo(typedOther.isSetSuper_column());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetSuper_column()) {
lastComparison = TBaseHelper.compareTo(this.super_column, typedOther.super_column);
if (lastComparison != 0) {
return lastComparison;
}
}
lastComparison = Boolean.valueOf(isSetPredicate()).compareTo(typedOther.isSetPredicate());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetPredicate()) {
lastComparison = TBaseHelper.compareTo(this.predicate, typedOther.predicate);
if (lastComparison != 0) {
return lastComparison;
}
}
return 0;
}
public _Fields fieldForId(int fieldId) {
return _Fields.findByThriftId(fieldId);
}
public void read(TProtocol iprot) throws TException {
TField field;
iprot.readStructBegin();
while (true)
{
field = iprot.readFieldBegin();
if (field.type == TType.STOP) {
break;
}
switch (field.id) {
case 1: // SUPER_COLUMN
if (field.type == TType.STRING) {
this.super_column = iprot.readBinary();
} else {
TProtocolUtil.skip(iprot, field.type);
}
break;
case 2: // PREDICATE
if (field.type == TType.STRUCT) {
this.predicate = new SlicePredicate();
this.predicate.read(iprot);
} else {
TProtocolUtil.skip(iprot, field.type);
}
break;
default:
TProtocolUtil.skip(iprot, field.type);
}
iprot.readFieldEnd();
}
iprot.readStructEnd();
// check for required fields of primitive type, which can't be checked in the validate method
validate();
}
public void write(TProtocol oprot) throws TException {
validate();
oprot.writeStructBegin(STRUCT_DESC);
if (this.super_column != null) {
if (isSetSuper_column()) {
oprot.writeFieldBegin(SUPER_COLUMN_FIELD_DESC);
oprot.writeBinary(this.super_column);
oprot.writeFieldEnd();
}
}
if (this.predicate != null) {
if (isSetPredicate()) {
oprot.writeFieldBegin(PREDICATE_FIELD_DESC);
this.predicate.write(oprot);
oprot.writeFieldEnd();
}
}
oprot.writeFieldStop();
oprot.writeStructEnd();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("CounterDeletion(");
boolean first = true;
if (isSetSuper_column()) {
sb.append("super_column:");
if (this.super_column == null) {
sb.append("null");
} else {
TBaseHelper.toString(this.super_column, sb);
}
first = false;
}
if (isSetPredicate()) {
if (!first) sb.append(", ");
sb.append("predicate:");
if (this.predicate == null) {
sb.append("null");
} else {
sb.append(this.predicate);
}
first = false;
}
sb.append(")");
return sb.toString();
}
public void validate() throws TException {
// check for required fields
}
}

View File

@ -0,0 +1,438 @@
/**
* Autogenerated by Thrift
*
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
*/
package org.apache.cassandra.thrift;
/*
*
* 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 org.apache.commons.lang.builder.HashCodeBuilder;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.EnumMap;
import java.util.Set;
import java.util.HashSet;
import java.util.EnumSet;
import java.util.Collections;
import java.util.BitSet;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.thrift.*;
import org.apache.thrift.async.*;
import org.apache.thrift.meta_data.*;
import org.apache.thrift.transport.*;
import org.apache.thrift.protocol.*;
/**
* A CounterMutation is either an insert, represented by filling counter, or a deletion, represented by filling the deletion attribute.
* @param counter. An insert to a counter column or supercolumn
* @param deletion. A deletion of a counter column or supercolumn
*/
public class CounterMutation implements TBase<CounterMutation, CounterMutation._Fields>, java.io.Serializable, Cloneable {
private static final TStruct STRUCT_DESC = new TStruct("CounterMutation");
private static final TField COUNTER_FIELD_DESC = new TField("counter", TType.STRUCT, (short)1);
private static final TField DELETION_FIELD_DESC = new TField("deletion", TType.STRUCT, (short)2);
public Counter counter;
public CounterDeletion deletion;
/** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
public enum _Fields implements TFieldIdEnum {
COUNTER((short)1, "counter"),
DELETION((short)2, "deletion");
private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
static {
for (_Fields field : EnumSet.allOf(_Fields.class)) {
byName.put(field.getFieldName(), field);
}
}
/**
* Find the _Fields constant that matches fieldId, or null if its not found.
*/
public static _Fields findByThriftId(int fieldId) {
switch(fieldId) {
case 1: // COUNTER
return COUNTER;
case 2: // DELETION
return DELETION;
default:
return null;
}
}
/**
* Find the _Fields constant that matches fieldId, throwing an exception
* if it is not found.
*/
public static _Fields findByThriftIdOrThrow(int fieldId) {
_Fields fields = findByThriftId(fieldId);
if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
return fields;
}
/**
* Find the _Fields constant that matches name, or null if its not found.
*/
public static _Fields findByName(String name) {
return byName.get(name);
}
private final short _thriftId;
private final String _fieldName;
_Fields(short thriftId, String fieldName) {
_thriftId = thriftId;
_fieldName = fieldName;
}
public short getThriftFieldId() {
return _thriftId;
}
public String getFieldName() {
return _fieldName;
}
}
// isset id assignments
public static final Map<_Fields, FieldMetaData> metaDataMap;
static {
Map<_Fields, FieldMetaData> tmpMap = new EnumMap<_Fields, FieldMetaData>(_Fields.class);
tmpMap.put(_Fields.COUNTER, new FieldMetaData("counter", TFieldRequirementType.OPTIONAL,
new StructMetaData(TType.STRUCT, Counter.class)));
tmpMap.put(_Fields.DELETION, new FieldMetaData("deletion", TFieldRequirementType.OPTIONAL,
new StructMetaData(TType.STRUCT, CounterDeletion.class)));
metaDataMap = Collections.unmodifiableMap(tmpMap);
FieldMetaData.addStructMetaDataMap(CounterMutation.class, metaDataMap);
}
public CounterMutation() {
}
/**
* Performs a deep copy on <i>other</i>.
*/
public CounterMutation(CounterMutation other) {
if (other.isSetCounter()) {
this.counter = new Counter(other.counter);
}
if (other.isSetDeletion()) {
this.deletion = new CounterDeletion(other.deletion);
}
}
public CounterMutation deepCopy() {
return new CounterMutation(this);
}
@Override
public void clear() {
this.counter = null;
this.deletion = null;
}
public Counter getCounter() {
return this.counter;
}
public CounterMutation setCounter(Counter counter) {
this.counter = counter;
return this;
}
public void unsetCounter() {
this.counter = null;
}
/** Returns true if field counter is set (has been asigned a value) and false otherwise */
public boolean isSetCounter() {
return this.counter != null;
}
public void setCounterIsSet(boolean value) {
if (!value) {
this.counter = null;
}
}
public CounterDeletion getDeletion() {
return this.deletion;
}
public CounterMutation setDeletion(CounterDeletion deletion) {
this.deletion = deletion;
return this;
}
public void unsetDeletion() {
this.deletion = null;
}
/** Returns true if field deletion is set (has been asigned a value) and false otherwise */
public boolean isSetDeletion() {
return this.deletion != null;
}
public void setDeletionIsSet(boolean value) {
if (!value) {
this.deletion = null;
}
}
public void setFieldValue(_Fields field, Object value) {
switch (field) {
case COUNTER:
if (value == null) {
unsetCounter();
} else {
setCounter((Counter)value);
}
break;
case DELETION:
if (value == null) {
unsetDeletion();
} else {
setDeletion((CounterDeletion)value);
}
break;
}
}
public Object getFieldValue(_Fields field) {
switch (field) {
case COUNTER:
return getCounter();
case DELETION:
return getDeletion();
}
throw new IllegalStateException();
}
/** Returns true if field corresponding to fieldID is set (has been asigned a value) and false otherwise */
public boolean isSet(_Fields field) {
if (field == null) {
throw new IllegalArgumentException();
}
switch (field) {
case COUNTER:
return isSetCounter();
case DELETION:
return isSetDeletion();
}
throw new IllegalStateException();
}
@Override
public boolean equals(Object that) {
if (that == null)
return false;
if (that instanceof CounterMutation)
return this.equals((CounterMutation)that);
return false;
}
public boolean equals(CounterMutation that) {
if (that == null)
return false;
boolean this_present_counter = true && this.isSetCounter();
boolean that_present_counter = true && that.isSetCounter();
if (this_present_counter || that_present_counter) {
if (!(this_present_counter && that_present_counter))
return false;
if (!this.counter.equals(that.counter))
return false;
}
boolean this_present_deletion = true && this.isSetDeletion();
boolean that_present_deletion = true && that.isSetDeletion();
if (this_present_deletion || that_present_deletion) {
if (!(this_present_deletion && that_present_deletion))
return false;
if (!this.deletion.equals(that.deletion))
return false;
}
return true;
}
@Override
public int hashCode() {
HashCodeBuilder builder = new HashCodeBuilder();
boolean present_counter = true && (isSetCounter());
builder.append(present_counter);
if (present_counter)
builder.append(counter);
boolean present_deletion = true && (isSetDeletion());
builder.append(present_deletion);
if (present_deletion)
builder.append(deletion);
return builder.toHashCode();
}
public int compareTo(CounterMutation other) {
if (!getClass().equals(other.getClass())) {
return getClass().getName().compareTo(other.getClass().getName());
}
int lastComparison = 0;
CounterMutation typedOther = (CounterMutation)other;
lastComparison = Boolean.valueOf(isSetCounter()).compareTo(typedOther.isSetCounter());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetCounter()) {
lastComparison = TBaseHelper.compareTo(this.counter, typedOther.counter);
if (lastComparison != 0) {
return lastComparison;
}
}
lastComparison = Boolean.valueOf(isSetDeletion()).compareTo(typedOther.isSetDeletion());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetDeletion()) {
lastComparison = TBaseHelper.compareTo(this.deletion, typedOther.deletion);
if (lastComparison != 0) {
return lastComparison;
}
}
return 0;
}
public _Fields fieldForId(int fieldId) {
return _Fields.findByThriftId(fieldId);
}
public void read(TProtocol iprot) throws TException {
TField field;
iprot.readStructBegin();
while (true)
{
field = iprot.readFieldBegin();
if (field.type == TType.STOP) {
break;
}
switch (field.id) {
case 1: // COUNTER
if (field.type == TType.STRUCT) {
this.counter = new Counter();
this.counter.read(iprot);
} else {
TProtocolUtil.skip(iprot, field.type);
}
break;
case 2: // DELETION
if (field.type == TType.STRUCT) {
this.deletion = new CounterDeletion();
this.deletion.read(iprot);
} else {
TProtocolUtil.skip(iprot, field.type);
}
break;
default:
TProtocolUtil.skip(iprot, field.type);
}
iprot.readFieldEnd();
}
iprot.readStructEnd();
// check for required fields of primitive type, which can't be checked in the validate method
validate();
}
public void write(TProtocol oprot) throws TException {
validate();
oprot.writeStructBegin(STRUCT_DESC);
if (this.counter != null) {
if (isSetCounter()) {
oprot.writeFieldBegin(COUNTER_FIELD_DESC);
this.counter.write(oprot);
oprot.writeFieldEnd();
}
}
if (this.deletion != null) {
if (isSetDeletion()) {
oprot.writeFieldBegin(DELETION_FIELD_DESC);
this.deletion.write(oprot);
oprot.writeFieldEnd();
}
}
oprot.writeFieldStop();
oprot.writeStructEnd();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("CounterMutation(");
boolean first = true;
if (isSetCounter()) {
sb.append("counter:");
if (this.counter == null) {
sb.append("null");
} else {
sb.append(this.counter);
}
first = false;
}
if (isSetDeletion()) {
if (!first) sb.append(", ");
sb.append("deletion:");
if (this.deletion == null) {
sb.append("null");
} else {
sb.append(this.deletion);
}
first = false;
}
sb.append(")");
return sb.toString();
}
public void validate() throws TException {
// check for required fields
}
}

View File

@ -0,0 +1,487 @@
/**
* Autogenerated by Thrift
*
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
*/
package org.apache.cassandra.thrift;
/*
*
* 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 org.apache.commons.lang.builder.HashCodeBuilder;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.EnumMap;
import java.util.Set;
import java.util.HashSet;
import java.util.EnumSet;
import java.util.Collections;
import java.util.BitSet;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.thrift.*;
import org.apache.thrift.async.*;
import org.apache.thrift.meta_data.*;
import org.apache.thrift.transport.*;
import org.apache.thrift.protocol.*;
public class CounterSuperColumn implements TBase<CounterSuperColumn, CounterSuperColumn._Fields>, java.io.Serializable, Cloneable {
private static final TStruct STRUCT_DESC = new TStruct("CounterSuperColumn");
private static final TField NAME_FIELD_DESC = new TField("name", TType.STRING, (short)1);
private static final TField COLUMNS_FIELD_DESC = new TField("columns", TType.LIST, (short)2);
public ByteBuffer name;
public List<CounterColumn> columns;
/** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
public enum _Fields implements TFieldIdEnum {
NAME((short)1, "name"),
COLUMNS((short)2, "columns");
private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
static {
for (_Fields field : EnumSet.allOf(_Fields.class)) {
byName.put(field.getFieldName(), field);
}
}
/**
* Find the _Fields constant that matches fieldId, or null if its not found.
*/
public static _Fields findByThriftId(int fieldId) {
switch(fieldId) {
case 1: // NAME
return NAME;
case 2: // COLUMNS
return COLUMNS;
default:
return null;
}
}
/**
* Find the _Fields constant that matches fieldId, throwing an exception
* if it is not found.
*/
public static _Fields findByThriftIdOrThrow(int fieldId) {
_Fields fields = findByThriftId(fieldId);
if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
return fields;
}
/**
* Find the _Fields constant that matches name, or null if its not found.
*/
public static _Fields findByName(String name) {
return byName.get(name);
}
private final short _thriftId;
private final String _fieldName;
_Fields(short thriftId, String fieldName) {
_thriftId = thriftId;
_fieldName = fieldName;
}
public short getThriftFieldId() {
return _thriftId;
}
public String getFieldName() {
return _fieldName;
}
}
// isset id assignments
public static final Map<_Fields, FieldMetaData> metaDataMap;
static {
Map<_Fields, FieldMetaData> tmpMap = new EnumMap<_Fields, FieldMetaData>(_Fields.class);
tmpMap.put(_Fields.NAME, new FieldMetaData("name", TFieldRequirementType.REQUIRED,
new FieldValueMetaData(TType.STRING)));
tmpMap.put(_Fields.COLUMNS, new FieldMetaData("columns", TFieldRequirementType.REQUIRED,
new ListMetaData(TType.LIST,
new StructMetaData(TType.STRUCT, CounterColumn.class))));
metaDataMap = Collections.unmodifiableMap(tmpMap);
FieldMetaData.addStructMetaDataMap(CounterSuperColumn.class, metaDataMap);
}
public CounterSuperColumn() {
}
public CounterSuperColumn(
ByteBuffer name,
List<CounterColumn> columns)
{
this();
this.name = name;
this.columns = columns;
}
/**
* Performs a deep copy on <i>other</i>.
*/
public CounterSuperColumn(CounterSuperColumn other) {
if (other.isSetName()) {
this.name = TBaseHelper.copyBinary(other.name);
;
}
if (other.isSetColumns()) {
List<CounterColumn> __this__columns = new ArrayList<CounterColumn>();
for (CounterColumn other_element : other.columns) {
__this__columns.add(new CounterColumn(other_element));
}
this.columns = __this__columns;
}
}
public CounterSuperColumn deepCopy() {
return new CounterSuperColumn(this);
}
@Override
public void clear() {
this.name = null;
this.columns = null;
}
public byte[] getName() {
setName(TBaseHelper.rightSize(name));
return name.array();
}
public ByteBuffer BufferForName() {
return name;
}
public CounterSuperColumn setName(byte[] name) {
setName(ByteBuffer.wrap(name));
return this;
}
public CounterSuperColumn setName(ByteBuffer name) {
this.name = name;
return this;
}
public void unsetName() {
this.name = null;
}
/** Returns true if field name is set (has been asigned a value) and false otherwise */
public boolean isSetName() {
return this.name != null;
}
public void setNameIsSet(boolean value) {
if (!value) {
this.name = null;
}
}
public int getColumnsSize() {
return (this.columns == null) ? 0 : this.columns.size();
}
public java.util.Iterator<CounterColumn> getColumnsIterator() {
return (this.columns == null) ? null : this.columns.iterator();
}
public void addToColumns(CounterColumn elem) {
if (this.columns == null) {
this.columns = new ArrayList<CounterColumn>();
}
this.columns.add(elem);
}
public List<CounterColumn> getColumns() {
return this.columns;
}
public CounterSuperColumn setColumns(List<CounterColumn> columns) {
this.columns = columns;
return this;
}
public void unsetColumns() {
this.columns = null;
}
/** Returns true if field columns is set (has been asigned a value) and false otherwise */
public boolean isSetColumns() {
return this.columns != null;
}
public void setColumnsIsSet(boolean value) {
if (!value) {
this.columns = null;
}
}
public void setFieldValue(_Fields field, Object value) {
switch (field) {
case NAME:
if (value == null) {
unsetName();
} else {
setName((ByteBuffer)value);
}
break;
case COLUMNS:
if (value == null) {
unsetColumns();
} else {
setColumns((List<CounterColumn>)value);
}
break;
}
}
public Object getFieldValue(_Fields field) {
switch (field) {
case NAME:
return getName();
case COLUMNS:
return getColumns();
}
throw new IllegalStateException();
}
/** Returns true if field corresponding to fieldID is set (has been asigned a value) and false otherwise */
public boolean isSet(_Fields field) {
if (field == null) {
throw new IllegalArgumentException();
}
switch (field) {
case NAME:
return isSetName();
case COLUMNS:
return isSetColumns();
}
throw new IllegalStateException();
}
@Override
public boolean equals(Object that) {
if (that == null)
return false;
if (that instanceof CounterSuperColumn)
return this.equals((CounterSuperColumn)that);
return false;
}
public boolean equals(CounterSuperColumn that) {
if (that == null)
return false;
boolean this_present_name = true && this.isSetName();
boolean that_present_name = true && that.isSetName();
if (this_present_name || that_present_name) {
if (!(this_present_name && that_present_name))
return false;
if (!this.name.equals(that.name))
return false;
}
boolean this_present_columns = true && this.isSetColumns();
boolean that_present_columns = true && that.isSetColumns();
if (this_present_columns || that_present_columns) {
if (!(this_present_columns && that_present_columns))
return false;
if (!this.columns.equals(that.columns))
return false;
}
return true;
}
@Override
public int hashCode() {
HashCodeBuilder builder = new HashCodeBuilder();
boolean present_name = true && (isSetName());
builder.append(present_name);
if (present_name)
builder.append(name);
boolean present_columns = true && (isSetColumns());
builder.append(present_columns);
if (present_columns)
builder.append(columns);
return builder.toHashCode();
}
public int compareTo(CounterSuperColumn other) {
if (!getClass().equals(other.getClass())) {
return getClass().getName().compareTo(other.getClass().getName());
}
int lastComparison = 0;
CounterSuperColumn typedOther = (CounterSuperColumn)other;
lastComparison = Boolean.valueOf(isSetName()).compareTo(typedOther.isSetName());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetName()) {
lastComparison = TBaseHelper.compareTo(this.name, typedOther.name);
if (lastComparison != 0) {
return lastComparison;
}
}
lastComparison = Boolean.valueOf(isSetColumns()).compareTo(typedOther.isSetColumns());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetColumns()) {
lastComparison = TBaseHelper.compareTo(this.columns, typedOther.columns);
if (lastComparison != 0) {
return lastComparison;
}
}
return 0;
}
public _Fields fieldForId(int fieldId) {
return _Fields.findByThriftId(fieldId);
}
public void read(TProtocol iprot) throws TException {
TField field;
iprot.readStructBegin();
while (true)
{
field = iprot.readFieldBegin();
if (field.type == TType.STOP) {
break;
}
switch (field.id) {
case 1: // NAME
if (field.type == TType.STRING) {
this.name = iprot.readBinary();
} else {
TProtocolUtil.skip(iprot, field.type);
}
break;
case 2: // COLUMNS
if (field.type == TType.LIST) {
{
TList _list4 = iprot.readListBegin();
this.columns = new ArrayList<CounterColumn>(_list4.size);
for (int _i5 = 0; _i5 < _list4.size; ++_i5)
{
CounterColumn _elem6;
_elem6 = new CounterColumn();
_elem6.read(iprot);
this.columns.add(_elem6);
}
iprot.readListEnd();
}
} else {
TProtocolUtil.skip(iprot, field.type);
}
break;
default:
TProtocolUtil.skip(iprot, field.type);
}
iprot.readFieldEnd();
}
iprot.readStructEnd();
// check for required fields of primitive type, which can't be checked in the validate method
validate();
}
public void write(TProtocol oprot) throws TException {
validate();
oprot.writeStructBegin(STRUCT_DESC);
if (this.name != null) {
oprot.writeFieldBegin(NAME_FIELD_DESC);
oprot.writeBinary(this.name);
oprot.writeFieldEnd();
}
if (this.columns != null) {
oprot.writeFieldBegin(COLUMNS_FIELD_DESC);
{
oprot.writeListBegin(new TList(TType.STRUCT, this.columns.size()));
for (CounterColumn _iter7 : this.columns)
{
_iter7.write(oprot);
}
oprot.writeListEnd();
}
oprot.writeFieldEnd();
}
oprot.writeFieldStop();
oprot.writeStructEnd();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("CounterSuperColumn(");
boolean first = true;
sb.append("name:");
if (this.name == null) {
sb.append("null");
} else {
TBaseHelper.toString(this.name, sb);
}
first = false;
if (!first) sb.append(", ");
sb.append("columns:");
if (this.columns == null) {
sb.append("null");
} else {
sb.append(this.columns);
}
first = false;
sb.append(")");
return sb.toString();
}
public void validate() throws TException {
// check for required fields
if (name == null) {
throw new TProtocolException("Required field 'name' was not present! Struct: " + toString());
}
if (columns == null) {
throw new TProtocolException("Required field 'columns' was not present! Struct: " + toString());
}
}
}

View File

@ -470,14 +470,14 @@ public class IndexClause implements TBase<IndexClause, IndexClause._Fields>, jav
case 1: // EXPRESSIONS
if (field.type == TType.LIST) {
{
TList _list8 = iprot.readListBegin();
this.expressions = new ArrayList<IndexExpression>(_list8.size);
for (int _i9 = 0; _i9 < _list8.size; ++_i9)
TList _list12 = iprot.readListBegin();
this.expressions = new ArrayList<IndexExpression>(_list12.size);
for (int _i13 = 0; _i13 < _list12.size; ++_i13)
{
IndexExpression _elem10;
_elem10 = new IndexExpression();
_elem10.read(iprot);
this.expressions.add(_elem10);
IndexExpression _elem14;
_elem14 = new IndexExpression();
_elem14.read(iprot);
this.expressions.add(_elem14);
}
iprot.readListEnd();
}
@ -522,9 +522,9 @@ public class IndexClause implements TBase<IndexClause, IndexClause._Fields>, jav
oprot.writeFieldBegin(EXPRESSIONS_FIELD_DESC);
{
oprot.writeListBegin(new TList(TType.STRUCT, this.expressions.size()));
for (IndexExpression _iter11 : this.expressions)
for (IndexExpression _iter15 : this.expressions)
{
_iter11.write(oprot);
_iter15.write(oprot);
}
oprot.writeListEnd();
}

View File

@ -405,14 +405,14 @@ public class KeySlice implements TBase<KeySlice, KeySlice._Fields>, java.io.Seri
case 2: // COLUMNS
if (field.type == TType.LIST) {
{
TList _list12 = iprot.readListBegin();
this.columns = new ArrayList<ColumnOrSuperColumn>(_list12.size);
for (int _i13 = 0; _i13 < _list12.size; ++_i13)
TList _list16 = iprot.readListBegin();
this.columns = new ArrayList<ColumnOrSuperColumn>(_list16.size);
for (int _i17 = 0; _i17 < _list16.size; ++_i17)
{
ColumnOrSuperColumn _elem14;
_elem14 = new ColumnOrSuperColumn();
_elem14.read(iprot);
this.columns.add(_elem14);
ColumnOrSuperColumn _elem18;
_elem18 = new ColumnOrSuperColumn();
_elem18.read(iprot);
this.columns.add(_elem18);
}
iprot.readListEnd();
}
@ -444,9 +444,9 @@ public class KeySlice implements TBase<KeySlice, KeySlice._Fields>, java.io.Seri
oprot.writeFieldBegin(COLUMNS_FIELD_DESC);
{
oprot.writeListBegin(new TList(TType.STRUCT, this.columns.size()));
for (ColumnOrSuperColumn _iter15 : this.columns)
for (ColumnOrSuperColumn _iter19 : this.columns)
{
_iter15.write(oprot);
_iter19.write(oprot);
}
oprot.writeListEnd();
}

View File

@ -642,15 +642,15 @@ public class KsDef implements TBase<KsDef, KsDef._Fields>, java.io.Serializable,
case 3: // STRATEGY_OPTIONS
if (field.type == TType.MAP) {
{
TMap _map29 = iprot.readMapBegin();
this.strategy_options = new HashMap<String,String>(2*_map29.size);
for (int _i30 = 0; _i30 < _map29.size; ++_i30)
TMap _map33 = iprot.readMapBegin();
this.strategy_options = new HashMap<String,String>(2*_map33.size);
for (int _i34 = 0; _i34 < _map33.size; ++_i34)
{
String _key31;
String _val32;
_key31 = iprot.readString();
_val32 = iprot.readString();
this.strategy_options.put(_key31, _val32);
String _key35;
String _val36;
_key35 = iprot.readString();
_val36 = iprot.readString();
this.strategy_options.put(_key35, _val36);
}
iprot.readMapEnd();
}
@ -669,14 +669,14 @@ public class KsDef implements TBase<KsDef, KsDef._Fields>, java.io.Serializable,
case 5: // CF_DEFS
if (field.type == TType.LIST) {
{
TList _list33 = iprot.readListBegin();
this.cf_defs = new ArrayList<CfDef>(_list33.size);
for (int _i34 = 0; _i34 < _list33.size; ++_i34)
TList _list37 = iprot.readListBegin();
this.cf_defs = new ArrayList<CfDef>(_list37.size);
for (int _i38 = 0; _i38 < _list37.size; ++_i38)
{
CfDef _elem35;
_elem35 = new CfDef();
_elem35.read(iprot);
this.cf_defs.add(_elem35);
CfDef _elem39;
_elem39 = new CfDef();
_elem39.read(iprot);
this.cf_defs.add(_elem39);
}
iprot.readListEnd();
}
@ -717,10 +717,10 @@ public class KsDef implements TBase<KsDef, KsDef._Fields>, java.io.Serializable,
oprot.writeFieldBegin(STRATEGY_OPTIONS_FIELD_DESC);
{
oprot.writeMapBegin(new TMap(TType.STRING, TType.STRING, this.strategy_options.size()));
for (Map.Entry<String, String> _iter36 : this.strategy_options.entrySet())
for (Map.Entry<String, String> _iter40 : this.strategy_options.entrySet())
{
oprot.writeString(_iter36.getKey());
oprot.writeString(_iter36.getValue());
oprot.writeString(_iter40.getKey());
oprot.writeString(_iter40.getValue());
}
oprot.writeMapEnd();
}
@ -734,9 +734,9 @@ public class KsDef implements TBase<KsDef, KsDef._Fields>, java.io.Serializable,
oprot.writeFieldBegin(CF_DEFS_FIELD_DESC);
{
oprot.writeListBegin(new TList(TType.STRUCT, this.cf_defs.size()));
for (CfDef _iter37 : this.cf_defs)
for (CfDef _iter41 : this.cf_defs)
{
_iter37.write(oprot);
_iter41.write(oprot);
}
oprot.writeListEnd();
}

View File

@ -385,13 +385,13 @@ public class SlicePredicate implements TBase<SlicePredicate, SlicePredicate._Fie
case 1: // COLUMN_NAMES
if (field.type == TType.LIST) {
{
TList _list4 = iprot.readListBegin();
this.column_names = new ArrayList<ByteBuffer>(_list4.size);
for (int _i5 = 0; _i5 < _list4.size; ++_i5)
TList _list8 = iprot.readListBegin();
this.column_names = new ArrayList<ByteBuffer>(_list8.size);
for (int _i9 = 0; _i9 < _list8.size; ++_i9)
{
ByteBuffer _elem6;
_elem6 = iprot.readBinary();
this.column_names.add(_elem6);
ByteBuffer _elem10;
_elem10 = iprot.readBinary();
this.column_names.add(_elem10);
}
iprot.readListEnd();
}
@ -427,9 +427,9 @@ public class SlicePredicate implements TBase<SlicePredicate, SlicePredicate._Fie
oprot.writeFieldBegin(COLUMN_NAMES_FIELD_DESC);
{
oprot.writeListBegin(new TList(TType.STRING, this.column_names.size()));
for (ByteBuffer _iter7 : this.column_names)
for (ByteBuffer _iter11 : this.column_names)
{
oprot.writeBinary(_iter7);
oprot.writeBinary(_iter11);
}
oprot.writeListEnd();
}

View File

@ -468,13 +468,13 @@ public class TokenRange implements TBase<TokenRange, TokenRange._Fields>, java.i
case 3: // ENDPOINTS
if (field.type == TType.LIST) {
{
TList _list16 = iprot.readListBegin();
this.endpoints = new ArrayList<String>(_list16.size);
for (int _i17 = 0; _i17 < _list16.size; ++_i17)
TList _list20 = iprot.readListBegin();
this.endpoints = new ArrayList<String>(_list20.size);
for (int _i21 = 0; _i21 < _list20.size; ++_i21)
{
String _elem18;
_elem18 = iprot.readString();
this.endpoints.add(_elem18);
String _elem22;
_elem22 = iprot.readString();
this.endpoints.add(_elem22);
}
iprot.readListEnd();
}
@ -511,9 +511,9 @@ public class TokenRange implements TBase<TokenRange, TokenRange._Fields>, java.i
oprot.writeFieldBegin(ENDPOINTS_FIELD_DESC);
{
oprot.writeListBegin(new TList(TType.STRING, this.endpoints.size()));
for (String _iter19 : this.endpoints)
for (String _iter23 : this.endpoints)
{
oprot.writeString(_iter19);
oprot.writeString(_iter23);
}
oprot.writeListEnd();
}

View File

@ -88,7 +88,6 @@ public class CassandraServer implements Cassandra {
// CfDef default values
private final static String D_CF_CFTYPE = "Standard";
private final static String D_CF_CFCLOCKTYPE = "Timestamp";
private final static String D_CF_COMPTYPE = "BytesType";
private final static String D_CF_SUBCOMPTYPE = "";
private final static String D_CF_RECONCILER = null;
@ -837,6 +836,7 @@ public class CassandraServer implements Cassandra {
cf_def.row_cache_size == null ? CFMetaData.DEFAULT_ROW_CACHE_SIZE : cf_def.row_cache_size,
cf_def.key_cache_size == null ? CFMetaData.DEFAULT_KEY_CACHE_SIZE : cf_def.key_cache_size,
cf_def.read_repair_chance == null ? CFMetaData.DEFAULT_READ_REPAIR_CHANCE : cf_def.read_repair_chance,
cf_def.replicate_on_write == null ? CFMetaData.DEFAULT_REPLICATE_ON_WRITE : cf_def.replicate_on_write,
cf_def.gc_grace_seconds != null ? cf_def.gc_grace_seconds : CFMetaData.DEFAULT_GC_GRACE_SECONDS,
DatabaseDescriptor.getComparator(validate),
cf_def.min_compaction_threshold == null ? CFMetaData.DEFAULT_MIN_COMPACTION_THRESHOLD : cf_def.min_compaction_threshold,

View File

@ -31,7 +31,8 @@ public enum Stage
ANTI_ENTROPY,
MIGRATION,
MISC,
INTERNAL_RESPONSE;
INTERNAL_RESPONSE,
REPLICATE_ON_WRITE;
public String getJmxType()
{
@ -47,6 +48,7 @@ public enum Stage
case MUTATION:
case READ:
case REQUEST_RESPONSE:
case REPLICATE_ON_WRITE:
return "request";
default:
throw new AssertionError("Unknown stage " + this);

View File

@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit;
import static org.apache.cassandra.config.DatabaseDescriptor.getConcurrentReaders;
import static org.apache.cassandra.config.DatabaseDescriptor.getConcurrentWriters;
import static org.apache.cassandra.config.DatabaseDescriptor.getConcurrentReplicators;
/**
@ -44,6 +45,7 @@ public class StageManager
stages.put(Stage.READ, multiThreadedConfigurableStage(Stage.READ, getConcurrentReaders()));
stages.put(Stage.REQUEST_RESPONSE, multiThreadedStage(Stage.REQUEST_RESPONSE, Math.max(2, Runtime.getRuntime().availableProcessors())));
stages.put(Stage.INTERNAL_RESPONSE, multiThreadedStage(Stage.INTERNAL_RESPONSE, Math.max(2, Runtime.getRuntime().availableProcessors())));
stages.put(Stage.REPLICATE_ON_WRITE, multiThreadedConfigurableStage(Stage.REPLICATE_ON_WRITE, getConcurrentReplicators()));
// the rest are all single-threaded
stages.put(Stage.STREAM, new JMXEnabledThreadPoolExecutor(Stage.STREAM));
stages.put(Stage.GOSSIP, new JMXEnabledThreadPoolExecutor(Stage.GOSSIP));

View File

@ -58,6 +58,7 @@ public final class CFMetaData
public final static double DEFAULT_ROW_CACHE_SIZE = 0.0;
public final static double DEFAULT_KEY_CACHE_SIZE = 200000;
public final static double DEFAULT_READ_REPAIR_CHANCE = 1.0;
public final static boolean DEFAULT_REPLICATE_ON_WRITE = true;
public final static int DEFAULT_ROW_CACHE_SAVE_PERIOD_IN_SECONDS = 0;
public final static int DEFAULT_KEY_CACHE_SAVE_PERIOD_IN_SECONDS = 3600;
public final static int DEFAULT_GC_GRACE_SECONDS = 864000;
@ -90,6 +91,7 @@ public final class CFMetaData
0,
0.01,
0,
false,
0,
BytesType.instance,
DEFAULT_MIN_COMPACTION_THRESHOLD,
@ -155,6 +157,7 @@ public final class CFMetaData
private double rowCacheSize; // default 0
private double keyCacheSize; // default 0.01
private double readRepairChance; // default 1.0 (always), chance [0.0,1.0] of read repair
private boolean replicateOnWrite; // default false
private int gcGraceSeconds; // default 864000 (ten days)
private AbstractType defaultValidator; // default none, use comparator types
private Integer minCompactionThreshold; // default 4
@ -177,6 +180,7 @@ public final class CFMetaData
double rowCacheSize,
double keyCacheSize,
double readRepairChance,
boolean replicateOnWrite,
int gcGraceSeconds,
AbstractType defaultValidator,
int minCompactionThreshold,
@ -204,6 +208,7 @@ public final class CFMetaData
this.rowCacheSize = rowCacheSize;
this.keyCacheSize = keyCacheSize;
this.readRepairChance = readRepairChance;
this.replicateOnWrite = replicateOnWrite;
this.gcGraceSeconds = gcGraceSeconds;
this.defaultValidator = defaultValidator;
this.minCompactionThreshold = minCompactionThreshold;
@ -242,6 +247,7 @@ public final class CFMetaData
double rowCacheSize,
double keyCacheSize,
double readRepairChance,
boolean replicateOnWrite,
int gcGraceSeconds,
AbstractType defaultValidator,
int minCompactionThreshold,
@ -263,6 +269,7 @@ public final class CFMetaData
rowCacheSize,
keyCacheSize,
readRepairChance,
replicateOnWrite,
gcGraceSeconds,
defaultValidator,
minCompactionThreshold,
@ -287,6 +294,7 @@ public final class CFMetaData
0,
0,
0,
false,
DEFAULT_GC_GRACE_SECONDS,
BytesType.instance,
DEFAULT_MIN_COMPACTION_THRESHOLD,
@ -311,6 +319,7 @@ public final class CFMetaData
cfm.rowCacheSize,
cfm.keyCacheSize,
cfm.readRepairChance,
cfm.replicateOnWrite,
cfm.gcGraceSeconds,
cfm.defaultValidator,
cfm.minCompactionThreshold,
@ -336,6 +345,7 @@ public final class CFMetaData
cfm.rowCacheSize,
cfm.keyCacheSize,
cfm.readRepairChance,
cfm.replicateOnWrite,
cfm.gcGraceSeconds,
cfm.defaultValidator,
cfm.minCompactionThreshold,
@ -369,6 +379,7 @@ public final class CFMetaData
cf.row_cache_size = rowCacheSize;
cf.key_cache_size = keyCacheSize;
cf.read_repair_chance = readRepairChance;
cf.replicate_on_write = replicateOnWrite;
cf.gc_grace_seconds = gcGraceSeconds;
cf.default_validation_class = new Utf8(defaultValidator.getClass().getName());
cf.min_compaction_threshold = minCompactionThreshold;
@ -428,6 +439,7 @@ public final class CFMetaData
cf.row_cache_size,
cf.key_cache_size,
cf.read_repair_chance,
cf.replicate_on_write,
cf.gc_grace_seconds,
validator,
minct,
@ -460,6 +472,11 @@ public final class CFMetaData
{
return readRepairChance;
}
public boolean getReplicateOnWrite()
{
return replicateOnWrite;
}
public int getGcGraceSeconds()
{
@ -533,6 +550,7 @@ public final class CFMetaData
.append(rowCacheSize, rhs.rowCacheSize)
.append(keyCacheSize, rhs.keyCacheSize)
.append(readRepairChance, rhs.readRepairChance)
.append(replicateOnWrite, rhs.replicateOnWrite)
.append(gcGraceSeconds, rhs.gcGraceSeconds)
.append(minCompactionThreshold, rhs.minCompactionThreshold)
.append(maxCompactionThreshold, rhs.maxCompactionThreshold)
@ -558,6 +576,7 @@ public final class CFMetaData
.append(rowCacheSize)
.append(keyCacheSize)
.append(readRepairChance)
.append(replicateOnWrite)
.append(gcGraceSeconds)
.append(defaultValidator)
.append(minCompactionThreshold)
@ -654,6 +673,7 @@ public final class CFMetaData
rowCacheSize = cf_def.row_cache_size;
keyCacheSize = cf_def.key_cache_size;
readRepairChance = cf_def.read_repair_chance;
replicateOnWrite = cf_def.replicate_on_write;
gcGraceSeconds = cf_def.gc_grace_seconds;
defaultValidator = DatabaseDescriptor.getComparator(cf_def.default_validation_class);
minCompactionThreshold = cf_def.min_compaction_threshold;
@ -716,6 +736,7 @@ public final class CFMetaData
def.setRow_cache_size(cfm.rowCacheSize);
def.setKey_cache_size(cfm.keyCacheSize);
def.setRead_repair_chance(cfm.readRepairChance);
def.setReplicate_on_write(cfm.replicateOnWrite);
def.setGc_grace_seconds(cfm.gcGraceSeconds);
def.setDefault_validation_class(cfm.defaultValidator.getClass().getName());
def.setMin_compaction_threshold(cfm.minCompactionThreshold);
@ -757,6 +778,7 @@ public final class CFMetaData
def.row_cache_size = cfm.rowCacheSize;
def.key_cache_size = cfm.keyCacheSize;
def.read_repair_chance = cfm.readRepairChance;
def.replicate_on_write = cfm.replicateOnWrite;
def.gc_grace_seconds = cfm.gcGraceSeconds;
def.default_validation_class = cfm.defaultValidator == null ? null : cfm.defaultValidator.getClass().getName();
def.min_compaction_threshold = cfm.minCompactionThreshold;
@ -799,6 +821,7 @@ public final class CFMetaData
newDef.memtable_throughput_in_mb = def.getMemtable_throughput_in_mb();
newDef.min_compaction_threshold = def.getMin_compaction_threshold();
newDef.read_repair_chance = def.getRead_repair_chance();
newDef.replicate_on_write = def.isReplicate_on_write();
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();
@ -917,6 +940,7 @@ public final class CFMetaData
.append("rowCacheSize", rowCacheSize)
.append("keyCacheSize", keyCacheSize)
.append("readRepairChance", readRepairChance)
.append("replicateOnWrite", replicateOnWrite)
.append("gcGraceSeconds", gcGraceSeconds)
.append("defaultValidator", defaultValidator)
.append("minCompactionThreshold", minCompactionThreshold)

View File

@ -53,6 +53,7 @@ public class Config
public Integer concurrent_reads = 8;
public Integer concurrent_writes = 32;
public Integer concurrent_replicates = 32;
public Integer memtable_flush_writers = null; // will get set to the length of data dirs in DatabaseDescriptor

View File

@ -224,6 +224,11 @@ public class DatabaseDescriptor
throw new ConfigurationException("concurrent_writes must be at least 2");
}
if (conf.concurrent_replicates != null && conf.concurrent_replicates < 2)
{
throw new ConfigurationException("conf.concurrent_replicates must be at least 2");
}
/* Memtable flush writer threads */
if (conf.memtable_flush_writers != null && conf.memtable_flush_writers < 1)
{
@ -613,6 +618,7 @@ public class DatabaseDescriptor
cf.rows_cached,
cf.keys_cached,
cf.read_repair_chance,
cf.replicate_on_write,
cf.gc_grace_seconds,
default_validator,
cf.min_compaction_threshold,
@ -841,6 +847,11 @@ public class DatabaseDescriptor
return conf.concurrent_writes;
}
public static int getConcurrentReplicators()
{
return conf.concurrent_replicates;
}
public static int getFlushWriters()
{
return conf.memtable_flush_writers;

View File

@ -36,6 +36,7 @@ public class RawColumnFamily
public double rows_cached = CFMetaData.DEFAULT_ROW_CACHE_SIZE;
public double keys_cached = CFMetaData.DEFAULT_KEY_CACHE_SIZE;
public double read_repair_chance = CFMetaData.DEFAULT_READ_REPAIR_CHANCE;
public boolean replicate_on_write = CFMetaData.DEFAULT_REPLICATE_ON_WRITE;
public int gc_grace_seconds = CFMetaData.DEFAULT_GC_GRACE_SECONDS;
public String default_validation_class;
public int min_compaction_threshold = CFMetaData.DEFAULT_MIN_COMPACTION_THRESHOLD;

View File

@ -33,6 +33,7 @@ import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.filter.QueryPath;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.AbstractCommutativeType;
import org.apache.cassandra.io.ICompactSerializer2;
import org.apache.cassandra.io.util.IIterableColumns;
import org.apache.cassandra.utils.FBUtilities;
@ -154,18 +155,25 @@ public class ColumnFamily implements IColumnContainer, IIterableColumns
public void addColumn(QueryPath path, ByteBuffer value, long timestamp)
{
assert path.columnName != null : path;
addColumn(path.superColumnName, new Column(path.columnName, value, timestamp));
addColumn(path, value, timestamp, 0);
}
public void addColumn(QueryPath path, ByteBuffer value, long timestamp, int timeToLive)
{
assert path.columnName != null : path;
Column column;
if (timeToLive > 0)
column = new ExpiringColumn(path.columnName, value, timestamp, timeToLive);
AbstractType defaultValidator = metadata().getDefaultValidator();
if (!defaultValidator.isCommutative())
{
if (timeToLive > 0)
column = new ExpiringColumn(path.columnName, value, timestamp, timeToLive);
else
column = new Column(path.columnName, value, timestamp);
}
else
column = new Column(path.columnName, value, timestamp);
{
column = ((AbstractCommutativeType)defaultValidator).createColumn(path.columnName, value, timestamp);
}
addColumn(path.superColumnName, column);
}

View File

@ -25,6 +25,7 @@ import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.regex.Pattern;
import javax.management.MBeanServer;
import javax.management.ObjectName;
@ -292,6 +293,42 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
return keys;
}
public boolean reverseReadWriteOrder()
{
//XXX: PURPOSE: allow less harmful race condition w/o locking
// normal read/write order (non-commutative):
// purpose:
// avoid missing an MT; may double reconcile
// read path order:
// 1) live MT
// 2) MTs pending flush
// 3) SSTs
// write path order: (live MT => MT pending flush)
// 1) add live MT to MTs pending flush
// 2) reset live MT
// write path order: (MT pending flush => SST)
// 1) add SST
// 2) remove MT pending flush
// reversed read/write order (commutative):
// purpose:
// avoid over-counting an MT; may miss an MT
// read path order:
// 1) SSTs
// 2) MTs pending flush
// 3) live MT
// write path order: (live MT => MT pending flush)
// 1) save live MT
// 2) reset live MT
// 3) add saved MT to MTs pending flush
// write path order: (MT pending flush => SST)
// 1) remove MT pending flush
// 2) add SST
return metadata.getDefaultValidator().isCommutative();
}
public void addIndex(final ColumnDefinition info)
{
assert info.getIndexType() != null;
@ -650,8 +687,19 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
final CountDownLatch latch = new CountDownLatch(icc.size());
for (ColumnFamilyStore cfs : icc)
{
submitFlush(cfs.memtable, latch);
cfs.memtable = new Memtable(cfs);
if (!reverseReadWriteOrder())
{
//XXX: race condition: may allow double reconcile; but never misses an MT
submitFlush(cfs.memtable, latch);
cfs.memtable = new Memtable(cfs);
}
else
{
//XXX: race condition: may miss an MT, but no double counts
Memtable pendingFlush = cfs.memtable;
cfs.memtable = new Memtable(cfs);
submitFlush(pendingFlush, latch);
}
}
// when all the memtables have been written, including for indexes, mark the flush in the commitlog header.
@ -793,7 +841,6 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
// remove columns if
// (a) the column itself is tombstoned or
// (b) the CF is tombstoned and the column is not newer than it
// (we split the test to avoid computing ClockRelationship if not necessary)
if ((c.isMarkedForDelete() && c.getLocalDeletionTime() <= gcBefore)
|| c.timestamp() <= cf.getMarkedForDeleteAt())
{
@ -1180,36 +1227,75 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
{
IColumnIterator iter;
/* add the current memtable */
iter = filter.getMemtableColumnIterator(getMemtableThreadSafe(), getComparator());
if (iter != null)
int sstablesToIterate = 0;
if (!reverseReadWriteOrder())
{
returnCF.delete(iter.getColumnFamily());
iterators.add(iter);
}
//XXX: race condition: may allow double reconcile; but never misses an MT
/* add the memtables being flushed */
for (Memtable memtable : memtablesPendingFlush)
{
iter = filter.getMemtableColumnIterator(memtable, getComparator());
/* add the current memtable */
iter = filter.getMemtableColumnIterator(getMemtableThreadSafe(), getComparator());
if (iter != null)
{
returnCF.delete(iter.getColumnFamily());
iterators.add(iter);
}
}
/* add the SSTables on disk */
int sstablesToIterate = 0;
for (SSTableReader sstable : ssTables)
/* add the memtables being flushed */
for (Memtable memtable : memtablesPendingFlush)
{
iter = filter.getMemtableColumnIterator(memtable, getComparator());
if (iter != null)
{
returnCF.delete(iter.getColumnFamily());
iterators.add(iter);
}
}
/* add the SSTables on disk */
for (SSTableReader sstable : ssTables)
{
iter = filter.getSSTableColumnIterator(sstable);
if (iter.getColumnFamily() != null)
{
returnCF.delete(iter.getColumnFamily());
iterators.add(iter);
}
sstablesToIterate++;
}
}
else
{
iter = filter.getSSTableColumnIterator(sstable);
if (iter.getColumnFamily() != null)
//XXX: race condition: may miss an MT, but no double counts
/* add the SSTables on disk */
for (SSTableReader sstable : ssTables)
{
iter = filter.getSSTableColumnIterator(sstable);
if (iter.getColumnFamily() != null)
{
returnCF.delete(iter.getColumnFamily());
iterators.add(iter);
}
sstablesToIterate++;
}
/* add the memtables being flushed */
for (Memtable memtable : memtablesPendingFlush)
{
iter = filter.getMemtableColumnIterator(memtable, getComparator());
if (iter != null)
{
returnCF.delete(iter.getColumnFamily());
iterators.add(iter);
}
}
/* add the current memtable */
iter = filter.getMemtableColumnIterator(getMemtableThreadSafe(), getComparator());
if (iter != null)
{
returnCF.delete(iter.getColumnFamily());
iterators.add(iter);
sstablesToIterate++;
}
}
recentSSTablesPerRead.add(sstablesToIterate);
@ -1270,12 +1356,23 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
QueryFilter filter = new QueryFilter(null, new QueryPath(columnFamily, superColumn, null), columnFilter);
Collection<Memtable> memtables = new ArrayList<Memtable>();
memtables.add(getMemtableThreadSafe());
memtables.addAll(memtablesPendingFlush);
Collection<SSTableReader> sstables = new ArrayList<SSTableReader>();
Iterables.addAll(sstables, ssTables);
if (!reverseReadWriteOrder())
{
//XXX: race condition: may allow double reconcile; but never misses an MT
memtables.add(getMemtableThreadSafe());
memtables.addAll(memtablesPendingFlush);
Iterables.addAll(sstables, ssTables);
}
else
{
//XXX: race condition: may miss an MT, but no double counts
Iterables.addAll(sstables, ssTables);
memtables.addAll(memtablesPendingFlush);
memtables.add(getMemtableThreadSafe());
}
RowIterator iterator = RowIteratorFactory.getIterator(memtables, sstables, startWith, stopAt, filter, getComparator(), this);
int gcBefore = (int)(System.currentTimeMillis() / 1000) - metadata.getGcGraceSeconds();

View File

@ -25,6 +25,7 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -35,9 +36,10 @@ import org.apache.cassandra.utils.FBUtilities;
public class ColumnSerializer implements ICompactSerializer2<IColumn>
{
private static final Logger logger = LoggerFactory.getLogger(ColumnSerializer.class);
public final static int DELETION_MASK = 0x01;
public final static int EXPIRATION_MASK = 0x02;
public final static int COUNTER_MASK = 0x04;
public void serialize(IColumn column, DataOutput dos)
{
@ -45,12 +47,21 @@ public class ColumnSerializer implements ICompactSerializer2<IColumn>
FBUtilities.writeShortByteArray(column.name(), dos);
try
{
if (column instanceof ExpiringColumn) {
dos.writeByte(EXPIRATION_MASK);
dos.writeInt(((ExpiringColumn) column).getTimeToLive());
dos.writeInt(column.getLocalDeletionTime());
} else {
dos.writeByte((column.isMarkedForDelete()) ? DELETION_MASK : 0);
if (column instanceof CounterColumn)
{
dos.writeByte(COUNTER_MASK);
dos.writeLong(((CounterColumn)column).timestampOfLastDelete());
FBUtilities.writeShortByteArray(ByteBuffer.wrap(((CounterColumn)column).partitionedCounter()), dos);
}
else if (column instanceof ExpiringColumn)
{
dos.writeByte(EXPIRATION_MASK);
dos.writeInt(((ExpiringColumn) column).getTimeToLive());
dos.writeInt(column.getLocalDeletionTime());
}
else
{
dos.writeByte((column.isMarkedForDelete()) ? DELETION_MASK : 0);
}
dos.writeLong(column.timestamp());
FBUtilities.writeByteArray(column.value(), dos);
@ -68,7 +79,16 @@ public class ColumnSerializer implements ICompactSerializer2<IColumn>
throw new CorruptColumnException("invalid column name length " + name.remaining());
int b = dis.readUnsignedByte();
if ((b & EXPIRATION_MASK) != 0)
if ((b & COUNTER_MASK) != 0)
{
long timestampOfLastDelete = dis.readLong();
ByteBuffer pc = FBUtilities.readShortByteArray(dis);
byte[] partitionedCounter = Arrays.copyOfRange(pc.array(), pc.position() + pc.arrayOffset(), pc.limit());
long timestamp = dis.readLong();
ByteBuffer value = FBUtilities.readByteArray(dis);
return new CounterColumn(name, value, timestamp, partitionedCounter, timestampOfLastDelete);
}
else if ((b & EXPIRATION_MASK) != 0)
{
int ttl = dis.readInt();
int expiration = dis.readInt();

View File

@ -52,6 +52,7 @@ import org.apache.cassandra.io.sstable.*;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.service.AntiEntropyService;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.streaming.OperationType;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.Pair;
import org.cliffc.high_scale_lib.NonBlockingHashMap;
@ -595,11 +596,11 @@ public class CompactionManager implements CompactionManagerMBean
else
return executor.submit(runnable);
}
public Future<SSTableReader> submitSSTableBuild(Descriptor desc)
public Future<SSTableReader> submitSSTableBuild(Descriptor desc, OperationType type)
{
// invalid descriptions due to missing or dropped CFS are handled by SSTW and StreamInSession.
final SSTableWriter.Builder builder = SSTableWriter.createBuilder(desc);
final SSTableWriter.Builder builder = SSTableWriter.createBuilder(desc, type);
Callable<SSTableReader> callable = new Callable<SSTableReader>()
{
public SSTableReader call() throws IOException

View File

@ -0,0 +1,287 @@
/**
* 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;
import java.io.IOException;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.Arrays;
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;
/**
* A column that represents a partitioned counter.
*/
public class CounterColumn extends Column
{
private static Logger logger = Logger.getLogger(CounterColumn.class);
private static CounterContext contextManager = CounterContext.instance();
protected ByteBuffer value; // NOT final: delta OR total of partitioned counter
protected byte[] partitionedCounter; // NOT final: only modify inline, carefully
protected final long timestampOfLastDelete;
public CounterColumn(ByteBuffer name, ByteBuffer value, long timestamp)
{
this(name, value, timestamp, contextManager.create());
}
public CounterColumn(ByteBuffer name, ByteBuffer value, long timestamp, byte[] partitionedCounter)
{
this(name, value, timestamp, partitionedCounter, Long.MIN_VALUE);
}
public CounterColumn(ByteBuffer name, ByteBuffer value, long timestamp, byte[] partitionedCounter, long timestampOfLastDelete)
{
super(name, FBUtilities.EMPTY_BYTE_BUFFER, timestamp);
this.value = value;
this.partitionedCounter = partitionedCounter;
this.timestampOfLastDelete = timestampOfLastDelete;
}
@Override
public ByteBuffer value()
{
return value;
}
public byte[] partitionedCounter()
{
return partitionedCounter;
}
public long timestampOfLastDelete()
{
return timestampOfLastDelete;
}
@Override
public int size()
{
/*
* An expired column adds to a Column :
* 4 bytes for length of partitionedCounter
* + length of partitionedCounter
* + 8 bytes for timestampOfLastDelete
*/
return super.size() + DBConstants.intSize_ + partitionedCounter.length + DBConstants.tsSize_;
}
@Override
public IColumn diff(IColumn column)
{
assert column instanceof CounterColumn : "Wrong class type.";
if (timestamp() < column.timestamp())
return column;
if (timestampOfLastDelete() < ((CounterColumn)column).timestampOfLastDelete())
return column;
ContextRelationship rel = contextManager.diff(
((CounterColumn)column).partitionedCounter(),
partitionedCounter());
if (ContextRelationship.GREATER_THAN == rel || ContextRelationship.DISJOINT == rel)
return column;
return null;
}
@Override
public void updateDigest(MessageDigest digest)
{
digest.update(name.array(),name.position()+name.arrayOffset(),name.remaining());
digest.update(value.array(),value.position()+name.arrayOffset(),value.remaining());
digest.update(FBUtilities.toByteArray(timestamp));
digest.update(partitionedCounter);
digest.update(FBUtilities.toByteArray(timestampOfLastDelete));
}
@Override
public IColumn reconcile(IColumn column)
{
assert (column instanceof CounterColumn) || (column instanceof DeletedColumn) : "Wrong class type.";
if (isMarkedForDelete())
{
if (column.isMarkedForDelete()) // tombstone + tombstone: keep later tombstone
{
return timestamp() > column.timestamp() ? this : column;
}
else // tombstone + live: track last tombstone
{
if (timestamp() > column.timestamp()) // tombstone > live
{
return this;
}
// tombstone <= live last delete
if (timestamp() <= ((CounterColumn)column).timestampOfLastDelete())
{
return column;
}
// tombstone > live last delete
return new CounterColumn(
column.name(),
column.value(),
column.timestamp(),
((CounterColumn)column).partitionedCounter(),
timestamp());
}
}
else if (column.isMarkedForDelete()) // live + tombstone: track last tombstone
{
if (timestamp() < column.timestamp()) // live < tombstone
{
return column;
}
// live last delete >= tombstone
if (timestampOfLastDelete() >= column.timestamp())
{
return this;
}
// live last delete < tombstone
return new CounterColumn(
name(),
value(),
timestamp(),
partitionedCounter(),
column.timestamp());
}
// live + live: merge clocks; update value
byte[] mergedPartitionedCounter = contextManager.merge(
partitionedCounter(),
((CounterColumn)column).partitionedCounter());
ByteBuffer byteBufferValue;
if (0 == mergedPartitionedCounter.length)
{
long mergedValue = value().getLong(value().arrayOffset()) +
column.value().getLong(column.value().arrayOffset());
byteBufferValue = FBUtilities.toByteBuffer(mergedValue);
} else
byteBufferValue = ByteBuffer.wrap(contextManager.total(mergedPartitionedCounter));
return new CounterColumn(
name(),
byteBufferValue,
Math.max(timestamp(), column.timestamp()),
mergedPartitionedCounter,
Math.max(timestampOfLastDelete(), ((CounterColumn)column).timestampOfLastDelete()));
}
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
CounterColumn column = (CounterColumn)o;
if (timestamp != column.timestamp)
return false;
if (timestampOfLastDelete != column.timestampOfLastDelete)
return false;
if (!Arrays.equals(partitionedCounter, column.partitionedCounter))
return false;
if (!name.equals(column.name))
return false;
return value.equals(column.value);
}
@Override
public int hashCode()
{
int result = super.hashCode();
result = 31 * result + (partitionedCounter != null ? Arrays.hashCode(partitionedCounter) : 0);
result = 31 * result + (int)(timestampOfLastDelete ^ (timestampOfLastDelete >>> 32));
return result;
}
@Override
public IColumn deepCopy()
{
return new CounterColumn(
ByteBufferUtil.clone(name),
ByteBufferUtil.clone(value),
timestamp,
partitionedCounter,
timestampOfLastDelete);
}
@Override
public String getString(AbstractType comparator)
{
StringBuilder sb = new StringBuilder();
sb.append(comparator.getString(name));
sb.append(":");
sb.append(isMarkedForDelete());
sb.append(":");
sb.append(value.getLong(value.arrayOffset()));
sb.append("@");
sb.append(timestamp());
sb.append("!");
sb.append(timestampOfLastDelete);
sb.append("@");
sb.append(contextManager.toString(partitionedCounter));
return sb.toString();
}
private void updateValue()
{
value = ByteBuffer.wrap(contextManager.total(partitionedCounter));
}
public void update(InetAddress node)
{
long delta = value.getLong(value.arrayOffset());
partitionedCounter = contextManager.update(partitionedCounter, node, delta);
updateValue();
}
public CounterColumn cleanNodeCounts(InetAddress node)
{
//XXX: inline modification non-destructive; cases:
// 1) AES post-stream
// 2) RRR, after CF.cloneMe()
// 3) RRR, after CF.diff() which creates a new CF
byte[] cleanPartitionedCounter = contextManager.cleanNodeCounts(partitionedCounter, node);
if (cleanPartitionedCounter == partitionedCounter)
return this;
if (0 == cleanPartitionedCounter.length)
return null;
return new CounterColumn(
name,
ByteBuffer.wrap(contextManager.total(cleanPartitionedCounter)),
timestamp,
cleanPartitionedCounter,
timestampOfLastDelete
);
}
}

View File

@ -56,6 +56,14 @@ public class DeletedColumn extends Column
{
return value.getInt(value.position()+value.arrayOffset() );
}
@Override
public IColumn reconcile(IColumn column)
{
if (column instanceof DeletedColumn)
return super.reconcile(column);
return column.reconcile(this);
}
@Override
public IColumn deepCopy()

View File

@ -20,15 +20,20 @@ package org.apache.cassandra.db;
*
*/
import java.nio.ByteBuffer;
import java.util.Collection;
import org.apache.cassandra.db.marshal.AbstractType;
public interface IColumnContainer
{
public void addColumn(IColumn column);
public void remove(ByteBuffer columnName);
public boolean isMarkedForDelete();
public long getMarkedForDeleteAt();
public AbstractType getComparator();
public Collection<IColumn> getSortedColumns();
}

View File

@ -171,8 +171,18 @@ public class Memtable implements Comparable<Memtable>, IFlushable
{
public void runMayThrow() throws IOException
{
cfs.addSSTable(writeSortedContents());
cfs.getMemtablesPendingFlush().remove(Memtable.this);
if (!cfs.reverseReadWriteOrder())
{
//XXX: race condition: may allow double reconcile; but never misses an MT
cfs.addSSTable(writeSortedContents());
cfs.getMemtablesPendingFlush().remove(Memtable.this);
}
else
{
//XXX: race condition: may miss an MT, but no double counts
cfs.getMemtablesPendingFlush().remove(Memtable.this);
cfs.addSSTable(writeSortedContents());
}
latch.countDown();
}
});

View File

@ -0,0 +1,129 @@
package org.apache.cassandra.db;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import org.apache.log4j.Logger;
import org.apache.cassandra.db.filter.QueryPath;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.AbstractCommutativeType;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.FBUtilities;
public class ReplicateOnWriteTask implements Runnable
{
private RowMutation mutation;
private static Logger logger = Logger.getLogger(ReplicateOnWriteTask.class);
public ReplicateOnWriteTask(RowMutation mutation)
{
this.mutation = mutation;
}
public void run()
{
// construct SliceByNamesReadCommand for CFs to repair
List<ReadCommand> readCommands = new LinkedList<ReadCommand>();
for (ColumnFamily columnFamily : mutation.getColumnFamilies())
{
// filter out non-repair CFs
if (!columnFamily.metadata().getReplicateOnWrite())
continue;
// CF type: regular
if (!columnFamily.isSuper())
{
QueryPath queryPath = new QueryPath(columnFamily.metadata().cfName);
ReadCommand readCommand = new SliceByNamesReadCommand(
mutation.getTable(),
mutation.key(),
queryPath,
columnFamily.getColumnNames()
);
readCommands.add(readCommand);
continue;
}
// CF type: super
for (IColumn superColumn : columnFamily.getSortedColumns())
{
QueryPath queryPath = new QueryPath(columnFamily.metadata().cfName, superColumn.name());
// construct set of sub-column names
Collection<IColumn> subColumns = superColumn.getSubColumns();
Collection<ByteBuffer> subColNames = new HashSet<ByteBuffer>(subColumns.size());
for (IColumn subCol : subColumns)
{
subColNames.add(subCol.name());
}
ReadCommand readCommand = new SliceByNamesReadCommand(
mutation.getTable(),
mutation.key(),
queryPath,
subColNames
);
readCommands.add(readCommand);
}
}
if (0 == readCommands.size())
return;
try
{
// send repair to non-local replicas
List<InetAddress> foreignReplicas = StorageService.instance.getLiveNaturalEndpoints(
mutation.getTable(),
mutation.key()
);
foreignReplicas.remove(FBUtilities.getLocalAddress()); // remove local replica
// create a repair RowMutation
RowMutation repairRowMutation = new RowMutation(mutation.getTable(), mutation.key());
for (ReadCommand readCommand : readCommands)
{
Table table = Table.open(readCommand.table);
Row row = readCommand.getRow(table);
AbstractType defaultValidator = row.cf.metadata().getDefaultValidator();
if (defaultValidator.isCommutative())
{
/**
* 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)
{
((AbstractCommutativeType)defaultValidator).cleanContext(row.cf, foreignNode);
}
}
repairRowMutation.add(row.cf);
}
// send repair to non-local replicas
for (InetAddress foreignReplica : foreignReplicas)
{
RowMutationMessage repairMessage = new RowMutationMessage(
repairRowMutation);
Message message = repairMessage.makeRowMutationMessage(StorageService.Verb.REPLICATE_ON_WRITE);
MessagingService.instance.sendOneWay(message, foreignReplica);
}
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,51 @@
/**
* 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;
import java.io.*;
import org.apache.cassandra.net.IVerbHandler;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.log4j.Logger;
public class ReplicateOnWriteVerbHandler implements IVerbHandler
{
private static Logger logger = Logger.getLogger(ReplicateOnWriteVerbHandler.class);
public void doVerb(Message message)
{
byte[] body = message.getMessageBody();
ByteArrayInputStream buffer = new ByteArrayInputStream(body);
try
{
RowMutationMessage rmMsg = RowMutationMessage.serializer().deserialize(new DataInputStream(buffer));
RowMutation rm = rmMsg.getRowMutation();
if (logger.isDebugEnabled())
logger.debug("repair-on-write for key " + FBUtilities.bytesToHex(rm.key()));
rm.apply();
}
catch (IOException e)
{
throw new IOError(e);
}
}
}

View File

@ -23,6 +23,9 @@ import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
@ -43,6 +46,9 @@ import org.apache.cassandra.thrift.Deletion;
import org.apache.cassandra.thrift.Mutation;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.AbstractCommutativeType;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
public class RowMutation
@ -316,7 +322,21 @@ public class RowMutation
rm.delete(new QueryPath(cfName, del.super_column), del.timestamp);
}
}
/**
* Update the context of all CounterColumns in this RowMutation
*/
public void updateCommutativeTypes(InetAddress node)
{
for (ColumnFamily cf : modifications_.values())
{
AbstractType defaultValidator = cf.metadata().getDefaultValidator();
if (!defaultValidator.isCommutative())
continue;
((AbstractCommutativeType)defaultValidator).update(cf, node);
}
}
static RowMutation fromBytes(byte[] raw) throws IOException
{
RowMutation rm = serializer_.deserialize(new DataInputStream(new ByteArrayInputStream(raw)));

View File

@ -31,6 +31,8 @@ import org.apache.cassandra.net.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.concurrent.Stage;
import org.apache.cassandra.concurrent.StageManager;
import org.apache.cassandra.net.*;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
@ -74,6 +76,10 @@ public class RowMutationVerbHandler implements IVerbHandler
if (logger_.isDebugEnabled())
logger_.debug(rm + " applied. Sending response to " + message.getMessageId() + "@" + message.getFrom());
MessagingService.instance.sendOneWay(responseMessage, message.getFrom());
// repair-on-write (remote message)
ReplicateOnWriteTask replicateOnWriteTask = new ReplicateOnWriteTask(rm);
StageManager.getStage(Stage.REPLICATE_ON_WRITE).execute(replicateOnWriteTask);
}
catch (IOException e)
{

View File

@ -99,6 +99,11 @@ public class SuperColumn implements IColumn, IColumnContainer
return columns_.values();
}
public Collection<IColumn> getSortedColumns()
{
return getSubColumns();
}
public IColumn getSubColumn(ByteBuffer columnName)
{
IColumn column = columns_.get(columnName);

View File

@ -0,0 +1,553 @@
/**
* 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.context;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.cassandra.db.DBConstants;
import org.apache.cassandra.db.context.IContext;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.commons.lang.ArrayUtils;
/**
* An implementation of a partitioned counter context.
*
* The data structure is:
* a vector of (node id, logical clock, count) tuples.
*
* On update, the node will:
* 1) increment the logical clock, and
* 2) update the count associated w/ its tuple.
*
* 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.
*
* NOTE: only a given node id may increment its associated count and
* care must be taken to ensure that tuples are correctly made consistent.
*/
public class CounterContext implements IContext
{
private static final int idLength;
private static final FBUtilities.ByteArrayWrapper idWrapper;
private static final int clockLength = DBConstants.longSize_;
private static final int countLength = DBConstants.longSize_;
private static final int stepLength; // length: id + logical clock + count
// lazy-load singleton
private static class LazyHolder
{
private static final CounterContext counterContext = new CounterContext();
}
static
{
byte[] id = FBUtilities.getLocalAddress().getAddress();
idLength = id.length;
idWrapper = new FBUtilities.ByteArrayWrapper(id);
stepLength = idLength + clockLength + countLength;
}
public static CounterContext instance()
{
return LazyHolder.counterContext;
}
/**
* Creates an initial counter context.
*
* @return an empty counter context.
*/
public byte[] create()
{
return new byte[0];
}
// write a tuple (node id, clock, count) at the front
protected static void writeElement(byte[] context, byte[] id, long clock, long count)
{
writeElementAtStepOffset(context, 0, id, clock, count);
}
// write a tuple (node id, clock, count) at step offset
protected static void writeElementAtStepOffset(byte[] context, int stepOffset, byte[] id, long clock, long count)
{
int offset = stepOffset * stepLength;
System.arraycopy(id, 0, context, offset, idLength);
FBUtilities.copyIntoBytes(context, offset + idLength, clock);
FBUtilities.copyIntoBytes(context, offset + idLength + clockLength, count);
}
public byte[] update(byte[] context, InetAddress node, long delta)
{
// calculate node id
byte[] nodeId = node.getAddress();
// look for this node id
for (int offset = 0; offset < context.length; offset += stepLength)
{
if (FBUtilities.compareByteSubArrays(nodeId, 0, context, offset, idLength) != 0)
continue;
// node id found: increment clock, update count; shift to front
long clock = FBUtilities.byteArrayToLong(context, offset + idLength);
long count = FBUtilities.byteArrayToLong(context, offset + idLength + clockLength);
System.arraycopy(
context,
0,
context,
stepLength,
offset);
writeElement(context, nodeId, clock + 1L, count + delta);
return context;
}
// node id not found: widen context
byte[] previous = context;
context = new byte[previous.length + stepLength];
writeElement(context, nodeId, 1L, delta);
System.arraycopy(
previous,
0,
context,
stepLength,
previous.length);
return context;
}
// swap bytes of step length in context
protected static void swapElement(byte[] context, int left, int right)
{
if (left == right) return;
byte temp;
for (int i = 0; i < stepLength; i++)
{
temp = context[left+i];
context[left+i] = context[right+i];
context[right+i] = temp;
}
}
// partition bytes of step length in context (for quicksort)
protected static int partitionElements(byte[] context, int left, int right, int pivotIndex)
{
int leftOffset = left * stepLength;
int rightOffset = right * stepLength;
int pivotOffset = pivotIndex * stepLength;
byte[] pivotValue = ArrayUtils.subarray(context, pivotOffset, pivotOffset + stepLength);
swapElement(context, pivotOffset, rightOffset);
int storeOffset = leftOffset;
for (int i = leftOffset; i < rightOffset; i += stepLength)
{
if (FBUtilities.compareByteSubArrays(context, i, pivotValue, 0, stepLength) <= 0)
{
swapElement(context, i, storeOffset);
storeOffset += stepLength;
}
}
swapElement(context, storeOffset, rightOffset);
return storeOffset / stepLength;
}
// quicksort helper
protected static void sortElementsByIdHelper(byte[] context, int left, int right)
{
if (right <= left) return;
int pivotIndex = (left + right) / 2;
int pivotIndexNew = partitionElements(context, left, right, pivotIndex);
sortElementsByIdHelper(context, left, pivotIndexNew - 1);
sortElementsByIdHelper(context, pivotIndexNew + 1, right);
}
// quicksort context by id
protected static byte[] sortElementsById(byte[] context)
{
assert 0 == (context.length % stepLength) : "context size is not correct.";
sortElementsByIdHelper(
context,
0,
(int)(context.length / stepLength) - 1);
return context;
}
/**
* Determine the count relationship between two contexts.
*
* EQUAL: Equal set of nodes and every count is equal.
* GREATER_THAN: Superset of nodes and every count is equal or greater than its corollary.
* 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).
*
* @param left
* counter context.
* @param right
* counter context.
* @return the ContextRelationship between the contexts.
*/
public ContextRelationship diff(byte[] left, byte[] right)
{
left = sortElementsById(left);
right = sortElementsById(right);
ContextRelationship relationship = ContextRelationship.EQUAL;
int leftIndex = 0;
int rightIndex = 0;
while (leftIndex < left.length && rightIndex < right.length)
{
// compare id bytes
int compareId = FBUtilities.compareByteSubArrays(left, leftIndex, right, rightIndex, idLength);
if (compareId == 0)
{
long leftClock = FBUtilities.byteArrayToLong(left, leftIndex + idLength);
long rightClock = FBUtilities.byteArrayToLong(right, rightIndex + idLength);
// advance indexes
leftIndex += stepLength;
rightIndex += stepLength;
// process clock comparisons
if (leftClock == rightClock)
{
continue;
}
else if (leftClock > rightClock)
{
if (relationship == ContextRelationship.EQUAL)
{
relationship = ContextRelationship.GREATER_THAN;
}
else if (relationship == ContextRelationship.GREATER_THAN)
{
continue;
}
else
{
// relationship == ContextRelationship.LESS_THAN
return ContextRelationship.DISJOINT;
}
}
else
// leftClock < rightClock
{
if (relationship == ContextRelationship.EQUAL)
{
relationship = ContextRelationship.LESS_THAN;
}
else if (relationship == ContextRelationship.GREATER_THAN)
{
return ContextRelationship.DISJOINT;
}
else
{
// relationship == ContextRelationship.LESS_THAN
continue;
}
}
}
else if (compareId > 0)
{
// only advance the right context
rightIndex += stepLength;
if (relationship == ContextRelationship.EQUAL)
{
relationship = ContextRelationship.LESS_THAN;
}
else if (relationship == ContextRelationship.GREATER_THAN)
{
return ContextRelationship.DISJOINT;
}
else
{
// relationship == ContextRelationship.LESS_THAN
continue;
}
}
else
{
// compareId < 0
// only advance the left context
leftIndex += stepLength;
if (relationship == ContextRelationship.EQUAL)
{
relationship = ContextRelationship.GREATER_THAN;
}
else if (relationship == ContextRelationship.GREATER_THAN)
{
continue;
}
else
// relationship == ContextRelationship.LESS_THAN
{
return ContextRelationship.DISJOINT;
}
}
}
// check final lengths
if (leftIndex < left.length)
{
if (relationship == ContextRelationship.EQUAL)
{
return ContextRelationship.GREATER_THAN;
}
else if (relationship == ContextRelationship.LESS_THAN)
{
return ContextRelationship.DISJOINT;
}
}
else if (rightIndex < right.length)
{
if (relationship == ContextRelationship.EQUAL)
{
return ContextRelationship.LESS_THAN;
}
else if (relationship == ContextRelationship.GREATER_THAN)
{
return ContextRelationship.DISJOINT;
}
}
return relationship;
}
private class CounterNode
{
public final long clock;
public final long count;
public CounterNode(long clock, long count)
{
this.clock = clock;
this.count = count;
}
public int compareClockTo(CounterNode o)
{
if (clock == o.clock)
{
return 0;
}
else if (clock > o.clock)
{
return 1;
}
// clock < o.clock
return -1;
}
@Override
public String toString()
{
return "(" + clock + "," + count + ")";
}
}
/**
* Return a context w/ an aggregated count for each node id.
*
* @param left
* counter context.
* @param right
* counter context.
*/
public byte[] merge(byte[] left, byte[] right)
{
// strategy:
// 1) map id -> (clock, count)
// a) local id: sum clocks, counts
// b) remote id: keep highest clock, count (reconcile)
// 2) create a context from sorted array
Map<FBUtilities.ByteArrayWrapper, CounterNode> contextsMap =
new HashMap<FBUtilities.ByteArrayWrapper, CounterNode>();
// map left context: id -> (clock, count)
for (int offset = 0; offset < left.length; offset += stepLength)
{
FBUtilities.ByteArrayWrapper id = new FBUtilities.ByteArrayWrapper(
ArrayUtils.subarray(left, offset, offset + idLength));
long clock = FBUtilities.byteArrayToLong(left, offset + idLength);
long count = FBUtilities.byteArrayToLong(left, offset + idLength + clockLength);
contextsMap.put(id, new CounterNode(clock, count));
}
// map right context: id -> (clock, count)
for (int offset = 0; offset < right.length; offset += stepLength)
{
FBUtilities.ByteArrayWrapper id = new FBUtilities.ByteArrayWrapper(
ArrayUtils.subarray(right, offset, offset + idLength));
long clock = FBUtilities.byteArrayToLong(right, offset + idLength);
long count = FBUtilities.byteArrayToLong(right, offset + idLength + clockLength);
if (!contextsMap.containsKey(id))
{
contextsMap.put(id, new CounterNode(clock, count));
continue;
}
CounterNode node = contextsMap.get(id);
// local id: sum clocks, counts
if (this.idWrapper.equals(id))
{
contextsMap.put(id, new CounterNode(
clock + node.clock,
count + node.count));
continue;
}
// remote id: keep highest clock and its count
if (node.clock < clock)
{
contextsMap.put(id, new CounterNode(clock, count));
}
}
// sort merged tuples
List<Map.Entry<FBUtilities.ByteArrayWrapper, CounterNode>> contextsList =
new ArrayList<Map.Entry<FBUtilities.ByteArrayWrapper, CounterNode>>(
contextsMap.entrySet());
Collections.sort(
contextsList,
new Comparator<Map.Entry<FBUtilities.ByteArrayWrapper, CounterNode>>()
{
public int compare(
Map.Entry<FBUtilities.ByteArrayWrapper, CounterNode> e1,
Map.Entry<FBUtilities.ByteArrayWrapper, CounterNode> e2)
{
// reversed
return e2.getValue().compareClockTo(e1.getValue());
}
});
// create merged context
int length = contextsList.size();
byte[] merged = new byte[length * stepLength];
for (int i = 0; i < length; i++)
{
Map.Entry<FBUtilities.ByteArrayWrapper, CounterNode> entry = contextsList.get(i);
writeElementAtStepOffset(
merged,
i,
entry.getKey().data,
entry.getValue().clock,
entry.getValue().count);
}
return merged;
}
/**
* Human-readable String from context.
*
* @param context
* version context.
* @return a human-readable String of the context.
*/
public String toString(byte[] context)
{
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int offset = 0; offset < context.length; offset += stepLength)
{
if (offset > 0)
{
sb.append(",");
}
sb.append("{");
try
{
InetAddress address = InetAddress.getByAddress(
ArrayUtils.subarray(context, offset, offset + idLength));
sb.append(address.getHostAddress());
}
catch (UnknownHostException uhe)
{
sb.append("?.?.?.?");
}
sb.append(", ");
sb.append(FBUtilities.byteArrayToLong(context, offset + idLength));
sb.append(", ");
sb.append(FBUtilities.byteArrayToLong(context, offset + idLength + clockLength));
sb.append("}");
}
sb.append("]");
return sb.toString();
}
// return an aggregated count across all node ids
public byte[] total(byte[] context)
{
long total = 0L;
for (int offset = 0; offset < context.length; offset += stepLength)
{
long count = FBUtilities.byteArrayToLong(context, offset + idLength + clockLength);
total += count;
}
return FBUtilities.toByteArray(total);
}
// remove the count for a given node id
public byte[] cleanNodeCounts(byte[] context, InetAddress node)
{
// calculate node id
byte[] nodeId = node.getAddress();
// look for this node id
for (int offset = 0; offset < context.length; offset += stepLength)
{
if (FBUtilities.compareByteSubArrays(context, offset, nodeId, 0, idLength) != 0)
continue;
// node id found: remove node count
byte[] truncatedContext = new byte[context.length - stepLength];
System.arraycopy(context, 0, truncatedContext, 0, offset);
System.arraycopy(
context,
offset + stepLength,
truncatedContext,
offset,
context.length - (offset + stepLength));
return truncatedContext;
}
return context;
}
}

View File

@ -0,0 +1,79 @@
/**
* 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.context;
import java.net.InetAddress;
import java.util.List;
/**
* An opaque commutative context.
*
* Maintains a byte[] context that represents a partitioned commutative value.
*/
public interface IContext
{
public static enum ContextRelationship
{
EQUAL,
GREATER_THAN,
LESS_THAN,
DISJOINT
};
/**
* Creates an initial context.
*
* @return the initial context.
*/
public byte[] create();
/**
* Determine the relationship between two contexts.
*
* EQUAL: Equal set of nodes and every count is equal.
* GREATER_THAN: Superset of nodes and every count is equal or greater than its corollary.
* 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.
*
* @param left
* context.
* @param right
* context.
* @return the ContextRelationship between the contexts.
*/
public ContextRelationship diff(byte[] left, byte[] right);
/**
* Return a context w/ an aggregated count for each node id.
*
* @param left
* context.
* @param right
* context.
*/
public byte[] merge(byte[] left, byte[] right);
/**
* Human-readable String from context.
*
* @param context
* context.
* @return a human-readable String of the context.
*/
public String toString(byte[] context);
}

View File

@ -0,0 +1,50 @@
/*
*
* 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.marshal;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import org.apache.cassandra.db.Column;
import org.apache.cassandra.db.IColumnContainer;
public abstract class AbstractCommutativeType extends AbstractType
{
public boolean isCommutative()
{
return true;
}
/**
* create commutative column
*/
public abstract Column createColumn(ByteBuffer name, ByteBuffer value, long timestamp);
/**
* update commutative columns for target node
*/
public abstract void update(IColumnContainer cc, InetAddress node);
/**
* remove target node from commutative columns
*/
public abstract void cleanContext(IColumnContainer cc, InetAddress node);
}

View File

@ -91,4 +91,9 @@ public abstract class AbstractType implements Comparator<ByteBuffer>
}
return builder.toString();
}
public boolean isCommutative()
{
return false;
}
}

View File

@ -0,0 +1,133 @@
/*
*
* 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.marshal;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import org.apache.cassandra.db.ColumnFamily;
import org.apache.cassandra.db.CounterColumn;
import org.apache.cassandra.db.Column;
import org.apache.cassandra.db.DeletedColumn;
import org.apache.cassandra.db.IColumn;
import org.apache.cassandra.db.IColumnContainer;
import org.apache.cassandra.db.SuperColumn;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
public class CounterColumnType extends AbstractCommutativeType
{
public static final CounterColumnType instance = new CounterColumnType();
CounterColumnType() {} // singleton
public int compare(ByteBuffer o1, ByteBuffer o2)
{
if (o1.remaining() == 0)
{
return o2.remaining() == 0 ? 0 : -1;
}
if (o2.remaining() == 0)
{
return 1;
}
return ByteBufferUtil.compareUnsigned(o1, o2);
}
public String getString(ByteBuffer bytes)
{
if (bytes.remaining() == 0)
{
return "";
}
if (bytes.remaining() != 8)
{
throw new MarshalException("A long is exactly 8 bytes");
}
return String.valueOf(bytes.getLong(bytes.position()+bytes.arrayOffset()));
}
/**
* create commutative column
*/
public Column createColumn(ByteBuffer name, ByteBuffer value, long timestamp)
{
return new CounterColumn(name, value, timestamp);
}
/**
* update commutative columns for target node
*/
public void update(IColumnContainer cc, InetAddress node)
{
for (IColumn column : cc.getSortedColumns())
{
if (column instanceof SuperColumn)
{
update((IColumnContainer)column, node);
continue;
}
if (column instanceof DeletedColumn)
continue;
((CounterColumn)column).update(node);
}
}
/**
* 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())
{
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 void validate(ByteBuffer bytes) throws MarshalException
{
if (bytes.remaining() != 8 && bytes.remaining() != 0)
throw new MarshalException(String.format("Expected 8 or 0 byte long (%d)", bytes.remaining()));
}
}

View File

@ -45,6 +45,7 @@ package org.apache.cassandra.dht;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.streaming.OperationType;
import org.apache.cassandra.streaming.StreamIn;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.SimpleCondition;
@ -107,8 +108,8 @@ public class BootStrapper
}
};
if (logger.isDebugEnabled())
logger.debug("Bootstrapping from " + source + " ranges " + StringUtils.join(ranges, ", "));
StreamIn.requestRanges(source, table, ranges, callback);
logger.debug("Bootstrapping from " + source + " ranges " + StringUtils.join(entry.getValue(), ", "));
StreamIn.requestRanges(source, table, entry.getValue(), callback, OperationType.BOOTSTRAP);
}
}

View File

@ -19,6 +19,8 @@
package org.apache.cassandra.io.sstable;
import java.io.*;
import java.net.InetAddress;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
@ -31,6 +33,9 @@ import java.util.Set;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.AbstractCommutativeType;
import org.apache.cassandra.db.ColumnFamily;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.DecoratedKey;
@ -43,6 +48,7 @@ import org.apache.cassandra.io.util.FileMark;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.io.util.SegmentedFile;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.streaming.OperationType;
import org.apache.cassandra.utils.BloomFilter;
import org.apache.cassandra.utils.EstimatedHistogram;
import org.apache.cassandra.utils.FBUtilities;
@ -221,7 +227,7 @@ public class SSTableWriter extends SSTable
return dataFile.getFilePointer();
}
public static Builder createBuilder(Descriptor desc)
public static Builder createBuilder(Descriptor desc, OperationType type)
{
if (!desc.isLatestVersion)
// TODO: streaming between different versions will fail: need support for
@ -229,7 +235,7 @@ public class SSTableWriter extends SSTable
throw new RuntimeException(String.format("Cannot recover SSTable with version %s (current version %s).",
desc.version, Descriptor.CURRENT_VERSION));
return new Builder(desc);
return new Builder(desc, type);
}
/**
@ -240,16 +246,18 @@ public class SSTableWriter extends SSTable
{
private final Descriptor desc;
public final ColumnFamilyStore cfs;
private BufferedRandomAccessFile dfile;
private final RowIndexer indexer;
public Builder(Descriptor desc)
public Builder(Descriptor desc, OperationType type)
{
this.desc = desc;
cfs = Table.open(desc.ksname).getColumnFamilyStore(desc.cfname);
try
{
dfile = new BufferedRandomAccessFile(desc.filenameFor(SSTable.COMPONENT_DATA), "r", 8 * 1024 * 1024);
if (OperationType.AES == type && cfs.metadata.getDefaultValidator().isCommutative())
indexer = new AESCommutativeRowIndexer(desc, cfs.metadata);
else
indexer = new RowIndexer(desc, cfs.metadata);
}
catch (IOException e)
{
@ -266,47 +274,79 @@ public class SSTableWriter extends SSTable
assert !ifile.exists();
assert !ffile.exists();
EstimatedHistogram rowSizes = SSTable.defaultRowHistogram();
EstimatedHistogram columnCounts = SSTable.defaultColumnHistogram();
long estimatedRows = indexer.prepareIndexing();
IndexWriter iwriter;
// build the index and filter
long rows = indexer.index();
logger.debug("estimated row count was {} of real count", ((double)estimatedRows) / rows);
return SSTableReader.open(rename(desc, SSTable.componentsFor(desc)));
}
public long getTotalBytes()
{
try
{
return indexer.dfile.length();
}
catch (IOException e)
{
throw new IOError(e);
}
}
public long getBytesRead()
{
return indexer.dfile.getFilePointer();
}
public String getTaskType()
{
return "SSTable rebuild";
}
}
static class RowIndexer
{
protected final Descriptor desc;
public final BufferedRandomAccessFile dfile;
protected IndexWriter iwriter;
protected CFMetaData metadata;
RowIndexer(Descriptor desc, CFMetaData metadata) throws IOException
{
this(desc, new BufferedRandomAccessFile(desc.filenameFor(SSTable.COMPONENT_DATA), "r", 8 * 1024 * 1024), metadata);
}
protected RowIndexer(Descriptor desc, BufferedRandomAccessFile dfile, CFMetaData metadata) throws IOException
{
this.desc = desc;
this.dfile = dfile;
this.metadata = metadata;
}
long prepareIndexing() throws IOException
{
long estimatedRows;
try
{
estimatedRows = SSTable.estimateRowsFromData(desc, dfile);
iwriter = new IndexWriter(desc, StorageService.getPartitioner(), estimatedRows);
return estimatedRows;
}
catch(IOException e)
{
dfile.close();
throw e;
}
}
// build the index and filter
long rows = 0;
long index() throws IOException
{
try
{
DecoratedKey key;
long rowPosition = 0;
while (rowPosition < dfile.length())
{
key = SSTableReader.decodeKey(StorageService.getPartitioner(), desc, FBUtilities.readShortByteArray(dfile));
iwriter.afterAppend(key, rowPosition);
long dataSize = SSTableReader.readRowSize(dfile, desc);
rowPosition = dfile.getFilePointer() + dataSize; // next row
IndexHelper.skipBloomFilter(dfile);
IndexHelper.skipIndex(dfile);
ColumnFamily.serializer().deserializeFromSSTableNoColumns(ColumnFamily.create(cfs.metadata), dfile);
rowSizes.add(dataSize);
columnCounts.add(dfile.readInt());
dfile.seek(rowPosition);
rows++;
}
writeStatistics(desc, rowSizes, columnCounts);
return doIndexing();
}
finally
{
@ -320,31 +360,113 @@ public class SSTableWriter extends SSTable
throw new IOError(e);
}
}
logger.debug("estimated row count was %s of real count", ((double)estimatedRows) / rows);
return SSTableReader.open(rename(desc, SSTable.componentsFor(desc)));
}
public long getTotalBytes()
protected long doIndexing() throws IOException
{
try
EstimatedHistogram rowSizes = SSTable.defaultRowHistogram();
EstimatedHistogram columnCounts = SSTable.defaultColumnHistogram();
long rows = 0;
DecoratedKey key;
long rowPosition = 0;
while (rowPosition < dfile.length())
{
return dfile.length();
// read key
key = SSTableReader.decodeKey(StorageService.getPartitioner(), desc, FBUtilities.readShortByteArray(dfile));
iwriter.afterAppend(key, rowPosition);
// seek to next key
long dataSize = SSTableReader.readRowSize(dfile, desc);
rowPosition = dfile.getFilePointer() + dataSize;
IndexHelper.skipBloomFilter(dfile);
IndexHelper.skipIndex(dfile);
ColumnFamily.serializer().deserializeFromSSTableNoColumns(ColumnFamily.create(metadata), dfile);
rowSizes.add(dataSize);
columnCounts.add(dfile.readInt());
dfile.seek(rowPosition);
rows++;
}
catch (IOException e)
return rows;
}
}
static class AESCommutativeRowIndexer extends RowIndexer
{
AESCommutativeRowIndexer(Descriptor desc, CFMetaData metadata) throws IOException
{
super(desc, new BufferedRandomAccessFile(desc.filenameFor(SSTable.COMPONENT_DATA), "rw", 8 * 1024 * 1024), metadata);
}
@Override
protected long doIndexing() throws IOException
{
EstimatedHistogram rowSizes = SSTable.defaultRowHistogram();
EstimatedHistogram columnCounts = SSTable.defaultColumnHistogram();
long rows = 0L;
ByteBuffer diskKey;
DecoratedKey key;
long readRowPosition = 0L;
long writeRowPosition = 0L;
while (readRowPosition < dfile.length())
{
throw new IOError(e);
// read key
dfile.seek(readRowPosition);
diskKey = FBUtilities.readShortByteArray(dfile);
// skip data size, bloom filter, column index
long dataSize = SSTableReader.readRowSize(dfile, desc);
dfile.skipBytes(dfile.readInt());
dfile.skipBytes(dfile.readInt());
// deserialize CF
ColumnFamily cf = ColumnFamily.create(desc.ksname, desc.cfname);
ColumnFamily.serializer().deserializeFromSSTableNoColumns(cf, dfile);
ColumnFamily.serializer().deserializeColumns(dfile, cf);
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);
FBUtilities.writeShortByteArray(diskKey, dfile);
// write data size; serialize CF w/ bloom filter, column index
long writeSizePosition = dfile.getFilePointer();
dfile.writeLong(-1L);
ColumnFamily.serializer().serializeWithIndexes(cf, dfile);
long writeEndPosition = dfile.getFilePointer();
dfile.seek(writeSizePosition);
dfile.writeLong(writeEndPosition - (writeSizePosition + 8L));
writeRowPosition = writeEndPosition;
rows++;
dfile.sync();
}
}
public long getBytesRead()
{
return dfile.getFilePointer();
}
if (writeRowPosition != readRowPosition)
{
// truncate file to new, reduced length
dfile.setLength(writeRowPosition);
dfile.sync();
}
public String getTaskType()
{
return "SSTable rebuild";
return rows;
}
}

View File

@ -42,6 +42,7 @@ import org.apache.cassandra.dht.Token;
import org.apache.cassandra.io.AbstractCompactedRow;
import org.apache.cassandra.io.ICompactSerializer;
import org.apache.cassandra.io.sstable.SSTableReader;
import org.apache.cassandra.streaming.OperationType;
import org.apache.cassandra.streaming.StreamIn;
import org.apache.cassandra.streaming.StreamOut;
import org.apache.cassandra.net.CompactEndpointSerializationHelper;
@ -502,9 +503,9 @@ public class AntiEntropyService
Callback callback = new Callback();
// send ranges to the remote node
StreamOutSession outsession = StreamOutSession.create(request.cf.left, request.endpoint, callback);
StreamOut.transferSSTables(outsession, sstables, ranges);
StreamOut.transferSSTables(outsession, sstables, ranges, OperationType.AES);
// request ranges from the remote node
StreamIn.requestRanges(request.endpoint, request.cf.left, ranges, callback);
StreamIn.requestRanges(request.endpoint, request.cf.left, ranges, callback, OperationType.AES);
}
catch(Exception e)
{

View File

@ -25,6 +25,8 @@ import java.io.IOException;
import java.util.*;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.AbstractCommutativeType;
import java.net.InetAddress;
import java.nio.ByteBuffer;
@ -52,6 +54,13 @@ public class ReadResponseResolver implements IResponseResolver<Row>
this.table = table;
this.key = StorageService.getPartitioner().decorateKey(key);
}
private void checkDigest(DecoratedKey key, ByteBuffer digest, ByteBuffer resultDigest) throws DigestMismatchException
{
if (resultDigest.equals(digest))
return;
throw new DigestMismatchException(key, digest, resultDigest);
}
/*
* This method for resolving read data should look at the timestamps of each
@ -82,21 +91,25 @@ public class ReadResponseResolver implements IResponseResolver<Row>
Message message = entry.getKey();
if (result.isDigestQuery())
{
if (digest == null)
{
digest = result.digest();
}
else
{
ByteBuffer digest2 = result.digest();
if (!digest.equals(digest2))
throw new DigestMismatchException(key, digest, digest2);
}
if (digest != null)
checkDigest(key, digest, result.digest());
digest = result.digest();
}
else
{
versions.add(result.row().cf);
endpoints.add(message.getFrom());
ColumnFamily cf = result.row().cf;
InetAddress from = message.getFrom();
if(cf != null) {
AbstractType defaultValidator = cf.metadata().getDefaultValidator();
if (!FBUtilities.getLocalAddress().equals(from) && cf != null && defaultValidator.isCommutative())
{
cf = cf.cloneMe();
((AbstractCommutativeType)defaultValidator).cleanContext(cf, FBUtilities.getLocalAddress());
}
}
versions.add(cf);
endpoints.add(from);
}
}
@ -107,9 +120,7 @@ public class ReadResponseResolver implements IResponseResolver<Row>
for (ColumnFamily cf : versions)
{
ByteBuffer digest2 = ColumnFamily.digest(cf);
if (!digest.equals(digest2))
throw new DigestMismatchException(key, digest, digest2);
checkDigest(key, digest, ColumnFamily.digest(cf));
}
if (logger_.isDebugEnabled())
logger_.debug("digests verified");
@ -147,6 +158,14 @@ public class ReadResponseResolver implements IResponseResolver<Row>
// create and send the row mutation message based on the diff
RowMutation rowMutation = new RowMutation(table, key.key);
AbstractType defaultValidator = diffCf.metadata().getDefaultValidator();
if (defaultValidator.isCommutative())
((AbstractCommutativeType)defaultValidator).cleanContext(diffCf, endpoints.get(i));
if (diffCf.getColumnsMap().isEmpty() && !diffCf.isMarkedForDelete())
continue;
rowMutation.add(diffCf);
RowMutationMessage rowMutationMessage = new RowMutationMessage(rowMutation);
Message repairMessage;
@ -170,7 +189,7 @@ public class ReadResponseResolver implements IResponseResolver<Row>
{
if (cf != null)
{
resolved = cf.cloneMe();
resolved = cf.cloneMeShallow();
break;
}
}

View File

@ -41,6 +41,8 @@ import org.apache.cassandra.concurrent.StageManager;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.AbstractCommutativeType;
import org.apache.cassandra.dht.*;
import org.apache.cassandra.gms.Gossiper;
import org.apache.cassandra.locator.AbstractReplicationStrategy;
@ -117,6 +119,10 @@ public class StorageProxy implements StorageProxyMBean
responseHandlers.add(responseHandler);
Message unhintedMessage = null;
//XXX: if commutative value, only allow CL.ONE write
updateDestinationForCommutativeTypes(consistency_level, rm, hintedEndpoints);
for (Map.Entry<InetAddress, Collection<InetAddress>> entry : hintedEndpoints.asMap().entrySet())
{
InetAddress destination = entry.getKey();
@ -124,6 +130,9 @@ public class StorageProxy implements StorageProxyMBean
if (targets.size() == 1 && targets.iterator().next().equals(destination))
{
// only non-hinted writes are supported
rm.updateCommutativeTypes(destination);
// unhinted writes
if (destination.equals(FBUtilities.getLocalAddress()))
{
@ -180,6 +189,40 @@ public class StorageProxy implements StorageProxyMBean
}
/**
* Update destination endpoints depending on the clock type.
*/
private static void updateDestinationForCommutativeTypes(ConsistencyLevel consistency_level, RowMutation rm,
Multimap<InetAddress, InetAddress> destinationEndpoints)
{
AbstractType defaultValidator = rm.getColumnFamilies().iterator().next().metadata().getDefaultValidator();
if (!defaultValidator.isCommutative())
return;
InetAddress randomDestination = pickRandomDestination(destinationEndpoints);
destinationEndpoints.clear();
destinationEndpoints.put(randomDestination, randomDestination);
}
/**
* @param endpoints potential destinations.
* @return one destination randomly chosen from the endpoints unless localhost is in the map, then that is returned.
*/
private static InetAddress pickRandomDestination(Multimap<InetAddress, InetAddress> endpoints)
{
Set<InetAddress> destinationSet = endpoints.keySet();
if (destinationSet.contains(FBUtilities.getLocalAddress()))
{
return FBUtilities.getLocalAddress();
}
else
{
InetAddress[] destinations = destinationSet.toArray(new InetAddress[0]);
return destinations[random.nextInt(destinations.length)];
}
}
private static void addHintHeader(Message message, InetAddress target) throws IOException
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
@ -203,6 +246,10 @@ public class StorageProxy implements StorageProxyMBean
{
rm.deepCopy().apply();
responseHandler.response(null);
// repair-on-write (local message)
ReplicateOnWriteTask replicateOnWriteTask = new ReplicateOnWriteTask(rm);
StageManager.getStage(Stage.REPLICATE_ON_WRITE).execute(replicateOnWriteTask);
}
};
StageManager.getStage(Stage.MUTATION).execute(runnable);

View File

@ -69,6 +69,7 @@ import org.apache.cassandra.db.HintedHandOffManager;
import org.apache.cassandra.db.ReadCommand;
import org.apache.cassandra.db.ReadRepairVerbHandler;
import org.apache.cassandra.db.ReadVerbHandler;
import org.apache.cassandra.db.ReplicateOnWriteVerbHandler;
import org.apache.cassandra.db.Row;
import org.apache.cassandra.db.RowMutationVerbHandler;
import org.apache.cassandra.db.SchemaCheckVerbHandler;
@ -101,6 +102,7 @@ import org.apache.cassandra.net.Message;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.net.ResponseVerbHandler;
import org.apache.cassandra.service.AntiEntropyService.TreeRequestVerbHandler;
import org.apache.cassandra.streaming.OperationType;
import org.apache.cassandra.streaming.ReplicationFinishedVerbHandler;
import org.apache.cassandra.streaming.StreamIn;
import org.apache.cassandra.streaming.StreamOut;
@ -165,6 +167,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
INDEX_SCAN,
REPLICATION_FINISHED,
INTERNAL_RESPONSE, // responses to internal calls
REPLICATE_ON_WRITE,
;
// remember to add new verbs at the end, since we serialize by ordinal
}
@ -193,6 +196,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
put(Verb.INDEX_SCAN, Stage.READ);
put(Verb.REPLICATION_FINISHED, Stage.MISC);
put(Verb.INTERNAL_RESPONSE, Stage.INTERNAL_RESPONSE);
put(Verb.REPLICATE_ON_WRITE, Stage.REPLICATE_ON_WRITE);
}};
@ -278,6 +282,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
MessagingService.instance.registerVerbHandlers(Verb.MUTATION, new RowMutationVerbHandler());
MessagingService.instance.registerVerbHandlers(Verb.READ_REPAIR, new ReadRepairVerbHandler());
MessagingService.instance.registerVerbHandlers(Verb.READ, new ReadVerbHandler());
MessagingService.instance.registerVerbHandlers(Verb.REPLICATE_ON_WRITE, new ReplicateOnWriteVerbHandler());
MessagingService.instance.registerVerbHandlers(Verb.RANGE_SLICE, new RangeSliceVerbHandler());
MessagingService.instance.registerVerbHandlers(Verb.INDEX_SCAN, new IndexScanVerbHandler());
// see BootStrapper for a summary of how the bootstrap verbs interact
@ -1004,7 +1009,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
};
if (logger_.isDebugEnabled())
logger_.debug("Requesting from " + source + " ranges " + StringUtils.join(ranges, ", "));
StreamIn.requestRanges(source, table, ranges, callback);
StreamIn.requestRanges(source, table, ranges, callback, OperationType.RESTORE_REPLICA_COUNT);
}
}
}
@ -1608,7 +1613,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
public void run()
{
// TODO each call to transferRanges re-flushes, this is potentially a lot of waste
StreamOut.transferRanges(newEndpoint, table, Arrays.asList(range), callback);
StreamOut.transferRanges(newEndpoint, table, Arrays.asList(range), callback, OperationType.UNBOOTSTRAP);
}
});
}
@ -1979,6 +1984,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
rcf.comment = cfm.getComment();
rcf.keys_cached = cfm.getKeyCacheSize();
rcf.read_repair_chance = cfm.getReadRepairChance();
rcf.replicate_on_write = cfm.getReplicateOnWrite();
rcf.gc_grace_seconds = cfm.getGcGraceSeconds();
rcf.rows_cached = cfm.getRowCacheSize();
rcf.column_metadata = new RawColumnDefinition[cfm.getColumn_metadata().size()];

View File

@ -0,0 +1,31 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.streaming;
/**
* Streaming operation type.
*/
public enum OperationType
{
AES,
BOOTSTRAP,
UNBOOTSTRAP,
RESTORE_REPLICA_COUNT;
}

View File

@ -52,20 +52,23 @@ public class PendingFile
public final Descriptor desc;
public final String component;
public final List<Pair<Long,Long>> sections;
public final OperationType type;
public final long size;
public long progress;
public PendingFile(Descriptor desc, PendingFile pf)
{
this(null, desc, pf.component, pf.sections);
this(null, desc, pf.component, pf.sections, pf.type);
}
public PendingFile(SSTable sstable, Descriptor desc, String component, List<Pair<Long,Long>> sections)
public PendingFile(SSTable sstable, Descriptor desc, String component, List<Pair<Long,Long>> sections, OperationType type)
{
this.sstable = sstable;
this.desc = desc;
this.component = component;
this.sections = sections;
this.type = type;
long tempSize = 0;
for(Pair<Long,Long> section : sections)
{
@ -115,6 +118,7 @@ public class PendingFile
{
dos.writeLong(section.left); dos.writeLong(section.right);
}
dos.writeUTF(sc.type.name());
}
public PendingFile deserialize(DataInputStream dis) throws IOException
@ -129,7 +133,8 @@ public class PendingFile
List<Pair<Long,Long>> sections = new ArrayList<Pair<Long,Long>>(count);
for (int i = 0; i < count; i++)
sections.add(new Pair<Long,Long>(Long.valueOf(dis.readLong()), Long.valueOf(dis.readLong())));
return new PendingFile(null, desc, component, sections);
OperationType type = OperationType.valueOf(dis.readUTF());
return new PendingFile(null, desc, component, sections, type);
}
}
}

View File

@ -49,19 +49,19 @@ public class StreamIn
/**
* Request ranges to be transferred from source to local node
*/
public static void requestRanges(InetAddress source, String tableName, Collection<Range> ranges)
public static void requestRanges(InetAddress source, String tableName, Collection<Range> ranges, OperationType type)
{
requestRanges(source, tableName, ranges, null);
requestRanges(source, tableName, ranges, null, type);
}
public static void requestRanges(InetAddress source, String tableName, Collection<Range> ranges, Runnable callback)
public static void requestRanges(InetAddress source, String tableName, Collection<Range> ranges, Runnable callback, OperationType type)
{
assert ranges.size() > 0;
if (logger.isDebugEnabled())
logger.debug("Requesting from {} ranges {}", source, StringUtils.join(ranges, ", "));
StreamInSession session = StreamInSession.create(source, callback);
Message message = new StreamRequestMessage(FBUtilities.getLocalAddress(), ranges, tableName, session.getSessionId()).makeMessage();
Message message = new StreamRequestMessage(FBUtilities.getLocalAddress(), ranges, tableName, session.getSessionId(), type).makeMessage();
MessagingService.instance.sendOneWay(message, source);
}

View File

@ -107,7 +107,7 @@ public class StreamInSession
if (logger.isDebugEnabled())
logger.debug("Finished {}. Sending ack to {}", remoteFile, this);
Future future = CompactionManager.instance.submitSSTableBuild(localFile.desc);
Future future = CompactionManager.instance.submitSSTableBuild(localFile.desc, remoteFile.type);
buildFutures.add(future);
files.remove(remoteFile);

View File

@ -65,7 +65,7 @@ public class StreamOut
/**
* Split out files for all tables on disk locally for each range and then stream them to the target endpoint.
*/
public static void transferRanges(InetAddress target, String tableName, Collection<Range> ranges, Runnable callback)
public static void transferRanges(InetAddress target, String tableName, Collection<Range> ranges, Runnable callback, OperationType type)
{
assert ranges.size() > 0;
@ -79,7 +79,7 @@ public class StreamOut
{
Table table = flushSSTable(tableName);
// send the matching portion of every sstable in the keyspace
transferSSTables(session, table.getAllSSTables(), ranges);
transferSSTables(session, table.getAllSSTables(), ranges, type);
}
catch (IOException e)
{
@ -117,7 +117,7 @@ public class StreamOut
/**
* Split out files for all tables on disk locally for each range and then stream them to the target endpoint.
*/
public static void transferRangesForRequest(StreamOutSession session, Collection<Range> ranges)
public static void transferRangesForRequest(StreamOutSession session, Collection<Range> ranges, OperationType type)
{
assert ranges.size() > 0;
@ -128,7 +128,7 @@ public class StreamOut
{
Table table = flushSSTable(session.table);
// send the matching portion of every sstable in the keyspace
List<PendingFile> pending = createPendingFiles(table.getAllSSTables(), ranges);
List<PendingFile> pending = createPendingFiles(table.getAllSSTables(), ranges, type);
session.addFilesToStream(pending);
session.begin();
}
@ -141,9 +141,9 @@ public class StreamOut
/**
* Transfers matching portions of a group of sstables from a single table to the target endpoint.
*/
public static void transferSSTables(StreamOutSession session, Collection<SSTableReader> sstables, Collection<Range> ranges) throws IOException
public static void transferSSTables(StreamOutSession session, Collection<SSTableReader> sstables, Collection<Range> ranges, OperationType type) throws IOException
{
List<PendingFile> pending = createPendingFiles(sstables, ranges);
List<PendingFile> pending = createPendingFiles(sstables, ranges, type);
if (pending.size() > 0)
{
@ -157,7 +157,7 @@ public class StreamOut
}
// called prior to sending anything.
private static List<PendingFile> createPendingFiles(Collection<SSTableReader> sstables, Collection<Range> ranges)
private static List<PendingFile> createPendingFiles(Collection<SSTableReader> sstables, Collection<Range> ranges, OperationType type)
{
List<PendingFile> pending = new ArrayList<PendingFile>();
for (SSTableReader sstable : sstables)
@ -166,7 +166,7 @@ public class StreamOut
List<Pair<Long,Long>> sections = sstable.getPositionsForRanges(ranges);
if (sections.isEmpty())
continue;
pending.add(new PendingFile(sstable, desc, SSTable.COMPONENT_DATA, sections));
pending.add(new PendingFile(sstable, desc, SSTable.COMPONENT_DATA, sections, type));
}
logger.info("Stream context metadata {}, {} sstables.", pending, sstables.size());
return pending;

View File

@ -66,13 +66,15 @@ class StreamRequestMessage
// if these are specified, file shoud not be.
protected final Collection<Range> ranges;
protected final String table;
protected final OperationType type;
StreamRequestMessage(InetAddress target, Collection<Range> ranges, String table, long sessionId)
StreamRequestMessage(InetAddress target, Collection<Range> ranges, String table, long sessionId, OperationType type)
{
this.target = target;
this.ranges = ranges;
this.table = table;
this.sessionId = sessionId;
this.type = type;
file = null;
}
@ -81,6 +83,7 @@ class StreamRequestMessage
this.target = target;
this.file = file;
this.sessionId = sessionId;
this.type = file.type;
ranges = null;
table = null;
}
@ -114,6 +117,7 @@ class StreamRequestMessage
sb.append(range);
sb.append(" ");
}
sb.append(type);
}
else
{
@ -142,6 +146,7 @@ class StreamRequestMessage
{
AbstractBounds.serializer().serialize(range, dos);
}
dos.writeUTF(srm.type.name());
}
}
@ -164,7 +169,8 @@ class StreamRequestMessage
{
ranges.add((Range) AbstractBounds.serializer().deserialize(dis));
}
return new StreamRequestMessage(target, ranges, table, sessionId);
OperationType type = OperationType.valueOf(dis.readUTF());
return new StreamRequestMessage(target, ranges, table, sessionId, type);
}
}
}

View File

@ -52,7 +52,7 @@ public class StreamRequestVerbHandler implements IVerbHandler
logger.debug(srm.toString());
StreamOutSession session = StreamOutSession.create(srm.table, message.getFrom(), srm.sessionId);
StreamOut.transferRangesForRequest(session, srm.ranges);
StreamOut.transferRangesForRequest(session, srm.ranges, srm.type);
}
catch (IOException ex)
{

View File

@ -28,6 +28,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@ -72,6 +73,7 @@ import org.apache.cassandra.scheduler.IRequestScheduler;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.thrift.TException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -269,7 +271,6 @@ public class CassandraServer implements Cassandra.Iface
logger.debug("multiget_slice");
state().hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
return multigetSliceInternal(state().getKeyspace(), keys, column_parent, predicate, consistency_level);
}
@ -301,11 +302,9 @@ public class CassandraServer implements Cassandra.Iface
return getSlice(commands, consistency_level);
}
public ColumnOrSuperColumn get(ByteBuffer key, ColumnPath column_path, ConsistencyLevel consistency_level)
private ColumnOrSuperColumn internal_get(ByteBuffer key, ColumnPath column_path, ConsistencyLevel consistency_level)
throws InvalidRequestException, NotFoundException, UnavailableException, TimedOutException
{
logger.debug("get");
state().hasColumnFamilyAccess(column_path.column_family, Permission.READ);
String keyspace = state().getKeyspace();
@ -329,6 +328,14 @@ public class CassandraServer implements Cassandra.Iface
return tcolumns.get(0);
}
public ColumnOrSuperColumn get(ByteBuffer key, ColumnPath column_path, ConsistencyLevel consistency_level)
throws InvalidRequestException, NotFoundException, UnavailableException, TimedOutException
{
logger.debug("get");
return internal_get(key, column_path, consistency_level);
}
public int get_count(ByteBuffer key, ColumnParent column_parent, SlicePredicate predicate, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TimedOutException
{
@ -356,11 +363,9 @@ public class CassandraServer implements Cassandra.Iface
return counts;
}
public void insert(ByteBuffer key, ColumnParent column_parent, Column column, ConsistencyLevel consistency_level)
private void internal_insert(ByteBuffer key, ColumnParent column_parent, Column column, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TimedOutException
{
logger.debug("insert");
state().hasColumnFamilyAccess(column_parent.column_family, Permission.WRITE);
ThriftValidation.validateKey(key);
@ -379,11 +384,17 @@ public class CassandraServer implements Cassandra.Iface
doInsert(consistency_level, Arrays.asList(rm));
}
public void batch_mutate(Map<ByteBuffer,Map<String,List<Mutation>>> mutation_map, ConsistencyLevel consistency_level)
public void insert(ByteBuffer key, ColumnParent column_parent, Column column, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TimedOutException
{
logger.debug("insert");
internal_insert(key, column_parent, column, consistency_level);
}
private void internal_batch_mutate(Map<ByteBuffer,Map<String,List<Mutation>>> mutation_map, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TimedOutException
{
logger.debug("batch_mutate");
List<String> cfamsSeen = new ArrayList<String>();
List<RowMutation> rowMutations = new ArrayList<RowMutation>();
@ -415,11 +426,17 @@ public class CassandraServer implements Cassandra.Iface
doInsert(consistency_level, rowMutations);
}
public void remove(ByteBuffer key, ColumnPath column_path, long timestamp, ConsistencyLevel consistency_level)
public void batch_mutate(Map<ByteBuffer,Map<String,List<Mutation>>> mutation_map, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TimedOutException
{
logger.debug("remove");
logger.debug("batch_mutate");
internal_batch_mutate(mutation_map, consistency_level);
}
private void internal_remove(ByteBuffer key, ColumnPath column_path, long timestamp, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TimedOutException
{
state().hasColumnFamilyAccess(column_path.column_family, Permission.WRITE);
ThriftValidation.validateKey(key);
@ -431,6 +448,14 @@ public class CassandraServer implements Cassandra.Iface
doInsert(consistency_level, Arrays.asList(rm));
}
public void remove(ByteBuffer key, ColumnPath column_path, long timestamp, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TimedOutException
{
logger.debug("remove");
internal_remove(key, column_path, timestamp, consistency_level);
}
private void doInsert(ConsistencyLevel consistency_level, List<RowMutation> mutations) throws UnavailableException, TimedOutException
{
try
@ -898,6 +923,7 @@ public class CassandraServer implements Cassandra.Iface
cf_def.row_cache_size,
cf_def.key_cache_size,
cf_def.read_repair_chance,
cf_def.replicate_on_write,
cf_def.isSetGc_grace_seconds() ? cf_def.gc_grace_seconds : CFMetaData.DEFAULT_GC_GRACE_SECONDS,
DatabaseDescriptor.getComparator(cf_def.default_validation_class),
cf_def.isSetMin_compaction_threshold() ? cf_def.min_compaction_threshold : CFMetaData.DEFAULT_MIN_COMPACTION_THRESHOLD,
@ -946,5 +972,190 @@ public class CassandraServer implements Cassandra.Iface
return StorageProxy.describeSchemaVersions();
}
// counter methods
private Column getCounterColumn(CounterColumn column)
{
return new Column(column.name, FBUtilities.toByteBuffer(column.value), System.currentTimeMillis());
}
public void add(ByteBuffer key, ColumnParent column_parent, CounterColumn column, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TimedOutException, TException
{
logger.debug("add");
if (ConsistencyLevel.ONE != consistency_level)
{
throw new InvalidRequestException("Commutative CFs only support ConsistencyLevel.ONE");
}
String keyspace = state().getKeyspace();
ThriftValidation.validateCommutative(keyspace, column_parent.column_family);
internal_insert(key, column_parent, getCounterColumn(column), consistency_level);
}
private Mutation getMutation(CounterMutation counterMutation)
{
Mutation mutation = new Mutation();
if (counterMutation.isSetCounter())
{
Counter counter = counterMutation.counter;
ColumnOrSuperColumn cosc = new ColumnOrSuperColumn();
if (counter.isSetColumn())
{
Column c = new Column(counter.column.name, FBUtilities.toByteBuffer(counter.column.value), System.currentTimeMillis());
cosc.setColumn(c);
}
if (counter.isSetSuper_column())
{
List<Column> subcolumns = new ArrayList<Column>(counter.super_column.columns.size());
for (CounterColumn subcol : counter.super_column.columns)
{
subcolumns.add(new Column(subcol.name, FBUtilities.toByteBuffer(subcol.value), System.currentTimeMillis()));
}
SuperColumn sc = new SuperColumn(counter.super_column.name, subcolumns);
cosc.setSuper_column(sc);
}
mutation.setColumn_or_supercolumn(cosc);
}
if (counterMutation.isSetDeletion())
{
Deletion deletion = new Deletion(System.currentTimeMillis());
if (counterMutation.deletion.isSetSuper_column())
deletion.setSuper_column(counterMutation.deletion.super_column);
if (counterMutation.deletion.isSetPredicate())
deletion.setPredicate(counterMutation.deletion.predicate);
mutation.setDeletion(deletion);
}
return mutation;
}
public void batch_add(Map<ByteBuffer, Map<String, List<CounterMutation>>> updateMap, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TimedOutException, TException
{
logger.debug("batch_add");
if (ConsistencyLevel.ONE != consistency_level)
{
throw new InvalidRequestException("Commutative CFs only support ConsistencyLevel.ONE");
}
String keyspace = state().getKeyspace();
Map<ByteBuffer,Map<String,List<Mutation>>> mutation_map = new HashMap<ByteBuffer,Map<String,List<Mutation>>>();
for (Entry<ByteBuffer, Map<String, List<CounterMutation>>> entry : updateMap.entrySet())
{
Map<String, List<Mutation>> valueMap = new HashMap<String, List<Mutation>>(entry.getValue().size());
for (Entry<String, List<CounterMutation>> innerEntry : entry.getValue().entrySet())
{
ThriftValidation.validateCommutative(keyspace, innerEntry.getKey());
List<Mutation> mutations = new ArrayList<Mutation>(innerEntry.getValue().size());
for (CounterMutation cm : innerEntry.getValue())
{
mutations.add(getMutation(cm));
}
valueMap.put(innerEntry.getKey(), mutations);
}
mutation_map.put(entry.getKey(), valueMap);
}
internal_batch_mutate(mutation_map, consistency_level);
}
private Counter getCounter(ColumnOrSuperColumn cosc)
{
if (cosc.isSetColumn()) {
return new Counter().setColumn(new CounterColumn(cosc.column.name, cosc.column.value.getLong(cosc.column.value.arrayOffset())));
} else if(cosc.isSetSuper_column()) {
List<CounterColumn> cc = new ArrayList<CounterColumn>(cosc.super_column.columns.size());
for (Column col : cosc.super_column.columns)
{
cc.add(new CounterColumn(col.name, col.value.getLong(col.value.arrayOffset())));
}
return new Counter().setSuper_column(new CounterSuperColumn(cosc.super_column.name, cc));
}
return null;
}
public Counter get_counter(ByteBuffer key, ColumnPath path, ConsistencyLevel consistency_level)
throws InvalidRequestException, NotFoundException, UnavailableException, TimedOutException, TException
{
logger.debug("get_counter");
String keyspace = state().getKeyspace();
ThriftValidation.validateCommutative(keyspace, path.column_family);
return getCounter(internal_get(key, path, consistency_level));
}
private List<Counter> getCounters(List<ColumnOrSuperColumn> cosc)
{
List<Counter> rv = new ArrayList<Counter>(cosc.size());
for (ColumnOrSuperColumn columnOrSuperColumn : cosc)
{
Counter c = getCounter(columnOrSuperColumn);
if (c != null) {
rv.add(c);
}
}
return rv;
}
public List<Counter> get_counter_slice(ByteBuffer key, ColumnParent column_parent, SlicePredicate predicate,
ConsistencyLevel consistency_level) throws InvalidRequestException, UnavailableException, TimedOutException,
TException
{
if (logger.isDebugEnabled())
logger.debug("get_counter_slice");
String keyspace = state().getKeyspace();
ThriftValidation.validateCommutative(keyspace, column_parent.column_family);
state().hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
List<ColumnOrSuperColumn> cosc = multigetSliceInternal(state().getKeyspace(), Collections.singletonList(key), column_parent, predicate, consistency_level).get(key);
return getCounters(cosc);
}
public Map<ByteBuffer, List<Counter>> multiget_counter_slice(List<ByteBuffer> keys, ColumnParent column_parent,
SlicePredicate predicate, ConsistencyLevel consistency_level) throws InvalidRequestException,
UnavailableException, TimedOutException, TException
{
if (logger.isDebugEnabled())
logger.debug("multiget_counter_slice");
String keyspace = state().getKeyspace();
ThriftValidation.validateCommutative(keyspace, column_parent.column_family);
state().hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
Map<ByteBuffer, List<ColumnOrSuperColumn>> slices = multigetSliceInternal(state().getKeyspace(), keys, column_parent, predicate, consistency_level);
Map<ByteBuffer, List<Counter>> rv = new HashMap<ByteBuffer, List<Counter>>(slices.size());
for (Entry<ByteBuffer, List<ColumnOrSuperColumn>> entry : slices.entrySet())
{
rv.put(entry.getKey(), getCounters(entry.getValue()));
}
return rv;
}
public void remove_counter(ByteBuffer key, ColumnPath path, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TimedOutException, TException
{
if (logger.isDebugEnabled())
logger.debug("remove_counter");
String keyspace = state().getKeyspace();
ThriftValidation.validateCommutative(keyspace, path.column_family);
internal_remove(key, path, System.currentTimeMillis(), consistency_level);
}
// main method moved to CassandraDaemon
}

View File

@ -25,6 +25,7 @@ import java.util.Arrays;
import java.util.Comparator;
import java.util.Set;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.ConfigurationException;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.ColumnFamily;
@ -432,4 +433,28 @@ public class ThriftValidation
throw new InvalidRequestException(e.getMessage());
}
}
public static CFMetaData validateCFMetaData(String tablename, String cfName) throws InvalidRequestException
{
if (cfName.isEmpty())
{
throw new InvalidRequestException("non-empty columnfamily is required");
}
CFMetaData metadata = DatabaseDescriptor.getCFMetaData(tablename, cfName);
if (metadata == null)
{
throw new InvalidRequestException("unconfigured columnfamily " + cfName);
}
return metadata;
}
static void validateCommutative(String tablename, String cfName) throws InvalidRequestException
{
validateTable(tablename);
CFMetaData metadata = validateCFMetaData(tablename, cfName);
if (!metadata.getDefaultValidator().isCommutative())
{
throw new InvalidRequestException("not commutative columnfamily " + cfName);
}
}
}

View File

@ -152,6 +152,70 @@ public class FBUtilities
return new Pair(midpoint, remainder);
}
/**
* Copy bytes from int into bytes starting from offset.
* @param bytes Target array
* @param offset Offset into the array
* @param i Value to write
*/
public static void copyIntoBytes(byte[] bytes, int offset, int i)
{
bytes[offset] = (byte)( ( i >>> 24 ) & 0xFF );
bytes[offset+1] = (byte)( ( i >>> 16 ) & 0xFF );
bytes[offset+2] = (byte)( ( i >>> 8 ) & 0xFF );
bytes[offset+3] = (byte)( i & 0xFF );
}
/**
* @param i Write this int to an array
* @return 4-byte array containing the int
*/
public static byte[] toByteArray(int i)
{
byte[] bytes = new byte[4];
copyIntoBytes(bytes, 0, i);
return bytes;
}
/**
* @param bytes A byte array containing a serialized integer.
* @param offset Start position of the integer in the array.
* @return The integer value contained in the byte array.
*/
public static int byteArrayToInt(byte[] bytes, int offset)
{
if (bytes.length - offset < 4)
{
throw new IllegalArgumentException("An integer must be 4 bytes in size.");
}
int n = 0;
for ( int i = 0; i < 4; ++i )
{
n <<= 8;
n |= bytes[offset + i] & 0xFF;
}
return n;
}
/**
* Convert a byte buffer to an integer.
* Does not change the byte buffer position.
*/
public static int byteBufferToInt(ByteBuffer bytes)
{
if (bytes.remaining() < 4)
{
throw new IllegalArgumentException("An integer must be 4 bytes in size.");
}
int n = 0;
for (int i = 0; i < 4; ++i)
{
n <<= 8;
n |= bytes.array()[bytes.position() + bytes.arrayOffset() + i] & 0xFF;
}
return n;
}
public static ByteBuffer toByteBuffer(int i)
{
byte[] bytes = new byte[4];
@ -162,21 +226,71 @@ public class FBUtilities
return ByteBuffer.wrap(bytes);
}
public static int byteBufferToInt(ByteBuffer bytes)
/**
* Copy bytes from long into bytes starting from offset.
* @param bytes Target array
* @param offset Offset into the array
* @param l Value to write
*/
public static void copyIntoBytes(byte[] bytes, int offset, long l)
{
if (bytes.remaining() < 4 )
bytes[offset] = (byte)( ( l >>> 56 ) & 0xFF );
bytes[offset+1] = (byte)( ( l >>> 48 ) & 0xFF );
bytes[offset+2] = (byte)( ( l >>> 40 ) & 0xFF );
bytes[offset+3] = (byte)( ( l >>> 32 ) & 0xFF );
bytes[offset+4] = (byte)( ( l >>> 24 ) & 0xFF );
bytes[offset+5] = (byte)( ( l >>> 16 ) & 0xFF );
bytes[offset+6] = (byte)( ( l >>> 8 ) & 0xFF );
bytes[offset+7] = (byte)( l & 0xFF );
}
/**
* @param l Write this long to an array
* @return 8-byte array containing the long
*/
public static byte[] toByteArray(long l)
{
byte[] bytes = new byte[8];
copyIntoBytes(bytes, 0, l);
return bytes;
}
/**
* @param bytes A byte array containing a serialized long.
* @return The long value contained in the byte array.
*/
public static long byteArrayToLong(byte[] bytes)
{
return byteArrayToLong(bytes, 0);
}
/**
* @param bytes A byte array containing a serialized long.
* @param offset Start position of the long in the array.
* @return The long value contained in the byte array.
*/
public static long byteArrayToLong(byte[] bytes, int offset)
{
if (bytes.length - offset < 8)
{
throw new IllegalArgumentException("An integer must be 4 bytes in size.");
throw new IllegalArgumentException("A long must be 8 bytes in size.");
}
int n = 0;
for ( int i = 0; i < 4; ++i )
long n = 0;
for ( int i = 0; i < 8; ++i )
{
n <<= 8;
n |= bytes.array()[bytes.position() + bytes.arrayOffset() + i] & 0xFF;
n |= bytes[offset + i] & 0xFF;
}
return n;
}
public static ByteBuffer toByteBuffer(long n)
{
byte[] bytes = new byte[8];
ByteBuffer bb = ByteBuffer.wrap(bytes).putLong(0, n);
return bb;
}
public static int compareUnsigned(byte[] bytes1, byte[] bytes2, int offset1, int offset2, int len1, int len2)
{
if (bytes1 == null)
@ -196,6 +310,38 @@ public class FBUtilities
if ((len1 - offset1) == (len2 - offset2)) return 0;
else return ((len1 - offset1) < (len2 - offset2)) ? -1 : 1;
}
/**
* Compare two byte[] at specified offsets for length. Compares the non equal bytes as unsigned.
* @param bytes1 First array to compare.
* @param offset1 Position to start the comparison at in the first array.
* @param bytes2 Second array to compare.
* @param offset2 Position to start the comparison at in the second array.
* @param length How many bytes to compare?
* @return -1 if byte1 is less than byte2, 1 if byte2 is less than byte1 or 0 if equal.
*/
public static int compareByteSubArrays(byte[] bytes1, int offset1, byte[] bytes2, int offset2, int length)
{
if ( null == bytes1 )
{
if ( null == bytes2) return 0;
else return -1;
}
if (null == bytes2 ) return 1;
assert bytes1.length >= (offset1 + length) : "The first byte array isn't long enough for the specified offset and length.";
assert bytes2.length >= (offset2 + length) : "The second byte array isn't long enough for the specified offset and length.";
for ( int i = 0; i < length; i++ )
{
byte byte1 = bytes1[offset1+i];
byte byte2 = bytes2[offset2+i];
if ( byte1 == byte2 )
continue;
// compare non-equal bytes as unsigned
return (byte1 & 0xFF) < (byte2 & 0xFF) ? -1 : 1;
}
return 0;
}
/**
* @return The bitwise XOR of the inputs. The output will be the same length as the
@ -496,12 +642,41 @@ public class FBUtilities
return decoded;
}
public static ByteBuffer toByteBuffer(long n)
/**
* Thin wrapper around byte[] to provide meaningful equals() and hashCode() operations
* caveat: assumed that wrapped byte[] will not be modified
*/
public static final class ByteArrayWrapper
{
byte[] bytes = new byte[8];
ByteBuffer bb = ByteBuffer.wrap(bytes).putLong(n);
bb.rewind();
return bb;
public final byte[] data;
public ByteArrayWrapper(byte[] data)
{
if ( null == data )
{
throw new NullPointerException();
}
this.data = data;
}
public boolean equals(Object other)
{
if ( !( other instanceof ByteArrayWrapper ) )
{
return false;
}
return Arrays.equals(data, ((ByteArrayWrapper)other).data);
}
public int hashCode()
{
return Arrays.hashCode(data);
}
public String toString()
{
return ArrayUtils.toString(data);
}
}
public static String resourceToFile(String filename) throws ConfigurationException
@ -519,6 +694,10 @@ public class FBUtilities
try
{
InputStream in = FBUtilities.class.getClassLoader().getResourceAsStream("org/apache/cassandra/config/version.properties");
if (in == null)
{
return "Unknown";
}
Properties props = new Properties();
props.load(in);
return props.getProperty("CassandraVersion");

View File

@ -70,6 +70,14 @@ keyspaces:
column_type: Super
compare_subcolumns_with: UTF8Type
- name: Counter1
column_type: Standard
default_validation_class: CounterColumnType
- name: SuperCounter1
column_type: Super
default_validation_class: CounterColumnType
- name: Indexed1
column_metadata:
- name: birthdate
@ -135,3 +143,7 @@ keyspaces:
replication_factor: 2
column_families:
- name: Standard1
- name: Counter1
column_type: Standard
default_validation_class: CounterColumnType

View File

@ -170,6 +170,8 @@ class ThriftTester(BaseTester):
Cassandra.CfDef('Keyspace1', 'Super2', column_type='Super', subcomparator_type='LongType'),
Cassandra.CfDef('Keyspace1', 'Super3', column_type='Super', subcomparator_type='LongType'),
Cassandra.CfDef('Keyspace1', 'Super4', column_type='Super', subcomparator_type='UTF8Type'),
Cassandra.CfDef('Keyspace1', 'Counter1', default_validation_class='CounterColumnType'),
Cassandra.CfDef('Keyspace1', 'SuperCounter1', column_type='Super', default_validation_class='CounterColumnType'),
Cassandra.CfDef('Keyspace1', 'Indexed1', column_metadata=[Cassandra.ColumnDef('birthdate', 'LongType', Cassandra.IndexType.KEYS, 'birthdate')]),
])

View File

@ -1467,6 +1467,255 @@ class TestMutations(ThriftTester):
client.describe_ring('system')
_expect_exception(req, InvalidRequestException)
def test_incr_decr_standard_add(self):
_set_keyspace('Keyspace1')
d1 = 12
d2 = -21
d3 = 35
# insert positive and negative values and check the counts
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c1', d1), ConsistencyLevel.ONE)
time.sleep(0.1)
rv1 = client.get_counter('key1', ColumnPath(column_family='Counter1', column='c1'), ConsistencyLevel.ONE)
assert rv1.column.value == d1
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c1', d2), ConsistencyLevel.ONE)
time.sleep(0.1)
rv2 = client.get_counter('key1', ColumnPath(column_family='Counter1', column='c1'), ConsistencyLevel.ONE)
assert rv2.column.value == (d1+d2)
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c1', d3), ConsistencyLevel.ONE)
time.sleep(0.1)
rv3 = client.get_counter('key1', ColumnPath(column_family='Counter1', column='c1'), ConsistencyLevel.ONE)
assert rv3.column.value == (d1+d2+d3)
def test_incr_decr_super_add(self):
_set_keyspace('Keyspace1')
d1 = -234
d2 = 52345
d3 = 3123
client.add('key1', ColumnParent(column_family='SuperCounter1', super_column='sc1'), CounterColumn('c1', d1), ConsistencyLevel.ONE)
client.add('key1', ColumnParent(column_family='SuperCounter1', super_column='sc1'), CounterColumn('c2', d2), ConsistencyLevel.ONE)
time.sleep(0.1)
rv1 = client.get_counter('key1', ColumnPath(column_family='SuperCounter1', super_column='sc1'), ConsistencyLevel.ONE)
assert rv1.super_column.columns[0].value == d1
assert rv1.super_column.columns[1].value == d2
client.add('key1', ColumnParent(column_family='SuperCounter1', super_column='sc1'), CounterColumn('c1', d2), ConsistencyLevel.ONE)
time.sleep(0.1)
rv2 = client.get_counter('key1', ColumnPath('SuperCounter1', 'sc1', 'c1'), ConsistencyLevel.ONE)
assert rv2.column.value == (d1+d2)
client.add('key1', ColumnParent(column_family='SuperCounter1', super_column='sc1'), CounterColumn('c1', d3), ConsistencyLevel.ONE)
time.sleep(0.1)
rv3 = client.get_counter('key1', ColumnPath(column_family='SuperCounter1', super_column='sc1', column='c1'), ConsistencyLevel.ONE)
assert rv3.column.value == (d1+d2+d3)
def test_incr_standard_remove(self):
_set_keyspace('Keyspace1')
d1 = 124
# insert value and check it exists
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c1', d1), ConsistencyLevel.ONE)
time.sleep(5)
rv1 = client.get_counter('key1', ColumnPath(column_family='Counter1', column='c1'), ConsistencyLevel.ONE)
assert rv1.column.value == d1
# remove the previous column and check that it is gone
client.remove_counter('key1', ColumnPath(column_family='Counter1', column='c1'), ConsistencyLevel.ONE)
time.sleep(5)
_assert_no_columnpath('key1', ColumnPath(column_family='Counter1', column='c1'))
# insert again and this time delete the whole row, check that it is gone
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c1', d1), ConsistencyLevel.ONE)
time.sleep(5)
rv2 = client.get_counter('key1', ColumnPath(column_family='Counter1', column='c1'), ConsistencyLevel.ONE)
assert rv2.column.value == d1
client.remove_counter('key1', ColumnPath(column_family='Counter1'), ConsistencyLevel.ONE)
time.sleep(5)
_assert_no_columnpath('key1', ColumnPath(column_family='Counter1', column='c1'))
def test_incr_super_remove(self):
_set_keyspace('Keyspace1')
d1 = 52345
# insert value and check it exists
client.add('key1', ColumnParent(column_family='SuperCounter1', super_column='sc1'), CounterColumn('c1', d1), ConsistencyLevel.ONE)
time.sleep(5)
rv1 = client.get_counter('key1', ColumnPath(column_family='SuperCounter1', super_column='sc1', column='c1'), ConsistencyLevel.ONE)
assert rv1.column.value == d1
# remove the previous column and check that it is gone
client.remove_counter('key1', ColumnPath(column_family='SuperCounter1', super_column='sc1', column='c1'), ConsistencyLevel.ONE)
time.sleep(5)
_assert_no_columnpath('key1', ColumnPath(column_family='SuperCounter1', super_column='sc1', column='c1'))
# insert again and this time delete the whole row, check that it is gone
client.add('key1', ColumnParent(column_family='SuperCounter1', super_column='sc1'), CounterColumn('c1', d1), ConsistencyLevel.ONE)
time.sleep(5)
rv2 = client.get_counter('key1', ColumnPath(column_family='SuperCounter1', super_column='sc1', column='c1'), ConsistencyLevel.ONE)
assert rv2.column.value == d1
client.remove_counter('key1', ColumnPath(column_family='SuperCounter1', super_column='sc1'), ConsistencyLevel.ONE)
time.sleep(5)
_assert_no_columnpath('key1', ColumnPath(column_family='SuperCounter1', super_column='sc1', column='c1'))
def test_incr_decr_standard_remove(self):
_set_keyspace('Keyspace1')
d1 = 124
# insert value and check it exists
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c1', d1), ConsistencyLevel.ONE)
time.sleep(5)
rv1 = client.get_counter('key1', ColumnPath(column_family='Counter1', column='c1'), ConsistencyLevel.ONE)
assert rv1.column.value == d1
# remove the previous column and check that it is gone
client.remove_counter('key1', ColumnPath(column_family='Counter1', column='c1'), ConsistencyLevel.ONE)
time.sleep(5)
_assert_no_columnpath('key1', ColumnPath(column_family='Counter1', column='c1'))
# insert again and this time delete the whole row, check that it is gone
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c1', d1), ConsistencyLevel.ONE)
time.sleep(5)
rv2 = client.get_counter('key1', ColumnPath(column_family='Counter1', column='c1'), ConsistencyLevel.ONE)
assert rv2.column.value == d1
client.remove_counter('key1', ColumnPath(column_family='Counter1'), ConsistencyLevel.ONE)
time.sleep(5)
_assert_no_columnpath('key1', ColumnPath(column_family='Counter1', column='c1'))
def test_incr_decr_super_remove(self):
_set_keyspace('Keyspace1')
d1 = 52345
# insert value and check it exists
client.add('key1', ColumnParent(column_family='SuperCounter1', super_column='sc1'), CounterColumn('c1', d1), ConsistencyLevel.ONE)
time.sleep(5)
rv1 = client.get_counter('key1', ColumnPath(column_family='SuperCounter1', super_column='sc1', column='c1'), ConsistencyLevel.ONE)
assert rv1.column.value == d1
# remove the previous column and check that it is gone
client.remove_counter('key1', ColumnPath(column_family='SuperCounter1', super_column='sc1', column='c1'), ConsistencyLevel.ONE)
time.sleep(5)
_assert_no_columnpath('key1', ColumnPath(column_family='SuperCounter1', super_column='sc1', column='c1'))
# insert again and this time delete the whole row, check that it is gone
client.add('key1', ColumnParent(column_family='SuperCounter1', super_column='sc1'), CounterColumn('c1', d1), ConsistencyLevel.ONE)
time.sleep(5)
rv2 = client.get_counter('key1', ColumnPath(column_family='SuperCounter1', super_column='sc1', column='c1'), ConsistencyLevel.ONE)
assert rv2.column.value == d1
client.remove_counter('key1', ColumnPath(column_family='SuperCounter1', super_column='sc1'), ConsistencyLevel.ONE)
time.sleep(5)
_assert_no_columnpath('key1', ColumnPath(column_family='SuperCounter1', super_column='sc1', column='c1'))
def test_incr_decr_standard_batch_add(self):
_set_keyspace('Keyspace1')
d1 = 12
d2 = -21
update_map = {'key1': {'Counter1': [
CounterMutation(counter=Counter(column=CounterColumn('c1', d1))),
CounterMutation(counter=Counter(column=CounterColumn('c1', d2))),
]}}
# insert positive and negative values and check the counts
client.batch_add(update_map, ConsistencyLevel.ONE)
time.sleep(0.1)
rv1 = client.get_counter('key1', ColumnPath(column_family='Counter1', column='c1'), ConsistencyLevel.ONE)
assert rv1.column.value == d1+d2
def test_incr_decr_standard_batch_remove(self):
_set_keyspace('Keyspace1')
d1 = 12
d2 = -21
# insert positive and negative values and check the counts
update_map = {'key1': {'Counter1': [
CounterMutation(counter=Counter(column=CounterColumn('c1', d1))),
CounterMutation(counter=Counter(column=CounterColumn('c1', d2))),
]}}
client.batch_add(update_map, ConsistencyLevel.ONE)
time.sleep(5)
rv1 = client.get_counter('key1', ColumnPath(column_family='Counter1', column='c1'), ConsistencyLevel.ONE)
assert rv1.column.value == d1+d2
# remove the previous column and check that it is gone
update_map = {'key1': {'Counter1': [
CounterMutation(deletion=CounterDeletion(predicate=SlicePredicate(column_names=['c1']))),
]}}
client.batch_add(update_map, ConsistencyLevel.ONE)
time.sleep(5)
_assert_no_columnpath('key1', ColumnPath(column_family='Counter1', column='c1'))
# insert again and this time delete the whole row, check that it is gone
update_map = {'key1': {'Counter1': [
CounterMutation(counter=Counter(column=CounterColumn('c1', d1))),
CounterMutation(counter=Counter(column=CounterColumn('c1', d2))),
]}}
client.batch_add(update_map, ConsistencyLevel.ONE)
time.sleep(5)
rv2 = client.get_counter('key1', ColumnPath(column_family='Counter1', column='c1'), ConsistencyLevel.ONE)
assert rv2.column.value == d1+d2
update_map = {'key1': {'Counter1': [
CounterMutation(deletion=CounterDeletion()),
]}}
client.batch_add(update_map, ConsistencyLevel.ONE)
time.sleep(5)
_assert_no_columnpath('key1', ColumnPath(column_family='Counter1', column='c1'))
def test_incr_decr_standard_slice(self):
_set_keyspace('Keyspace1')
d1 = 12
d2 = -21
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c1', d1), ConsistencyLevel.ONE)
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c2', d1), ConsistencyLevel.ONE)
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c3', d1), ConsistencyLevel.ONE)
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c3', d2), ConsistencyLevel.ONE)
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c4', d1), ConsistencyLevel.ONE)
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c5', d1), ConsistencyLevel.ONE)
time.sleep(0.1)
# insert positive and negative values and check the counts
counters = client.get_counter_slice('key1', ColumnParent('Counter1'), SlicePredicate(['c3', 'c4']), ConsistencyLevel.ONE)
assert counters[0].column.value == d1+d2
assert counters[1].column.value == d1
def test_incr_decr_standard_muliget_slice(self):
_set_keyspace('Keyspace1')
d1 = 12
d2 = -21
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c2', d1), ConsistencyLevel.ONE)
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c3', d1), ConsistencyLevel.ONE)
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c3', d2), ConsistencyLevel.ONE)
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c4', d1), ConsistencyLevel.ONE)
client.add('key1', ColumnParent(column_family='Counter1'), CounterColumn('c5', d1), ConsistencyLevel.ONE)
client.add('key2', ColumnParent(column_family='Counter1'), CounterColumn('c2', d1), ConsistencyLevel.ONE)
client.add('key2', ColumnParent(column_family='Counter1'), CounterColumn('c3', d1), ConsistencyLevel.ONE)
client.add('key2', ColumnParent(column_family='Counter1'), CounterColumn('c3', d2), ConsistencyLevel.ONE)
client.add('key2', ColumnParent(column_family='Counter1'), CounterColumn('c4', d1), ConsistencyLevel.ONE)
client.add('key2', ColumnParent(column_family='Counter1'), CounterColumn('c5', d1), ConsistencyLevel.ONE)
time.sleep(0.1)
# insert positive and negative values and check the counts
counters = client.multiget_counter_slice(['key1', 'key2'], ColumnParent('Counter1'), SlicePredicate(['c3', 'c4']), ConsistencyLevel.ONE)
assert counters['key1'][0].column.value == d1+d2
assert counters['key1'][1].column.value == d1
assert counters['key2'][0].column.value == d1+d2
assert counters['key2'][1].column.value == d1
def test_index_scan(self):
_set_keyspace('Keyspace1')
client.insert('key1', ColumnParent('Indexed1'), Column('birthdate', _i64(1), 0), ConsistencyLevel.ONE)

View File

@ -128,6 +128,27 @@ public class Util
return cfStore.getColumnFamily(QueryFilter.getIdentityFilter(key, new QueryPath(cfName)));
}
public static byte[] concatByteArrays(byte[] first, byte[]... remaining)
{
int length = first.length;
for (byte[] array : remaining)
{
length += array.length;
}
byte[] result = new byte[length];
System.arraycopy(first, 0, result, 0, first.length);
int offset = first.length;
for (byte[] array : remaining)
{
System.arraycopy(array, 0, result, offset, array.length);
offset += array.length;
}
return result;
}
public static ColumnFamily cloneAndRemoveDeleted(ColumnFamily cf, int gcBefore)
{
return ColumnFamilyStore.removeDeleted(cf.cloneMe(), gcBefore);

View File

@ -0,0 +1,346 @@
/*
* 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;
import static org.junit.Assert.*;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import org.apache.commons.lang.ArrayUtils;
import org.junit.Test;
import org.apache.cassandra.Util;
import org.apache.cassandra.db.context.CounterContext;
import org.apache.cassandra.db.marshal.AbstractCommutativeType;
import org.apache.cassandra.db.marshal.CounterColumnType;
import org.apache.cassandra.io.util.DataOutputBuffer;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
public class CounterColumnTest
{
private static final CounterContext cc = new CounterContext();
private static final int idLength;
private static final int clockLength;
private static final int countLength;
private static final int stepLength;
static
{
idLength = 4; // size of int
clockLength = 8; // size of long
countLength = 8; // size of long
stepLength = idLength + clockLength + countLength;
}
@Test
public void testCreate() throws UnknownHostException
{
AbstractCommutativeType type = CounterColumnType.instance;
long delta = 3L;
CounterColumn column = (CounterColumn)type.createColumn(ByteBufferUtil.bytes("x"), FBUtilities.toByteBuffer(delta), 1L);
assert delta == column.value().getLong(column.value().arrayOffset());
assert 0 == column.partitionedCounter().length;
InetAddress node = InetAddress.getByAddress(FBUtilities.toByteArray(1));
column.update(node);
assert delta == column.value().getLong(column.value().arrayOffset());
assert 1 == FBUtilities.byteArrayToInt( column.partitionedCounter(), 0*stepLength);
assert 1L == FBUtilities.byteArrayToLong(column.partitionedCounter(), 0*stepLength + idLength);
assert 3L == FBUtilities.byteArrayToLong(column.partitionedCounter(), 0*stepLength + idLength + clockLength);
}
@Test
public void testUpdate() throws UnknownHostException
{
CounterColumn c = new CounterColumn(ByteBufferUtil.bytes("x"), FBUtilities.toByteBuffer(0L), 0L);
assert 0L == c.value().getLong(c.value().arrayOffset());
assert c.partitionedCounter().length == 0 : "badly formatted initial context";
c.value = FBUtilities.toByteBuffer(1L);
c.update(InetAddress.getByAddress(FBUtilities.toByteArray(1)));
assert 1L == c.value().getLong(c.value().arrayOffset());
assert c.partitionedCounter().length == stepLength;
assert 1 == FBUtilities.byteArrayToInt( c.partitionedCounter(), 0*stepLength);
assert 1L == FBUtilities.byteArrayToLong(c.partitionedCounter(), 0*stepLength + idLength);
assert 1L == FBUtilities.byteArrayToLong(c.partitionedCounter(), 0*stepLength + idLength + clockLength);
c.value = FBUtilities.toByteBuffer(3L);
c.update(InetAddress.getByAddress(FBUtilities.toByteArray(2)));
c.value = FBUtilities.toByteBuffer(2L);
c.update(InetAddress.getByAddress(FBUtilities.toByteArray(2)));
c.value = FBUtilities.toByteBuffer(9L);
c.update(InetAddress.getByAddress(FBUtilities.toByteArray(2)));
assert 15L == c.value().getLong(c.value().arrayOffset());
assert c.partitionedCounter().length == (2 * stepLength);
assert 2 == FBUtilities.byteArrayToInt(c.partitionedCounter(), 0*stepLength);
assert 3L == FBUtilities.byteArrayToLong(c.partitionedCounter(), 0*stepLength + idLength);
assert 14L == FBUtilities.byteArrayToLong(c.partitionedCounter(), 0*stepLength + idLength + clockLength);
assert 1 == FBUtilities.byteArrayToInt(c.partitionedCounter(), 1*stepLength);
assert 1L == FBUtilities.byteArrayToLong(c.partitionedCounter(), 1*stepLength + idLength);
assert 1L == FBUtilities.byteArrayToLong(c.partitionedCounter(), 1*stepLength + idLength + clockLength);
}
@Test
public void testReconcile() throws UnknownHostException
{
IColumn left;
IColumn right;
IColumn reconciled;
// tombstone + tombstone
left = new DeletedColumn(ByteBufferUtil.bytes("x"), 1, 1L);
right = new DeletedColumn(ByteBufferUtil.bytes("x"), 2, 2L);
assert left.reconcile(right).timestamp() == right.timestamp();
assert right.reconcile(left).timestamp() == right.timestamp();
// tombstone > live
left = new DeletedColumn(ByteBufferUtil.bytes("x"), 1, 2L);
right = new CounterColumn(ByteBufferUtil.bytes("x"), FBUtilities.toByteBuffer(0L), 1L);
assert left.reconcile(right) == left;
// tombstone < live last delete
left = new DeletedColumn(ByteBufferUtil.bytes("x"), 1, 1L);
right = new CounterColumn(ByteBufferUtil.bytes("x"), FBUtilities.toByteBuffer(0L), 4L, new byte[0], 2L);
assert left.reconcile(right) == right;
// tombstone == live last delete
left = new DeletedColumn(ByteBufferUtil.bytes("x"), 1, 2L);
right = new CounterColumn(ByteBufferUtil.bytes("x"), FBUtilities.toByteBuffer(0L), 4L, new byte[0], 2L);
assert left.reconcile(right) == right;
// tombstone > live last delete
left = new DeletedColumn(ByteBufferUtil.bytes("x"), 1, 4L);
right = new CounterColumn(ByteBufferUtil.bytes("x"), FBUtilities.toByteBuffer(0L), 9L, new byte[0], 1L);
reconciled = left.reconcile(right);
assert reconciled.name() == right.name();
assert reconciled.value() == right.value();
assert reconciled.timestamp() == right.timestamp();
assert ((CounterColumn)reconciled).partitionedCounter() == ((CounterColumn)right).partitionedCounter();
assert ((CounterColumn)reconciled).timestampOfLastDelete() == left.timestamp();
// live < tombstone
left = new CounterColumn(ByteBufferUtil.bytes("x"), FBUtilities.toByteBuffer(0L), 1L);
right = new DeletedColumn(ByteBufferUtil.bytes("x"), 1, 2L);
assert left.reconcile(right) == right;
// live last delete > tombstone
left = new CounterColumn(ByteBufferUtil.bytes("x"), FBUtilities.toByteBuffer(0L), 4L, new byte[0], 2L);
right = new DeletedColumn(ByteBufferUtil.bytes("x"), 1, 1L);
assert left.reconcile(right) == left;
// live last delete == tombstone
left = new CounterColumn(ByteBufferUtil.bytes("x"), FBUtilities.toByteBuffer(0L), 4L, new byte[0], 2L);
right = new DeletedColumn(ByteBufferUtil.bytes("x"), 1, 2L);
assert left.reconcile(right) == left;
// live last delete < tombstone
left = new CounterColumn(ByteBufferUtil.bytes("x"), FBUtilities.toByteBuffer(0L), 9L, new byte[0], 1L);
right = new DeletedColumn(ByteBufferUtil.bytes("x"), 1, 4L);
reconciled = left.reconcile(right);
assert reconciled.name() == left.name();
assert reconciled.value() == left.value();
assert reconciled.timestamp() == left.timestamp();
assert ((CounterColumn)reconciled).partitionedCounter() == ((CounterColumn)left).partitionedCounter();
assert ((CounterColumn)reconciled).timestampOfLastDelete() == right.timestamp();
// live + live
byte[] context;
context = new byte[0];
context = cc.update(context, InetAddress.getByAddress(FBUtilities.toByteArray(1)), 1L);
context = cc.update(context, InetAddress.getByAddress(FBUtilities.toByteArray(1)), 0L);
context = cc.update(context, InetAddress.getByAddress(FBUtilities.toByteArray(1)), 1L);
context = cc.update(context, InetAddress.getByAddress(FBUtilities.toByteArray(2)), 0L);
context = cc.update(context, InetAddress.getByAddress(FBUtilities.toByteArray(2)), 5L);
left = new CounterColumn(ByteBufferUtil.bytes("x"), ByteBuffer.wrap(cc.total(context)), 9L, context, 1L);
context = new byte[0];
context = cc.update(context, InetAddress.getByAddress(FBUtilities.toByteArray(2)), 4L);
context = cc.update(context, InetAddress.getByAddress(FBUtilities.toByteArray(3)), 2L);
right = new CounterColumn(ByteBufferUtil.bytes("x"), ByteBuffer.wrap(cc.total(context)), 3L, context, 4L);
reconciled = left.reconcile(right);
assert reconciled.name() == left.name();
assert 9L == reconciled.value().getLong(reconciled.value().arrayOffset());
assert reconciled.timestamp() == 9L;
context = ((CounterColumn)reconciled).partitionedCounter();
assert 3 * stepLength == context.length;
assert 1 == FBUtilities.byteArrayToInt(context, 0*stepLength);
assert 3L == FBUtilities.byteArrayToLong(context, 0*stepLength + idLength);
assert 2L == FBUtilities.byteArrayToLong(context, 0*stepLength + idLength + clockLength);
assert 2 == FBUtilities.byteArrayToInt(context, 1*stepLength);
assert 2L == FBUtilities.byteArrayToLong(context, 1*stepLength + idLength);
assert 5L == FBUtilities.byteArrayToLong(context, 1*stepLength + idLength + clockLength);
assert 3 == FBUtilities.byteArrayToInt(context, 2*stepLength);
assert 1L == FBUtilities.byteArrayToLong(context, 2*stepLength + idLength);
assert 2L == FBUtilities.byteArrayToLong(context, 2*stepLength + idLength + clockLength);
assert ((CounterColumn)reconciled).timestampOfLastDelete() == 4L;
}
@Test
public void testDiff() throws UnknownHostException
{
byte[] left;
byte[] right;
CounterColumn leftCol;
CounterColumn rightCol;
// timestamp
left = new byte[0];
leftCol = new CounterColumn(ByteBufferUtil.bytes("x"), ByteBuffer.wrap(cc.total(left)), 1L, left);
right = new byte[0];
rightCol = new CounterColumn(ByteBufferUtil.bytes("x"), ByteBuffer.wrap(cc.total(right)), 2L, right);
assert rightCol == leftCol.diff(rightCol);
assert null == rightCol.diff(leftCol);
// timestampOfLastDelete
left = new byte[0];
leftCol = new CounterColumn(ByteBufferUtil.bytes("x"), ByteBuffer.wrap(cc.total(left)), 1L, left, 1L);
right = new byte[0];
rightCol = new CounterColumn(ByteBufferUtil.bytes("x"), ByteBuffer.wrap(cc.total(right)), 1L, right, 2L);
assert rightCol == leftCol.diff(rightCol);
assert null == rightCol.diff(leftCol);
// equality: equal nodes, all counts same
left = 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 = ArrayUtils.clone(left);
leftCol = new CounterColumn(ByteBufferUtil.bytes("x"), ByteBuffer.wrap(cc.total(left)), 1L, left);
rightCol = new CounterColumn(ByteBufferUtil.bytes("x"), ByteBuffer.wrap(cc.total(right)), 1L, right);
assert null == leftCol.diff(rightCol);
// greater than: left has superset of nodes (counts equal)
left = 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 = 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)
);
leftCol = new CounterColumn(ByteBufferUtil.bytes("x"), ByteBuffer.wrap(cc.total(left)), 1L, left);
rightCol = new CounterColumn(ByteBufferUtil.bytes("x"), ByteBuffer.wrap(cc.total(right)), 1L, right);
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 = 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 = 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)
);
leftCol = new CounterColumn(ByteBufferUtil.bytes("x"), ByteBuffer.wrap(cc.total(left)), 1L, left);
rightCol = new CounterColumn(ByteBufferUtil.bytes("x"), ByteBuffer.wrap(cc.total(right)), 1L, right);
assert rightCol == leftCol.diff(rightCol);
assert leftCol == rightCol.diff(leftCol);
}
@Test
public void testCleanNodeCounts() throws UnknownHostException
{
byte[] context = 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"), ByteBuffer.wrap(cc.total(context)), 1L, context);
CounterColumn d = c.cleanNodeCounts(InetAddress.getByAddress(FBUtilities.toByteArray(4)));
assertEquals(7L, d.value().getLong(d.value().arrayOffset()));
}
@Test
public void testSerializeDeserialize() throws IOException
{
ColumnFamily cf;
byte[] context = 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"), ByteBuffer.wrap(cc.total(context)), 1L, context);
DataOutputBuffer bufOut = new DataOutputBuffer();
Column.serializer().serialize(original, bufOut);
ByteArrayInputStream bufIn = new ByteArrayInputStream(bufOut.getData(), 0, bufOut.getLength());
CounterColumn deserialized = (CounterColumn)Column.serializer().deserialize(new DataInputStream(bufIn));
assert original.equals(deserialized);
}
}

View File

@ -115,6 +115,7 @@ public class DefsTest extends CleanupHelper
1.0,
1.0,
0.5,
false,
100000,
null,
500,
@ -766,6 +767,7 @@ public class DefsTest extends CleanupHelper
0,
1.0,
0,
false,
CFMetaData.DEFAULT_GC_GRACE_SECONDS,
BytesType.instance,
CFMetaData.DEFAULT_MIN_COMPACTION_THRESHOLD,

View File

@ -24,12 +24,16 @@ import org.junit.Test;
import static junit.framework.Assert.assertNotNull;
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 org.apache.cassandra.db.marshal.LongType;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
public class SuperColumnTest
{
private static final CounterContext cc = new CounterContext();
@Test
public void testMissingSubcolumn() {
SuperColumn sc = new SuperColumn(ByteBufferUtil.bytes("sc1"), LongType.instance);
@ -37,4 +41,69 @@ public class SuperColumnTest
assertNotNull(sc.getSubColumn(getBytes(1)));
assertNull(sc.getSubColumn(getBytes(2)));
}
@Test
public void testAddColumnIncrementCounter()
{
byte[] context;
SuperColumn sc = new SuperColumn(ByteBufferUtil.bytes("sc1"), LongType.instance);
context = concatByteArrays(
FBUtilities.getLocalAddress().getAddress(), FBUtilities.toByteArray(3L), FBUtilities.toByteArray(3L),
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)
);
sc.addColumn(new CounterColumn(getBytes(1), ByteBuffer.wrap(cc.total(context)), 3L, context, 0L));
context = concatByteArrays(
FBUtilities.getLocalAddress().getAddress(), FBUtilities.toByteArray(9L), FBUtilities.toByteArray(5L),
FBUtilities.toByteArray(8), FBUtilities.toByteArray(9L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(4), FBUtilities.toByteArray(4L), FBUtilities.toByteArray(1L),
FBUtilities.toByteArray(2), FBUtilities.toByteArray(3L), FBUtilities.toByteArray(4L)
);
sc.addColumn(new CounterColumn(getBytes(1), ByteBuffer.wrap(cc.total(context)), 10L, context, 0L));
context = concatByteArrays(
FBUtilities.toByteArray(3), FBUtilities.toByteArray(6L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(7), FBUtilities.toByteArray(3L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(2), FBUtilities.toByteArray(1L), FBUtilities.toByteArray(0L)
);
sc.addColumn(new CounterColumn(getBytes(2), ByteBuffer.wrap(cc.total(context)), 9L, context, 0L));
assertNotNull(sc.getSubColumn(getBytes(1)));
assertNull(sc.getSubColumn(getBytes(3)));
// column: 1
byte[] c1 = concatByteArrays(
FBUtilities.getLocalAddress().getAddress(), FBUtilities.toByteArray(12L), FBUtilities.toByteArray(8L),
FBUtilities.toByteArray(8), FBUtilities.toByteArray(9L), FBUtilities.toByteArray(0L),
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)
);
assert 0 == FBUtilities.compareByteSubArrays(
((CounterColumn)sc.getSubColumn(getBytes(1))).partitionedCounter(),
0,
c1,
0,
c1.length);
// column: 2
byte[] c2 = concatByteArrays(
FBUtilities.toByteArray(3), FBUtilities.toByteArray(6L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(7), FBUtilities.toByteArray(3L), FBUtilities.toByteArray(0L),
FBUtilities.toByteArray(2), FBUtilities.toByteArray(1L), FBUtilities.toByteArray(0L)
);
assert 0 == FBUtilities.compareByteSubArrays(
((CounterColumn)sc.getSubColumn(getBytes(2))).partitionedCounter(),
0,
c2,
0,
c2.length);
assertNotNull(sc.getSubColumn(getBytes(1)));
assertNotNull(sc.getSubColumn(getBytes(2)));
assertNull(sc.getSubColumn(getBytes(3)));
}
}

View File

@ -0,0 +1,536 @@
/*
*
* 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.context;
import static org.junit.Assert.*;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import org.apache.commons.lang.ArrayUtils;
import org.junit.Test;
import org.apache.cassandra.Util;
import org.apache.cassandra.db.context.IContext.ContextRelationship;
import org.apache.cassandra.utils.FBUtilities;
/**
* 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
clockLength = 8; // size of long
countLength = 8; // size of long
stepLength = idLength + clockLength + countLength;
defaultEntries = 10;
}
@Test
public void testCreate()
{
byte[] context = cc.create();
assert context.length == 0;
}
@Test
public void testUpdatePresentReorder() throws UnknownHostException
{
byte[] context;
context = new byte[stepLength * defaultEntries];
for (int i = 0; i < defaultEntries - 1; i++)
{
cc.writeElementAtStepOffset(
context,
i,
FBUtilities.toByteArray(i),
1L,
1L);
}
cc.writeElementAtStepOffset(
context,
(defaultEntries - 1),
id,
2L,
3L);
context = cc.update(context, idAddress, 10L);
assertEquals(context.length, stepLength * defaultEntries);
assertEquals( 3L, FBUtilities.byteArrayToLong(context, idLength));
assertEquals( 13L, FBUtilities.byteArrayToLong(context, idLength + clockLength));
for (int i = 1; i < defaultEntries; i++)
{
int offset = i * stepLength;
assertEquals( i-1, FBUtilities.byteArrayToInt(context, offset));
assertEquals(1L, FBUtilities.byteArrayToLong(context, offset + idLength));
assertEquals(1L, FBUtilities.byteArrayToLong(context, offset + idLength + clockLength));
}
}
@Test
public void testUpdateNotPresent()
{
byte[] context = new byte[stepLength * 2];
for (int i = 0; i < 2; i++)
{
cc.writeElementAtStepOffset(
context,
i,
FBUtilities.toByteArray(i),
1L,
1L);
}
context = cc.update(context, idAddress, 328L);
assert context.length == stepLength * 3;
assert 1L == FBUtilities.byteArrayToLong(context, idLength);
assert 328L == FBUtilities.byteArrayToLong(context, idLength + clockLength);
for (int i = 1; i < 3; i++)
{
int offset = i * stepLength;
assert i-1 == FBUtilities.byteArrayToInt(context, offset);
assert 1L == FBUtilities.byteArrayToLong(context, offset + idLength);
assert 1L == FBUtilities.byteArrayToLong(context, offset + idLength + clockLength);
}
}
@Test
public void testSwapElement()
{
byte[] context = new byte[stepLength * 3];
for (int i = 0; i < 3; i++)
{
cc.writeElementAtStepOffset(
context,
i,
FBUtilities.toByteArray(i),
1L,
1L);
}
cc.swapElement(context, 0, 2*stepLength);
assert 2 == FBUtilities.byteArrayToInt(context, 0);
assert 0 == FBUtilities.byteArrayToInt(context, 2*stepLength);
cc.swapElement(context, 0, 1*stepLength);
assert 1 == FBUtilities.byteArrayToInt(context, 0);
assert 2 == FBUtilities.byteArrayToInt(context, 1*stepLength);
}
@Test
public void testPartitionElements()
{
byte[] context = new byte[stepLength * 10];
cc.writeElementAtStepOffset(context, 0, FBUtilities.toByteArray(5), 1L, 1L);
cc.writeElementAtStepOffset(context, 1, FBUtilities.toByteArray(3), 1L, 1L);
cc.writeElementAtStepOffset(context, 2, FBUtilities.toByteArray(6), 1L, 1L);
cc.writeElementAtStepOffset(context, 3, FBUtilities.toByteArray(7), 1L, 1L);
cc.writeElementAtStepOffset(context, 4, FBUtilities.toByteArray(8), 1L, 1L);
cc.writeElementAtStepOffset(context, 5, FBUtilities.toByteArray(9), 1L, 1L);
cc.writeElementAtStepOffset(context, 6, FBUtilities.toByteArray(2), 1L, 1L);
cc.writeElementAtStepOffset(context, 7, FBUtilities.toByteArray(4), 1L, 1L);
cc.writeElementAtStepOffset(context, 8, FBUtilities.toByteArray(1), 1L, 1L);
cc.writeElementAtStepOffset(context, 9, FBUtilities.toByteArray(3), 1L, 1L);
cc.partitionElements(
context,
0, // left
9, // right (inclusive)
2 // pivot
);
assert 5 == FBUtilities.byteArrayToInt(context, 0*stepLength);
assert 3 == FBUtilities.byteArrayToInt(context, 1*stepLength);
assert 3 == FBUtilities.byteArrayToInt(context, 2*stepLength);
assert 2 == FBUtilities.byteArrayToInt(context, 3*stepLength);
assert 4 == FBUtilities.byteArrayToInt(context, 4*stepLength);
assert 1 == FBUtilities.byteArrayToInt(context, 5*stepLength);
assert 6 == FBUtilities.byteArrayToInt(context, 6*stepLength);
assert 8 == FBUtilities.byteArrayToInt(context, 7*stepLength);
assert 9 == FBUtilities.byteArrayToInt(context, 8*stepLength);
assert 7 == FBUtilities.byteArrayToInt(context, 9*stepLength);
}
@Test
public void testSortElementsById()
{
byte[] context = new byte[stepLength * 10];
cc.writeElementAtStepOffset(context, 0, FBUtilities.toByteArray(5), 1L, 1L);
cc.writeElementAtStepOffset(context, 1, FBUtilities.toByteArray(3), 1L, 1L);
cc.writeElementAtStepOffset(context, 2, FBUtilities.toByteArray(6), 1L, 1L);
cc.writeElementAtStepOffset(context, 3, FBUtilities.toByteArray(7), 1L, 1L);
cc.writeElementAtStepOffset(context, 4, FBUtilities.toByteArray(8), 1L, 1L);
cc.writeElementAtStepOffset(context, 5, FBUtilities.toByteArray(9), 1L, 1L);
cc.writeElementAtStepOffset(context, 6, FBUtilities.toByteArray(2), 1L, 1L);
cc.writeElementAtStepOffset(context, 7, FBUtilities.toByteArray(4), 1L, 1L);
cc.writeElementAtStepOffset(context, 8, FBUtilities.toByteArray(1), 1L, 1L);
cc.writeElementAtStepOffset(context, 9, FBUtilities.toByteArray(3), 1L, 1L);
byte[] sorted = cc.sortElementsById(context);
assertEquals( 1, FBUtilities.byteArrayToInt(sorted, 0*stepLength));
assertEquals( 2, FBUtilities.byteArrayToInt(sorted, 1*stepLength));
assertEquals( 3, FBUtilities.byteArrayToInt(sorted, 2*stepLength));
assertEquals( 3, FBUtilities.byteArrayToInt(sorted, 3*stepLength));
assertEquals( 4, FBUtilities.byteArrayToInt(sorted, 4*stepLength));
assertEquals( 5, FBUtilities.byteArrayToInt(sorted, 5*stepLength));
assertEquals( 6, FBUtilities.byteArrayToInt(sorted, 6*stepLength));
assertEquals( 7, FBUtilities.byteArrayToInt(sorted, 7*stepLength));
assertEquals( 8, FBUtilities.byteArrayToInt(sorted, 8*stepLength));
assertEquals( 9, FBUtilities.byteArrayToInt(sorted, 9*stepLength));
}
@Test
public void testDiff()
{
byte[] left = new byte[3 * stepLength];
byte[] right;
// equality: equal nodes, all counts same
cc.writeElementAtStepOffset(left, 0, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtStepOffset(left, 1, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtStepOffset(left, 2, FBUtilities.toByteArray(9), 1L, 0L);
right = ArrayUtils.clone(left);
assert ContextRelationship.EQUAL ==
cc.diff(left, right);
// greater than: left has superset of nodes (counts equal)
left = new byte[4 * stepLength];
cc.writeElementAtStepOffset(left, 0, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtStepOffset(left, 1, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtStepOffset(left, 2, FBUtilities.toByteArray(9), 1L, 0L);
cc.writeElementAtStepOffset(left, 3, FBUtilities.toByteArray(12), 0L, 0L);
right = new byte[3 * stepLength];
cc.writeElementAtStepOffset(right, 0, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtStepOffset(right, 1, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtStepOffset(right, 2, FBUtilities.toByteArray(9), 1L, 0L);
assert ContextRelationship.GREATER_THAN ==
cc.diff(left, right);
// less than: left has subset of nodes (counts equal)
left = new byte[3 * stepLength];
cc.writeElementAtStepOffset(left, 0, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtStepOffset(left, 1, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtStepOffset(left, 2, FBUtilities.toByteArray(9), 1L, 0L);
right = new byte[4 * stepLength];
cc.writeElementAtStepOffset(right, 0, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtStepOffset(right, 1, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtStepOffset(right, 2, FBUtilities.toByteArray(9), 1L, 0L);
cc.writeElementAtStepOffset(right, 3, FBUtilities.toByteArray(12), 0L, 0L);
assert ContextRelationship.LESS_THAN ==
cc.diff(left, right);
// greater than: equal nodes, but left has higher counts
left = new byte[3 * stepLength];
cc.writeElementAtStepOffset(left, 0, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtStepOffset(left, 1, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtStepOffset(left, 2, FBUtilities.toByteArray(9), 3L, 0L);
right = new byte[3 * stepLength];
cc.writeElementAtStepOffset(right, 0, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtStepOffset(right, 1, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtStepOffset(right, 2, FBUtilities.toByteArray(9), 1L, 0L);
assert ContextRelationship.GREATER_THAN ==
cc.diff(left, right);
// less than: equal nodes, but right has higher counts
left = new byte[3 * stepLength];
cc.writeElementAtStepOffset(left, 0, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtStepOffset(left, 1, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtStepOffset(left, 2, FBUtilities.toByteArray(9), 3L, 0L);
right = new byte[3 * stepLength];
cc.writeElementAtStepOffset(right, 0, FBUtilities.toByteArray(3), 3L, 0L);
cc.writeElementAtStepOffset(right, 1, FBUtilities.toByteArray(6), 9L, 0L);
cc.writeElementAtStepOffset(right, 2, FBUtilities.toByteArray(9), 3L, 0L);
assert ContextRelationship.LESS_THAN ==
cc.diff(left, right);
// disjoint: right and left have disjoint node sets
left = new byte[3 * stepLength];
cc.writeElementAtStepOffset(left, 0, FBUtilities.toByteArray(3), 1L, 0L);
cc.writeElementAtStepOffset(left, 1, FBUtilities.toByteArray(4), 1L, 0L);
cc.writeElementAtStepOffset(left, 2, FBUtilities.toByteArray(9), 1L, 0L);
right = new byte[3 * stepLength];
cc.writeElementAtStepOffset(right, 0, FBUtilities.toByteArray(3), 1L, 0L);
cc.writeElementAtStepOffset(right, 1, FBUtilities.toByteArray(6), 1L, 0L);
cc.writeElementAtStepOffset(right, 2, FBUtilities.toByteArray(9), 1L, 0L);
assert ContextRelationship.DISJOINT ==
cc.diff(left, right);
left = new byte[3 * stepLength];
cc.writeElementAtStepOffset(left, 0, FBUtilities.toByteArray(3), 1L, 0L);
cc.writeElementAtStepOffset(left, 1, FBUtilities.toByteArray(4), 1L, 0L);
cc.writeElementAtStepOffset(left, 2, FBUtilities.toByteArray(9), 1L, 0L);
right = new byte[3 * stepLength];
cc.writeElementAtStepOffset(right, 0, FBUtilities.toByteArray(2), 1L, 0L);
cc.writeElementAtStepOffset(right, 1, FBUtilities.toByteArray(6), 1L, 0L);
cc.writeElementAtStepOffset(right, 2, FBUtilities.toByteArray(12), 1L, 0L);
assert ContextRelationship.DISJOINT ==
cc.diff(left, right);
// disjoint: equal nodes, but right and left have higher counts in differing nodes
left = new byte[3 * stepLength];
cc.writeElementAtStepOffset(left, 0, FBUtilities.toByteArray(3), 1L, 0L);
cc.writeElementAtStepOffset(left, 1, FBUtilities.toByteArray(6), 3L, 0L);
cc.writeElementAtStepOffset(left, 2, FBUtilities.toByteArray(9), 1L, 0L);
right = new byte[3 * stepLength];
cc.writeElementAtStepOffset(right, 0, FBUtilities.toByteArray(3), 1L, 0L);
cc.writeElementAtStepOffset(right, 1, FBUtilities.toByteArray(6), 1L, 0L);
cc.writeElementAtStepOffset(right, 2, FBUtilities.toByteArray(9), 5L, 0L);
assert ContextRelationship.DISJOINT ==
cc.diff(left, right);
left = new byte[3 * stepLength];
cc.writeElementAtStepOffset(left, 0, FBUtilities.toByteArray(3), 2L, 0L);
cc.writeElementAtStepOffset(left, 1, FBUtilities.toByteArray(6), 3L, 0L);
cc.writeElementAtStepOffset(left, 2, FBUtilities.toByteArray(9), 1L, 0L);
right = new byte[3 * stepLength];
cc.writeElementAtStepOffset(right, 0, FBUtilities.toByteArray(3), 1L, 0L);
cc.writeElementAtStepOffset(right, 1, FBUtilities.toByteArray(6), 9L, 0L);
cc.writeElementAtStepOffset(right, 2, FBUtilities.toByteArray(9), 5L, 0L);
assert ContextRelationship.DISJOINT ==
cc.diff(left, right);
// disjoint: left has more nodes, but lower counts
left = new byte[4 * stepLength];
cc.writeElementAtStepOffset(left, 0, FBUtilities.toByteArray(3), 2L, 0L);
cc.writeElementAtStepOffset(left, 1, FBUtilities.toByteArray(6), 3L, 0L);
cc.writeElementAtStepOffset(left, 2, FBUtilities.toByteArray(9), 1L, 0L);
cc.writeElementAtStepOffset(left, 3, FBUtilities.toByteArray(12), 1L, 0L);
right = new byte[3 * stepLength];
cc.writeElementAtStepOffset(right, 0, FBUtilities.toByteArray(3), 4L, 0L);
cc.writeElementAtStepOffset(right, 1, FBUtilities.toByteArray(6), 9L, 0L);
cc.writeElementAtStepOffset(right, 2, FBUtilities.toByteArray(9), 5L, 0L);
assert ContextRelationship.DISJOINT ==
cc.diff(left, right);
// disjoint: left has less nodes, but higher counts
left = new byte[3 * stepLength];
cc.writeElementAtStepOffset(left, 0, FBUtilities.toByteArray(3), 5L, 0L);
cc.writeElementAtStepOffset(left, 1, FBUtilities.toByteArray(6), 3L, 0L);
cc.writeElementAtStepOffset(left, 2, FBUtilities.toByteArray(9), 2L, 0L);
right = new byte[4 * stepLength];
cc.writeElementAtStepOffset(right, 0, FBUtilities.toByteArray(3), 4L, 0L);
cc.writeElementAtStepOffset(right, 1, FBUtilities.toByteArray(6), 3L, 0L);
cc.writeElementAtStepOffset(right, 2, FBUtilities.toByteArray(9), 2L, 0L);
cc.writeElementAtStepOffset(right, 3, FBUtilities.toByteArray(12), 1L, 0L);
assert ContextRelationship.DISJOINT ==
cc.diff(left, right);
// disjoint: mixed nodes and counts
left = new byte[3 * stepLength];
cc.writeElementAtStepOffset(left, 0, FBUtilities.toByteArray(3), 5L, 0L);
cc.writeElementAtStepOffset(left, 1, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtStepOffset(left, 2, FBUtilities.toByteArray(9), 2L, 0L);
right = new byte[4 * stepLength];
cc.writeElementAtStepOffset(right, 0, FBUtilities.toByteArray(3), 4L, 0L);
cc.writeElementAtStepOffset(right, 1, FBUtilities.toByteArray(6), 3L, 0L);
cc.writeElementAtStepOffset(right, 2, FBUtilities.toByteArray(9), 2L, 0L);
cc.writeElementAtStepOffset(right, 3, FBUtilities.toByteArray(12), 1L, 0L);
assert ContextRelationship.DISJOINT ==
cc.diff(left, right);
left = new byte[4 * stepLength];
cc.writeElementAtStepOffset(left, 0, FBUtilities.toByteArray(3), 5L, 0L);
cc.writeElementAtStepOffset(left, 1, FBUtilities.toByteArray(6), 2L, 0L);
cc.writeElementAtStepOffset(left, 2, FBUtilities.toByteArray(7), 2L, 0L);
cc.writeElementAtStepOffset(left, 3, FBUtilities.toByteArray(9), 2L, 0L);
right = new byte[3 * stepLength];
cc.writeElementAtStepOffset(right, 0, FBUtilities.toByteArray(3), 4L, 0L);
cc.writeElementAtStepOffset(right, 1, FBUtilities.toByteArray(6), 3L, 0L);
cc.writeElementAtStepOffset(right, 2, FBUtilities.toByteArray(9), 2L, 0L);
assert ContextRelationship.DISJOINT ==
cc.diff(left, right);
}
@Test
public void testMerge()
{
// note: local counts aggregated; remote counts are reconciled (i.e. take max)
byte[] left = new byte[4 * stepLength];
cc.writeElementAtStepOffset(left, 0, FBUtilities.toByteArray(1), 1L, 1L);
cc.writeElementAtStepOffset(left, 1, FBUtilities.toByteArray(2), 2L, 2L);
cc.writeElementAtStepOffset(left, 2, FBUtilities.toByteArray(4), 6L, 3L);
cc.writeElementAtStepOffset(
left,
3,
FBUtilities.getLocalAddress().getAddress(),
7L,
3L);
byte[] right = new byte[3 * stepLength];
cc.writeElementAtStepOffset(right, 2, FBUtilities.toByteArray(5), 5L, 5L);
cc.writeElementAtStepOffset(right, 1, FBUtilities.toByteArray(4), 4L, 4L);
cc.writeElementAtStepOffset(
right,
0,
FBUtilities.getLocalAddress().getAddress(),
2L,
9L);
byte[] merged = cc.merge(left, right);
// local node id's counts are aggregated
assertEquals(0, FBUtilities.compareUnsigned(
FBUtilities.getLocalAddress().getAddress(),
merged,
0,
0*stepLength,
4,
4));
assertEquals( 9L, FBUtilities.byteArrayToLong(merged, 0*stepLength + idLength));
assertEquals(12L, FBUtilities.byteArrayToLong(merged, 0*stepLength + idLength + clockLength));
// remote node id counts are reconciled (i.e. take max)
assertEquals( 4, FBUtilities.byteArrayToInt(merged, 1*stepLength));
assertEquals( 6L, FBUtilities.byteArrayToLong(merged, 1*stepLength + idLength));
assertEquals( 3L, FBUtilities.byteArrayToLong(merged, 1*stepLength + idLength + clockLength));
assertEquals( 5, FBUtilities.byteArrayToInt(merged, 2*stepLength));
assertEquals( 5L, FBUtilities.byteArrayToLong(merged, 2*stepLength + idLength));
assertEquals( 5L, FBUtilities.byteArrayToLong(merged, 2*stepLength + idLength + clockLength));
assertEquals( 2, FBUtilities.byteArrayToInt(merged, 3*stepLength));
assertEquals( 2L, FBUtilities.byteArrayToLong(merged, 3*stepLength + idLength));
assertEquals( 2L, FBUtilities.byteArrayToLong(merged, 3*stepLength + idLength + clockLength));
assertEquals( 1, FBUtilities.byteArrayToInt(merged, 4*stepLength));
assertEquals( 1L, FBUtilities.byteArrayToLong(merged, 4*stepLength + idLength));
assertEquals( 1L, FBUtilities.byteArrayToLong(merged, 4*stepLength + idLength + clockLength));
}
@Test
public void testTotal()
{
byte[] left = new byte[4 * stepLength];
cc.writeElementAtStepOffset(left, 0, FBUtilities.toByteArray(1), 1L, 1L);
cc.writeElementAtStepOffset(left, 1, FBUtilities.toByteArray(2), 2L, 2L);
cc.writeElementAtStepOffset(left, 2, FBUtilities.toByteArray(4), 3L, 3L);
cc.writeElementAtStepOffset(
left,
3,
FBUtilities.getLocalAddress().getAddress(),
3L,
3L);
byte[] right = new byte[3 * stepLength];
cc.writeElementAtStepOffset(right, 2, FBUtilities.toByteArray(5), 5L, 5L);
cc.writeElementAtStepOffset(right, 1, FBUtilities.toByteArray(4), 4L, 4L);
cc.writeElementAtStepOffset(
right,
0,
FBUtilities.getLocalAddress().getAddress(),
9L,
9L);
byte[] merged = cc.merge(left, right);
// 127.0.0.1: 12 (3+9)
// 0.0.0.1: 1
// 0.0.0.2: 2
// 0.0.0.4: 4
// 0.0.0.5: 5
assertEquals(24L, FBUtilities.byteArrayToLong(cc.total(merged)));
}
@Test
public void testCleanNodeCounts() throws UnknownHostException
{
byte[] bytes = new byte[4 * stepLength];
cc.writeElementAtStepOffset(bytes, 0, FBUtilities.toByteArray(1), 1L, 1L);
cc.writeElementAtStepOffset(bytes, 1, FBUtilities.toByteArray(2), 2L, 2L);
cc.writeElementAtStepOffset(bytes, 2, FBUtilities.toByteArray(4), 3L, 3L);
cc.writeElementAtStepOffset(bytes, 3, FBUtilities.toByteArray(8), 4L, 4L);
assertEquals(4, FBUtilities.byteArrayToInt(bytes, 2*stepLength));
assertEquals(3L, FBUtilities.byteArrayToLong(bytes, 2*stepLength + idLength));
bytes = cc.cleanNodeCounts(bytes, InetAddress.getByAddress(FBUtilities.toByteArray(4)));
// node: 0.0.0.4 should be removed
assertEquals(3 * stepLength, bytes.length);
// other nodes should be unaffected
assertEquals(1, FBUtilities.byteArrayToInt(bytes, 0*stepLength));
assertEquals(1L, FBUtilities.byteArrayToLong(bytes, 0*stepLength + idLength));
assertEquals(2, FBUtilities.byteArrayToInt(bytes, 1*stepLength));
assertEquals(2L, FBUtilities.byteArrayToLong(bytes, 1*stepLength + idLength));
assertEquals(8, FBUtilities.byteArrayToInt(bytes, 2*stepLength));
assertEquals(4L, FBUtilities.byteArrayToLong(bytes, 2*stepLength + idLength));
}
}

View File

@ -51,6 +51,11 @@ public class SSTableUtils
}
public static File tempSSTableFile(String tablename, String cfname) throws IOException
{
return tempSSTableFile(tablename, cfname, 0);
}
public static File tempSSTableFile(String tablename, String cfname, int generation) throws IOException
{
File tempdir = File.createTempFile(tablename, cfname);
if(!tempdir.delete() || !tempdir.mkdir())
@ -59,7 +64,7 @@ public class SSTableUtils
File tabledir = new File(tempdir, tablename);
tabledir.mkdir();
tabledir.deleteOnExit();
File datafile = new File(new Descriptor(tabledir, tablename, cfname, 0, false).filenameFor("Data.db"));
File datafile = new File(new Descriptor(tabledir, tablename, cfname, generation, false).filenameFor("Data.db"));
if (!datafile.createNewFile())
throw new IOException("unable to create file " + datafile);
datafile.deleteOnExit();
@ -92,7 +97,12 @@ public class SSTableUtils
public static SSTableReader writeRawSSTable(String tablename, String cfname, Map<ByteBuffer, ByteBuffer> entries) throws IOException
{
File datafile = tempSSTableFile(tablename, cfname);
return writeRawSSTable(tablename, cfname, entries, 0);
}
public static SSTableReader writeRawSSTable(String tablename, String cfname, Map<ByteBuffer, ByteBuffer> entries, int generation) throws IOException
{
File datafile = tempSSTableFile(tablename, cfname, generation);
SSTableWriter writer = new SSTableWriter(datafile.getAbsolutePath(), entries.size());
SortedMap<DecoratedKey, ByteBuffer> sortedEntries = new TreeMap<DecoratedKey, ByteBuffer>();
for (Map.Entry<ByteBuffer, ByteBuffer> entry : entries.entrySet())

View File

@ -0,0 +1,186 @@
/*
*
* 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.io.sstable;
import static org.junit.Assert.*;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import org.apache.cassandra.CleanupHelper;
import org.apache.cassandra.Util;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.context.CounterContext;
import org.apache.cassandra.db.marshal.CounterColumnType;
import org.apache.cassandra.io.util.BufferedRandomAccessFile;
import org.apache.cassandra.io.util.DataOutputBuffer;
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.junit.Test;
import org.apache.cassandra.utils.ByteBufferUtil;
public class SSTableWriterAESCommutativeTest 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
{
String keyspace = "Keyspace1";
String cfname = "Counter1";
Map<ByteBuffer, ByteBuffer> entries = new HashMap<ByteBuffer, ByteBuffer>();
Map<ByteBuffer, ByteBuffer> cleanedEntries = new HashMap<ByteBuffer, ByteBuffer>();
DataOutputBuffer buffer;
ColumnFamily cf = ColumnFamily.create(keyspace, cfname);
byte[] context;
// 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(cc.total(context)),
0L,
context
));
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(cc.total(context)),
0L,
context
));
buffer = new DataOutputBuffer();
ColumnFamily.serializer().serializeWithIndexes(cf, buffer);
entries.put(
ByteBufferUtil.bytes("k"),
ByteBuffer.wrap(Arrays.copyOf(buffer.getData(), buffer.getLength()))
);
ctype.cleanContext(cf, FBUtilities.getLocalAddress());
buffer = new DataOutputBuffer();
ColumnFamily.serializer().serializeWithIndexes(cf, buffer);
cleanedEntries.put(
ByteBufferUtil.bytes("k"),
ByteBuffer.wrap(Arrays.copyOf(buffer.getData(), buffer.getLength()))
);
cf.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(cc.total(context)),
0L,
context
));
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(cc.total(context)),
0L,
context
));
buffer = new DataOutputBuffer();
ColumnFamily.serializer().serializeWithIndexes(cf, buffer);
entries.put(
ByteBufferUtil.bytes("l"),
ByteBuffer.wrap(Arrays.copyOf(buffer.getData(), buffer.getLength()))
);
ctype.cleanContext(cf, FBUtilities.getLocalAddress());
buffer = new DataOutputBuffer();
ColumnFamily.serializer().serializeWithIndexes(cf, buffer);
cleanedEntries.put(
ByteBufferUtil.bytes("l"),
ByteBuffer.wrap(Arrays.copyOf(buffer.getData(), buffer.getLength()))
);
cf.clear();
// write out unmodified CF
SSTableReader orig = SSTableUtils.writeRawSSTable(keyspace, cfname, entries, 0);
// whack the index to trigger the recover
FileUtils.deleteWithConfirm(orig.descriptor.filenameFor(Component.PRIMARY_INDEX));
FileUtils.deleteWithConfirm(orig.descriptor.filenameFor(Component.FILTER));
// re-build inline
SSTableReader rebuilt = CompactionManager.instance.submitSSTableBuild(
orig.descriptor,
OperationType.AES
).get();
// write out cleaned CF
SSTableReader cleaned = SSTableUtils.writeRawSSTable(keyspace, cfname, cleanedEntries, 0);
// verify
BufferedRandomAccessFile origFile = new BufferedRandomAccessFile(orig.descriptor.filenameFor(SSTable.COMPONENT_DATA), "r", 8 * 1024 * 1024);
BufferedRandomAccessFile cleanedFile = new BufferedRandomAccessFile(cleaned.descriptor.filenameFor(SSTable.COMPONENT_DATA), "r", 8 * 1024 * 1024);
while(origFile.getFilePointer() < origFile.length() && cleanedFile.getFilePointer() < cleanedFile.length())
{
assert origFile.readByte() == cleanedFile.readByte();
}
assert origFile.getFilePointer() == origFile.length();
assert cleanedFile.getFilePointer() == cleanedFile.length();
}
}

View File

@ -41,6 +41,7 @@ import org.apache.cassandra.dht.Range;
import org.apache.cassandra.io.util.DataOutputBuffer;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.streaming.OperationType;
import org.apache.cassandra.thrift.IndexClause;
import org.apache.cassandra.thrift.IndexExpression;
import org.apache.cassandra.thrift.IndexOperator;
@ -80,7 +81,7 @@ public class SSTableWriterTest extends CleanupHelper {
FileUtils.deleteWithConfirm(orig.descriptor.filenameFor(Component.PRIMARY_INDEX));
FileUtils.deleteWithConfirm(orig.descriptor.filenameFor(Component.FILTER));
SSTableReader sstr = CompactionManager.instance.submitSSTableBuild(orig.descriptor).get();
SSTableReader sstr = CompactionManager.instance.submitSSTableBuild(orig.descriptor, OperationType.AES).get();
assert sstr != null;
ColumnFamilyStore cfs = Table.open("Keyspace1").getColumnFamilyStore("Indexed1");
cfs.addSSTable(sstr);

View File

@ -0,0 +1,10 @@
package org.apache.cassandra.service;
public class AntiEntropyServiceCounterTest extends AntiEntropyServiceTestAbstract
{
public void init()
{
tablename = "Keyspace5";
cfname = "Counter1";
}
}

View File

@ -0,0 +1,10 @@
package org.apache.cassandra.service;
public class AntiEntropyServiceStandardTest extends AntiEntropyServiceTestAbstract
{
public void init()
{
tablename = "Keyspace5";
cfname = "Standard1";
}
}

View File

@ -0,0 +1,273 @@
/*
* 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.service;
import java.net.InetAddress;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadPoolExecutor;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.cassandra.CleanupHelper;
import org.apache.cassandra.Util;
import org.apache.cassandra.concurrent.Stage;
import org.apache.cassandra.concurrent.StageManager;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.filter.QueryPath;
import org.apache.cassandra.dht.IPartitioner;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.io.PrecompactedRow;
import org.apache.cassandra.io.util.DataOutputBuffer;
import org.apache.cassandra.locator.AbstractReplicationStrategy;
import org.apache.cassandra.locator.TokenMetadata;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.MerkleTree;
import static org.apache.cassandra.service.AntiEntropyService.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.apache.cassandra.utils.ByteBufferUtil;
public abstract class AntiEntropyServiceTestAbstract extends CleanupHelper
{
// table and column family to test against
public AntiEntropyService aes;
public String tablename;
public String cfname;
public TreeRequest request;
public ColumnFamilyStore store;
public InetAddress LOCAL, REMOTE;
private boolean initialized;
public abstract void init();
@Before
public void prepare() throws Exception
{
if (!initialized)
{
initialized = true;
init();
LOCAL = FBUtilities.getLocalAddress();
StorageService.instance.initServer();
// generate a fake endpoint for which we can spoof receiving/sending trees
REMOTE = InetAddress.getByName("127.0.0.2");
store = null;
for (ColumnFamilyStore cfs : Table.open(tablename).getColumnFamilyStores())
{
if (cfs.columnFamily.equals(cfname))
{
store = cfs;
break;
}
}
assert store != null : "CF not found: " + cfname;
}
aes = AntiEntropyService.instance;
TokenMetadata tmd = StorageService.instance.getTokenMetadata();
tmd.clearUnsafe();
StorageService.instance.setToken(StorageService.getPartitioner().getRandomToken());
tmd.updateNormalToken(StorageService.getPartitioner().getMinimumToken(), REMOTE);
assert tmd.isMember(REMOTE);
// random session id for each test
request = new TreeRequest(UUID.randomUUID().toString(), LOCAL, new CFPair(tablename, cfname));
}
@After
public void teardown() throws Exception
{
flushAES();
}
@Test
public void testValidatorPrepare() throws Throwable
{
Validator validator;
// write
List<RowMutation> rms = new LinkedList<RowMutation>();
RowMutation rm;
rm = new RowMutation(tablename, ByteBufferUtil.bytes("key1"));
rm.add(new QueryPath(cfname, null, ByteBufferUtil.bytes("Column1")), ByteBufferUtil.bytes("asdf"), 0);
rms.add(rm);
Util.writeColumnFamily(rms);
// sample
validator = new Validator(request);
validator.prepare(store);
// and confirm that the tree was split
assertTrue(validator.tree.size() > 1);
}
@Test
public void testValidatorComplete() throws Throwable
{
Validator validator = new Validator(request);
validator.prepare(store);
validator.complete();
// confirm that the tree was validated
Token min = validator.tree.partitioner().getMinimumToken();
assert null != validator.tree.hash(new Range(min, min));
}
@Test
public void testValidatorAdd() throws Throwable
{
Validator validator = new Validator(request);
IPartitioner part = validator.tree.partitioner();
Token min = part.getMinimumToken();
Token mid = part.midpoint(min, min);
validator.prepare(store);
// add a row with the minimum token
validator.add(new PrecompactedRow(new DecoratedKey(min, ByteBufferUtil.bytes("nonsense!")),
new DataOutputBuffer()));
// and a row after it
validator.add(new PrecompactedRow(new DecoratedKey(mid, ByteBufferUtil.bytes("inconceivable!")),
new DataOutputBuffer()));
validator.complete();
// confirm that the tree was validated
assert null != validator.tree.hash(new Range(min, min));
}
@Test
public void testManualRepair() throws Throwable
{
AntiEntropyService.RepairSession sess = AntiEntropyService.instance.getRepairSession(tablename, cfname);
sess.start();
sess.blockUntilRunning();
// ensure that the session doesn't end without a response from REMOTE
sess.join(100);
assert sess.isAlive();
// deliver a fake response from REMOTE
AntiEntropyService.instance.completedRequest(new TreeRequest(sess.getName(), REMOTE, request.cf));
// block until the repair has completed
sess.join();
}
@Test
public void testGetNeighborsPlusOne() throws Throwable
{
// generate rf+1 nodes, and ensure that all nodes are returned
Set<InetAddress> expected = addTokens(1 + Table.open(tablename).getReplicationStrategy().getReplicationFactor());
expected.remove(FBUtilities.getLocalAddress());
assertEquals(expected, AntiEntropyService.getNeighbors(tablename));
}
@Test
public void testGetNeighborsTimesTwo() throws Throwable
{
TokenMetadata tmd = StorageService.instance.getTokenMetadata();
// generate rf*2 nodes, and ensure that only neighbors specified by the ARS are returned
addTokens(2 * Table.open(tablename).getReplicationStrategy().getReplicationFactor());
AbstractReplicationStrategy ars = Table.open(tablename).getReplicationStrategy();
Set<InetAddress> expected = new HashSet<InetAddress>();
for (Range replicaRange : ars.getAddressRanges().get(FBUtilities.getLocalAddress()))
{
expected.addAll(ars.getRangeAddresses(tmd).get(replicaRange));
}
expected.remove(FBUtilities.getLocalAddress());
assertEquals(expected, AntiEntropyService.getNeighbors(tablename));
}
@Test
public void testDifferencer() throws Throwable
{
// generate a tree
Validator validator = new Validator(request);
validator.prepare(store);
validator.complete();
MerkleTree ltree = validator.tree;
// and a clone
validator = new Validator(request);
validator.prepare(store);
validator.complete();
MerkleTree rtree = validator.tree;
// change a range we own in one of the trees
Token ltoken = StorageService.instance.getLocalToken();
ltree.invalidate(ltoken);
MerkleTree.TreeRange changed = ltree.invalids(StorageService.instance.getLocalPrimaryRange()).next();
changed.hash("non-empty hash!".getBytes());
// the changed range has two halves, split on our local token: both will be repaired
// (since this keyspace has RF > N, so every node is responsible for the entire ring)
Set<Range> interesting = new HashSet<Range>();
interesting.add(new Range(changed.left, ltoken));
interesting.add(new Range(ltoken, changed.right));
// difference the trees
Differencer diff = new Differencer(request, ltree, rtree);
diff.run();
// ensure that the changed range was recorded
assertEquals("Wrong differing ranges", interesting, new HashSet<Range>(diff.differences));
}
Set<InetAddress> addTokens(int max) throws Throwable
{
TokenMetadata tmd = StorageService.instance.getTokenMetadata();
Set<InetAddress> endpoints = new HashSet<InetAddress>();
for (int i = 1; i <= max; i++)
{
InetAddress endpoint = InetAddress.getByName("127.0.0." + i);
tmd.updateNormalToken(StorageService.getPartitioner().getRandomToken(), endpoint);
endpoints.add(endpoint);
}
return endpoints;
}
void flushAES() throws Exception
{
final ThreadPoolExecutor stage = StageManager.getStage(Stage.ANTI_ENTROPY);
final Callable noop = new Callable<Object>()
{
public Boolean call()
{
return true;
}
};
// send two tasks through the stage: one to follow existing tasks and a second to follow tasks created by
// those existing tasks: tasks won't recursively create more tasks
stage.submit(noop).get(5000, TimeUnit.MILLISECONDS);
stage.submit(noop).get(5000, TimeUnit.MILLISECONDS);
}
}

View File

@ -37,7 +37,7 @@ public class BootstrapTest extends SchemaLoader
public void testGetNewNames() throws IOException
{
Descriptor desc = Descriptor.fromFilename(new File("Keyspace1", "Standard1-500-Data.db").toString());
PendingFile inContext = new PendingFile(null, desc, "Data.db", Arrays.asList(new Pair<Long,Long>(0L, 1L)));
PendingFile inContext = new PendingFile(null, desc, "Data.db", Arrays.asList(new Pair<Long,Long>(0L, 1L)), OperationType.BOOTSTRAP);
PendingFile outContext = StreamIn.getContextMapping(inContext);
// filename and generation are expected to have changed

View File

@ -85,7 +85,7 @@ public class StreamingTransferTest extends CleanupHelper
ranges.add(new Range(p.getMinimumToken(), p.getToken(ByteBufferUtil.bytes("key1"))));
ranges.add(new Range(p.getToken(ByteBufferUtil.bytes("key2")), p.getMinimumToken()));
StreamOutSession session = StreamOutSession.create(table.name, LOCAL, null);
StreamOut.transferSSTables(session, Arrays.asList(sstable), ranges);
StreamOut.transferSSTables(session, Arrays.asList(sstable), ranges, OperationType.BOOTSTRAP);
session.await();
// confirm that the SSTable was transferred and registered
@ -135,7 +135,7 @@ public class StreamingTransferTest extends CleanupHelper
ranges.add(new Range(p.getMinimumToken(), p.getToken(ByteBufferUtil.bytes("transfer1"))));
ranges.add(new Range(p.getToken(ByteBufferUtil.bytes("test2")), p.getMinimumToken()));
StreamOutSession session = StreamOutSession.create(tablename, LOCAL, null);
StreamOut.transferSSTables(session, Arrays.asList(sstable, sstable2), ranges);
StreamOut.transferSSTables(session, Arrays.asList(sstable, sstable2), ranges, OperationType.BOOTSTRAP);
session.await();
// confirm that the SSTable was transferred and registered

View File

@ -0,0 +1,20 @@
package org.apache.cassandra.thrift;
import org.junit.Test;
import org.apache.cassandra.CleanupHelper;
public class ThriftValidationTest extends CleanupHelper
{
@Test(expected=InvalidRequestException.class)
public void testValidateCommutativeWithStandard() throws InvalidRequestException
{
ThriftValidation.validateCommutative("Keyspace1", "Standard1");
}
@Test
public void testValidateCommutativeWithCounter() throws InvalidRequestException
{
ThriftValidation.validateCommutative("Keyspace1", "Counter1");
}
}

View File

@ -20,6 +20,7 @@ package org.apache.cassandra.utils;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.nio.ByteBuffer;
@ -85,6 +86,78 @@ public class FBUtilitiesTest
}
}
@Test
public void testCopyIntoBytes()
{
int i = 300;
long l = 1000;
byte[] b = new byte[20];
FBUtilities.copyIntoBytes(b, 0, i);
FBUtilities.copyIntoBytes(b, 4, l);
assertEquals(i, FBUtilities.byteArrayToInt(b, 0));
assertEquals(l, FBUtilities.byteArrayToLong(b, 4));
}
@Test
public void testLongBytesConversions()
{
// positive, negative, 1 and 2 byte cases, including
// a few edges that would foul things up unless you're careful
// about masking away sign extension.
long[] longs = new long[]
{
-20L, -127L, -128L, 0L, 1L, 127L, 128L, 65534L, 65535L, -65534L, -65535L,
4294967294L, 4294967295L, -4294967294L, -4294967295L
};
for (long l : longs) {
byte[] ba = FBUtilities.toByteArray(l);
long actual = FBUtilities.byteArrayToLong(ba);
assertEquals(l, actual);
}
}
@Test
public void testCompareByteSubArrays()
{
byte[] bytes = new byte[16];
// handle null
assert FBUtilities.compareByteSubArrays(
null, 0, null, 0, 0) == 0;
assert FBUtilities.compareByteSubArrays(
null, 0, FBUtilities.toByteArray(524255231), 0, 4) == -1;
assert FBUtilities.compareByteSubArrays(
FBUtilities.toByteArray(524255231), 0, null, 0, 4) == 1;
// handle comparisons
FBUtilities.copyIntoBytes(bytes, 3, 524255231);
assert FBUtilities.compareByteSubArrays(
bytes, 3, FBUtilities.toByteArray(524255231), 0, 4) == 0;
assert FBUtilities.compareByteSubArrays(
bytes, 3, FBUtilities.toByteArray(524255232), 0, 4) == -1;
assert FBUtilities.compareByteSubArrays(
bytes, 3, FBUtilities.toByteArray(524255230), 0, 4) == 1;
// check that incorrect length throws exception
try
{
assert FBUtilities.compareByteSubArrays(
bytes, 3, FBUtilities.toByteArray(524255231), 0, 24) == 0;
fail("Should raise an AssertionError.");
} catch (AssertionError ae)
{
}
try
{
assert FBUtilities.compareByteSubArrays(
bytes, 3, FBUtilities.toByteArray(524255231), 0, 12) == 0;
fail("Should raise an AssertionError.");
} catch (AssertionError ae)
{
}
}
@Test(expected=CharacterCodingException.class)
public void testDecode() throws IOException
{