From ab6eaed8f6bdef21323be561a22e9fdb16bbd0fc Mon Sep 17 00:00:00 2001 From: Marcus Eriksson Date: Thu, 6 Feb 2014 10:08:55 +0100 Subject: [PATCH] Make it possible to cache a configurable amount of rows in row cache. Patch by pcmanus and marcuse, reviewed by pcmanus for CASSANDRA-5357 --- interface/cassandra.thrift | 3 +- .../org/apache/cassandra/thrift/CfDef.java | 404 ++++++++++++------ .../cassandra/thrift/cassandraConstants.java | 2 +- pylib/cqlshlib/cql3handling.py | 1 + .../org/apache/cassandra/cli/CliClient.java | 5 + .../apache/cassandra/config/CFMetaData.java | 66 ++- .../cassandra/cql/AlterTableStatement.java | 1 + .../org/apache/cassandra/cql/CFPropDefs.java | 2 + .../cql/CreateColumnFamilyStatement.java | 1 + .../cassandra/cql3/statements/CFPropDefs.java | 8 + .../org/apache/cassandra/db/ColumnFamily.java | 9 + .../cassandra/db/ColumnFamilyStore.java | 150 ++++++- .../cassandra/db/RowIteratorFactory.java | 11 +- .../cassandra/db/filter/IDiskAtomFilter.java | 13 +- .../cassandra/db/filter/NamesQueryFilter.java | 23 +- .../cassandra/db/filter/SliceQueryFilter.java | 36 ++ .../metrics/ColumnFamilyMetrics.java | 13 + .../cassandra/service/CacheService.java | 18 +- .../org/apache/cassandra/cli/CliHelp.yaml | 4 + .../org/apache/cassandra/SchemaLoader.java | 3 +- .../org/apache/cassandra/db/RowCacheTest.java | 78 ++++ 21 files changed, 678 insertions(+), 173 deletions(-) diff --git a/interface/cassandra.thrift b/interface/cassandra.thrift index 289be1fe4d..5f551ce57f 100644 --- a/interface/cassandra.thrift +++ b/interface/cassandra.thrift @@ -55,7 +55,7 @@ namespace rb CassandraThrift # An effort should be made not to break forward-client-compatibility either # (e.g. one should avoid removing obsolete fields from the IDL), but no # guarantees in this respect are made by the Cassandra project. -const string VERSION = "19.38.0" +const string VERSION = "19.38.1" # @@ -473,6 +473,7 @@ struct CfDef { 41: optional i32 index_interval, 42: optional string speculative_retry="NONE", 43: optional list triggers, + 44: optional string cells_per_row_to_cache = "100", /* All of the following are now ignored and unsupplied. */ diff --git a/interface/thrift/gen-java/org/apache/cassandra/thrift/CfDef.java b/interface/thrift/gen-java/org/apache/cassandra/thrift/CfDef.java index 0bdb5000ee..990b17e339 100644 --- a/interface/thrift/gen-java/org/apache/cassandra/thrift/CfDef.java +++ b/interface/thrift/gen-java/org/apache/cassandra/thrift/CfDef.java @@ -70,7 +70,6 @@ public class CfDef implements org.apache.thrift.TBase, jav private static final org.apache.thrift.protocol.TField ID_FIELD_DESC = new org.apache.thrift.protocol.TField("id", org.apache.thrift.protocol.TType.I32, (short)16); private static final org.apache.thrift.protocol.TField MIN_COMPACTION_THRESHOLD_FIELD_DESC = new org.apache.thrift.protocol.TField("min_compaction_threshold", org.apache.thrift.protocol.TType.I32, (short)17); private static final org.apache.thrift.protocol.TField MAX_COMPACTION_THRESHOLD_FIELD_DESC = new org.apache.thrift.protocol.TField("max_compaction_threshold", org.apache.thrift.protocol.TType.I32, (short)18); - private static final org.apache.thrift.protocol.TField REPLICATE_ON_WRITE_FIELD_DESC = new org.apache.thrift.protocol.TField("replicate_on_write", org.apache.thrift.protocol.TType.BOOL, (short)24); private static final org.apache.thrift.protocol.TField KEY_VALIDATION_CLASS_FIELD_DESC = new org.apache.thrift.protocol.TField("key_validation_class", org.apache.thrift.protocol.TType.STRING, (short)26); private static final org.apache.thrift.protocol.TField KEY_ALIAS_FIELD_DESC = new org.apache.thrift.protocol.TField("key_alias", org.apache.thrift.protocol.TType.STRING, (short)28); private static final org.apache.thrift.protocol.TField COMPACTION_STRATEGY_FIELD_DESC = new org.apache.thrift.protocol.TField("compaction_strategy", org.apache.thrift.protocol.TType.STRING, (short)29); @@ -85,6 +84,7 @@ public class CfDef implements org.apache.thrift.TBase, jav private static final org.apache.thrift.protocol.TField INDEX_INTERVAL_FIELD_DESC = new org.apache.thrift.protocol.TField("index_interval", org.apache.thrift.protocol.TType.I32, (short)41); private static final org.apache.thrift.protocol.TField SPECULATIVE_RETRY_FIELD_DESC = new org.apache.thrift.protocol.TField("speculative_retry", org.apache.thrift.protocol.TType.STRING, (short)42); private static final org.apache.thrift.protocol.TField TRIGGERS_FIELD_DESC = new org.apache.thrift.protocol.TField("triggers", org.apache.thrift.protocol.TType.LIST, (short)43); + private static final org.apache.thrift.protocol.TField CELLS_PER_ROW_TO_CACHE_FIELD_DESC = new org.apache.thrift.protocol.TField("cells_per_row_to_cache", org.apache.thrift.protocol.TType.STRING, (short)44); private static final org.apache.thrift.protocol.TField ROW_CACHE_SIZE_FIELD_DESC = new org.apache.thrift.protocol.TField("row_cache_size", org.apache.thrift.protocol.TType.DOUBLE, (short)9); private static final org.apache.thrift.protocol.TField KEY_CACHE_SIZE_FIELD_DESC = new org.apache.thrift.protocol.TField("key_cache_size", org.apache.thrift.protocol.TType.DOUBLE, (short)11); private static final org.apache.thrift.protocol.TField ROW_CACHE_SAVE_PERIOD_IN_SECONDS_FIELD_DESC = new org.apache.thrift.protocol.TField("row_cache_save_period_in_seconds", org.apache.thrift.protocol.TType.I32, (short)19); @@ -92,6 +92,7 @@ public class CfDef implements org.apache.thrift.TBase, jav private static final org.apache.thrift.protocol.TField MEMTABLE_FLUSH_AFTER_MINS_FIELD_DESC = new org.apache.thrift.protocol.TField("memtable_flush_after_mins", org.apache.thrift.protocol.TType.I32, (short)21); private static final org.apache.thrift.protocol.TField MEMTABLE_THROUGHPUT_IN_MB_FIELD_DESC = new org.apache.thrift.protocol.TField("memtable_throughput_in_mb", org.apache.thrift.protocol.TType.I32, (short)22); private static final org.apache.thrift.protocol.TField MEMTABLE_OPERATIONS_IN_MILLIONS_FIELD_DESC = new org.apache.thrift.protocol.TField("memtable_operations_in_millions", org.apache.thrift.protocol.TType.DOUBLE, (short)23); + private static final org.apache.thrift.protocol.TField REPLICATE_ON_WRITE_FIELD_DESC = new org.apache.thrift.protocol.TField("replicate_on_write", org.apache.thrift.protocol.TType.BOOL, (short)24); private static final org.apache.thrift.protocol.TField MERGE_SHARDS_CHANCE_FIELD_DESC = new org.apache.thrift.protocol.TField("merge_shards_chance", org.apache.thrift.protocol.TType.DOUBLE, (short)25); private static final org.apache.thrift.protocol.TField ROW_CACHE_PROVIDER_FIELD_DESC = new org.apache.thrift.protocol.TField("row_cache_provider", org.apache.thrift.protocol.TType.STRING, (short)27); private static final org.apache.thrift.protocol.TField ROW_CACHE_KEYS_TO_SAVE_FIELD_DESC = new org.apache.thrift.protocol.TField("row_cache_keys_to_save", org.apache.thrift.protocol.TType.I32, (short)31); @@ -115,7 +116,6 @@ public class CfDef implements org.apache.thrift.TBase, jav public int id; // optional public int min_compaction_threshold; // optional public int max_compaction_threshold; // optional - public boolean replicate_on_write; // optional public String key_validation_class; // optional public ByteBuffer key_alias; // optional public String compaction_strategy; // optional @@ -130,6 +130,7 @@ public class CfDef implements org.apache.thrift.TBase, jav public int index_interval; // optional public String speculative_retry; // optional public List triggers; // optional + public String cells_per_row_to_cache; // optional /** * @deprecated */ @@ -158,6 +159,10 @@ public class CfDef implements org.apache.thrift.TBase, jav * @deprecated */ public double memtable_operations_in_millions; // optional + /** + * @deprecated + */ + public boolean replicate_on_write; // optional /** * @deprecated */ @@ -186,7 +191,6 @@ public class CfDef implements org.apache.thrift.TBase, jav ID((short)16, "id"), MIN_COMPACTION_THRESHOLD((short)17, "min_compaction_threshold"), MAX_COMPACTION_THRESHOLD((short)18, "max_compaction_threshold"), - REPLICATE_ON_WRITE((short)24, "replicate_on_write"), KEY_VALIDATION_CLASS((short)26, "key_validation_class"), KEY_ALIAS((short)28, "key_alias"), COMPACTION_STRATEGY((short)29, "compaction_strategy"), @@ -201,6 +205,7 @@ public class CfDef implements org.apache.thrift.TBase, jav INDEX_INTERVAL((short)41, "index_interval"), SPECULATIVE_RETRY((short)42, "speculative_retry"), TRIGGERS((short)43, "triggers"), + CELLS_PER_ROW_TO_CACHE((short)44, "cells_per_row_to_cache"), /** * @deprecated */ @@ -229,6 +234,10 @@ public class CfDef implements org.apache.thrift.TBase, jav * @deprecated */ MEMTABLE_OPERATIONS_IN_MILLIONS((short)23, "memtable_operations_in_millions"), + /** + * @deprecated + */ + REPLICATE_ON_WRITE((short)24, "replicate_on_write"), /** * @deprecated */ @@ -281,8 +290,6 @@ public class CfDef implements org.apache.thrift.TBase, jav return MIN_COMPACTION_THRESHOLD; case 18: // MAX_COMPACTION_THRESHOLD return MAX_COMPACTION_THRESHOLD; - case 24: // REPLICATE_ON_WRITE - return REPLICATE_ON_WRITE; case 26: // KEY_VALIDATION_CLASS return KEY_VALIDATION_CLASS; case 28: // KEY_ALIAS @@ -311,6 +318,8 @@ public class CfDef implements org.apache.thrift.TBase, jav return SPECULATIVE_RETRY; case 43: // TRIGGERS return TRIGGERS; + case 44: // CELLS_PER_ROW_TO_CACHE + return CELLS_PER_ROW_TO_CACHE; case 9: // ROW_CACHE_SIZE return ROW_CACHE_SIZE; case 11: // KEY_CACHE_SIZE @@ -325,6 +334,8 @@ public class CfDef implements org.apache.thrift.TBase, jav 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; case 25: // MERGE_SHARDS_CHANCE return MERGE_SHARDS_CHANCE; case 27: // ROW_CACHE_PROVIDER @@ -376,24 +387,24 @@ public class CfDef implements org.apache.thrift.TBase, jav private static final int __ID_ISSET_ID = 2; private static final int __MIN_COMPACTION_THRESHOLD_ISSET_ID = 3; private static final int __MAX_COMPACTION_THRESHOLD_ISSET_ID = 4; - private static final int __REPLICATE_ON_WRITE_ISSET_ID = 5; - private static final int __BLOOM_FILTER_FP_CHANCE_ISSET_ID = 6; - private static final int __DCLOCAL_READ_REPAIR_CHANCE_ISSET_ID = 7; - private static final int __POPULATE_IO_CACHE_ON_FLUSH_ISSET_ID = 8; - private static final int __MEMTABLE_FLUSH_PERIOD_IN_MS_ISSET_ID = 9; - private static final int __DEFAULT_TIME_TO_LIVE_ISSET_ID = 10; - private static final int __INDEX_INTERVAL_ISSET_ID = 11; - private static final int __ROW_CACHE_SIZE_ISSET_ID = 12; - private static final int __KEY_CACHE_SIZE_ISSET_ID = 13; - private static final int __ROW_CACHE_SAVE_PERIOD_IN_SECONDS_ISSET_ID = 14; - private static final int __KEY_CACHE_SAVE_PERIOD_IN_SECONDS_ISSET_ID = 15; - private static final int __MEMTABLE_FLUSH_AFTER_MINS_ISSET_ID = 16; - private static final int __MEMTABLE_THROUGHPUT_IN_MB_ISSET_ID = 17; - private static final int __MEMTABLE_OPERATIONS_IN_MILLIONS_ISSET_ID = 18; + private static final int __BLOOM_FILTER_FP_CHANCE_ISSET_ID = 5; + private static final int __DCLOCAL_READ_REPAIR_CHANCE_ISSET_ID = 6; + private static final int __POPULATE_IO_CACHE_ON_FLUSH_ISSET_ID = 7; + private static final int __MEMTABLE_FLUSH_PERIOD_IN_MS_ISSET_ID = 8; + private static final int __DEFAULT_TIME_TO_LIVE_ISSET_ID = 9; + private static final int __INDEX_INTERVAL_ISSET_ID = 10; + private static final int __ROW_CACHE_SIZE_ISSET_ID = 11; + private static final int __KEY_CACHE_SIZE_ISSET_ID = 12; + private static final int __ROW_CACHE_SAVE_PERIOD_IN_SECONDS_ISSET_ID = 13; + private static final int __KEY_CACHE_SAVE_PERIOD_IN_SECONDS_ISSET_ID = 14; + private static final int __MEMTABLE_FLUSH_AFTER_MINS_ISSET_ID = 15; + private static final int __MEMTABLE_THROUGHPUT_IN_MB_ISSET_ID = 16; + private static final int __MEMTABLE_OPERATIONS_IN_MILLIONS_ISSET_ID = 17; + private static final int __REPLICATE_ON_WRITE_ISSET_ID = 18; private static final int __MERGE_SHARDS_CHANCE_ISSET_ID = 19; private static final int __ROW_CACHE_KEYS_TO_SAVE_ISSET_ID = 20; private int __isset_bitfield = 0; - private _Fields optionals[] = {_Fields.COLUMN_TYPE,_Fields.COMPARATOR_TYPE,_Fields.SUBCOMPARATOR_TYPE,_Fields.COMMENT,_Fields.READ_REPAIR_CHANCE,_Fields.COLUMN_METADATA,_Fields.GC_GRACE_SECONDS,_Fields.DEFAULT_VALIDATION_CLASS,_Fields.ID,_Fields.MIN_COMPACTION_THRESHOLD,_Fields.MAX_COMPACTION_THRESHOLD,_Fields.REPLICATE_ON_WRITE,_Fields.KEY_VALIDATION_CLASS,_Fields.KEY_ALIAS,_Fields.COMPACTION_STRATEGY,_Fields.COMPACTION_STRATEGY_OPTIONS,_Fields.COMPRESSION_OPTIONS,_Fields.BLOOM_FILTER_FP_CHANCE,_Fields.CACHING,_Fields.DCLOCAL_READ_REPAIR_CHANCE,_Fields.POPULATE_IO_CACHE_ON_FLUSH,_Fields.MEMTABLE_FLUSH_PERIOD_IN_MS,_Fields.DEFAULT_TIME_TO_LIVE,_Fields.INDEX_INTERVAL,_Fields.SPECULATIVE_RETRY,_Fields.TRIGGERS,_Fields.ROW_CACHE_SIZE,_Fields.KEY_CACHE_SIZE,_Fields.ROW_CACHE_SAVE_PERIOD_IN_SECONDS,_Fields.KEY_CACHE_SAVE_PERIOD_IN_SECONDS,_Fields.MEMTABLE_FLUSH_AFTER_MINS,_Fields.MEMTABLE_THROUGHPUT_IN_MB,_Fields.MEMTABLE_OPERATIONS_IN_MILLIONS,_Fields.MERGE_SHARDS_CHANCE,_Fields.ROW_CACHE_PROVIDER,_Fields.ROW_CACHE_KEYS_TO_SAVE}; + private _Fields optionals[] = {_Fields.COLUMN_TYPE,_Fields.COMPARATOR_TYPE,_Fields.SUBCOMPARATOR_TYPE,_Fields.COMMENT,_Fields.READ_REPAIR_CHANCE,_Fields.COLUMN_METADATA,_Fields.GC_GRACE_SECONDS,_Fields.DEFAULT_VALIDATION_CLASS,_Fields.ID,_Fields.MIN_COMPACTION_THRESHOLD,_Fields.MAX_COMPACTION_THRESHOLD,_Fields.KEY_VALIDATION_CLASS,_Fields.KEY_ALIAS,_Fields.COMPACTION_STRATEGY,_Fields.COMPACTION_STRATEGY_OPTIONS,_Fields.COMPRESSION_OPTIONS,_Fields.BLOOM_FILTER_FP_CHANCE,_Fields.CACHING,_Fields.DCLOCAL_READ_REPAIR_CHANCE,_Fields.POPULATE_IO_CACHE_ON_FLUSH,_Fields.MEMTABLE_FLUSH_PERIOD_IN_MS,_Fields.DEFAULT_TIME_TO_LIVE,_Fields.INDEX_INTERVAL,_Fields.SPECULATIVE_RETRY,_Fields.TRIGGERS,_Fields.CELLS_PER_ROW_TO_CACHE,_Fields.ROW_CACHE_SIZE,_Fields.KEY_CACHE_SIZE,_Fields.ROW_CACHE_SAVE_PERIOD_IN_SECONDS,_Fields.KEY_CACHE_SAVE_PERIOD_IN_SECONDS,_Fields.MEMTABLE_FLUSH_AFTER_MINS,_Fields.MEMTABLE_THROUGHPUT_IN_MB,_Fields.MEMTABLE_OPERATIONS_IN_MILLIONS,_Fields.REPLICATE_ON_WRITE,_Fields.MERGE_SHARDS_CHANCE,_Fields.ROW_CACHE_PROVIDER,_Fields.ROW_CACHE_KEYS_TO_SAVE}; public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; static { Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); @@ -424,8 +435,6 @@ public class CfDef implements org.apache.thrift.TBase, jav new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I32))); tmpMap.put(_Fields.MAX_COMPACTION_THRESHOLD, new org.apache.thrift.meta_data.FieldMetaData("max_compaction_threshold", org.apache.thrift.TFieldRequirementType.OPTIONAL, new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I32))); - tmpMap.put(_Fields.REPLICATE_ON_WRITE, new org.apache.thrift.meta_data.FieldMetaData("replicate_on_write", org.apache.thrift.TFieldRequirementType.OPTIONAL, - new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.BOOL))); tmpMap.put(_Fields.KEY_VALIDATION_CLASS, new org.apache.thrift.meta_data.FieldMetaData("key_validation_class", org.apache.thrift.TFieldRequirementType.OPTIONAL, new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); tmpMap.put(_Fields.KEY_ALIAS, new org.apache.thrift.meta_data.FieldMetaData("key_alias", org.apache.thrift.TFieldRequirementType.OPTIONAL, @@ -459,6 +468,8 @@ public class CfDef implements org.apache.thrift.TBase, jav tmpMap.put(_Fields.TRIGGERS, new org.apache.thrift.meta_data.FieldMetaData("triggers", org.apache.thrift.TFieldRequirementType.OPTIONAL, new org.apache.thrift.meta_data.ListMetaData(org.apache.thrift.protocol.TType.LIST, new org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType.STRUCT, TriggerDef.class)))); + tmpMap.put(_Fields.CELLS_PER_ROW_TO_CACHE, new org.apache.thrift.meta_data.FieldMetaData("cells_per_row_to_cache", org.apache.thrift.TFieldRequirementType.OPTIONAL, + new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); tmpMap.put(_Fields.ROW_CACHE_SIZE, new org.apache.thrift.meta_data.FieldMetaData("row_cache_size", org.apache.thrift.TFieldRequirementType.OPTIONAL, new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.DOUBLE))); tmpMap.put(_Fields.KEY_CACHE_SIZE, new org.apache.thrift.meta_data.FieldMetaData("key_cache_size", org.apache.thrift.TFieldRequirementType.OPTIONAL, @@ -473,6 +484,8 @@ public class CfDef implements org.apache.thrift.TBase, jav new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I32))); tmpMap.put(_Fields.MEMTABLE_OPERATIONS_IN_MILLIONS, new org.apache.thrift.meta_data.FieldMetaData("memtable_operations_in_millions", org.apache.thrift.TFieldRequirementType.OPTIONAL, new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.DOUBLE))); + tmpMap.put(_Fields.REPLICATE_ON_WRITE, new org.apache.thrift.meta_data.FieldMetaData("replicate_on_write", org.apache.thrift.TFieldRequirementType.OPTIONAL, + new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.BOOL))); tmpMap.put(_Fields.MERGE_SHARDS_CHANCE, new org.apache.thrift.meta_data.FieldMetaData("merge_shards_chance", org.apache.thrift.TFieldRequirementType.OPTIONAL, new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.DOUBLE))); tmpMap.put(_Fields.ROW_CACHE_PROVIDER, new org.apache.thrift.meta_data.FieldMetaData("row_cache_provider", org.apache.thrift.TFieldRequirementType.OPTIONAL, @@ -494,6 +507,8 @@ public class CfDef implements org.apache.thrift.TBase, jav this.speculative_retry = "NONE"; + this.cells_per_row_to_cache = "100"; + } public CfDef( @@ -543,7 +558,6 @@ public class CfDef implements org.apache.thrift.TBase, jav this.id = other.id; this.min_compaction_threshold = other.min_compaction_threshold; this.max_compaction_threshold = other.max_compaction_threshold; - this.replicate_on_write = other.replicate_on_write; if (other.isSetKey_validation_class()) { this.key_validation_class = other.key_validation_class; } @@ -581,6 +595,9 @@ public class CfDef implements org.apache.thrift.TBase, jav } this.triggers = __this__triggers; } + if (other.isSetCells_per_row_to_cache()) { + this.cells_per_row_to_cache = other.cells_per_row_to_cache; + } this.row_cache_size = other.row_cache_size; this.key_cache_size = other.key_cache_size; this.row_cache_save_period_in_seconds = other.row_cache_save_period_in_seconds; @@ -588,6 +605,7 @@ public class CfDef implements org.apache.thrift.TBase, jav 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; this.merge_shards_chance = other.merge_shards_chance; if (other.isSetRow_cache_provider()) { this.row_cache_provider = other.row_cache_provider; @@ -621,8 +639,6 @@ public class CfDef implements org.apache.thrift.TBase, jav this.min_compaction_threshold = 0; setMax_compaction_thresholdIsSet(false); this.max_compaction_threshold = 0; - setReplicate_on_writeIsSet(false); - this.replicate_on_write = false; this.key_validation_class = null; this.key_alias = null; this.compaction_strategy = null; @@ -645,6 +661,8 @@ public class CfDef implements org.apache.thrift.TBase, jav this.speculative_retry = "NONE"; this.triggers = null; + this.cells_per_row_to_cache = "100"; + setRow_cache_sizeIsSet(false); this.row_cache_size = 0.0; setKey_cache_sizeIsSet(false); @@ -659,6 +677,8 @@ public class CfDef implements org.apache.thrift.TBase, jav this.memtable_throughput_in_mb = 0; setMemtable_operations_in_millionsIsSet(false); this.memtable_operations_in_millions = 0.0; + setReplicate_on_writeIsSet(false); + this.replicate_on_write = false; setMerge_shards_chanceIsSet(false); this.merge_shards_chance = 0.0; this.row_cache_provider = null; @@ -988,29 +1008,6 @@ public class CfDef implements org.apache.thrift.TBase, jav __isset_bitfield = EncodingUtils.setBit(__isset_bitfield, __MAX_COMPACTION_THRESHOLD_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_bitfield = EncodingUtils.clearBit(__isset_bitfield, __REPLICATE_ON_WRITE_ISSET_ID); - } - - /** Returns true if field replicate_on_write is set (has been assigned a value) and false otherwise */ - public boolean isSetReplicate_on_write() { - return EncodingUtils.testBit(__isset_bitfield, __REPLICATE_ON_WRITE_ISSET_ID); - } - - public void setReplicate_on_writeIsSet(boolean value) { - __isset_bitfield = EncodingUtils.setBit(__isset_bitfield, __REPLICATE_ON_WRITE_ISSET_ID, value); - } - public String getKey_validation_class() { return this.key_validation_class; } @@ -1388,6 +1385,30 @@ public class CfDef implements org.apache.thrift.TBase, jav } } + public String getCells_per_row_to_cache() { + return this.cells_per_row_to_cache; + } + + public CfDef setCells_per_row_to_cache(String cells_per_row_to_cache) { + this.cells_per_row_to_cache = cells_per_row_to_cache; + return this; + } + + public void unsetCells_per_row_to_cache() { + this.cells_per_row_to_cache = null; + } + + /** Returns true if field cells_per_row_to_cache is set (has been assigned a value) and false otherwise */ + public boolean isSetCells_per_row_to_cache() { + return this.cells_per_row_to_cache != null; + } + + public void setCells_per_row_to_cacheIsSet(boolean value) { + if (!value) { + this.cells_per_row_to_cache = null; + } + } + /** * @deprecated */ @@ -1591,6 +1612,35 @@ public class CfDef implements org.apache.thrift.TBase, jav __isset_bitfield = EncodingUtils.setBit(__isset_bitfield, __MEMTABLE_OPERATIONS_IN_MILLIONS_ISSET_ID, value); } + /** + * @deprecated + */ + public boolean isReplicate_on_write() { + return this.replicate_on_write; + } + + /** + * @deprecated + */ + 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_bitfield = EncodingUtils.clearBit(__isset_bitfield, __REPLICATE_ON_WRITE_ISSET_ID); + } + + /** Returns true if field replicate_on_write is set (has been assigned a value) and false otherwise */ + public boolean isSetReplicate_on_write() { + return EncodingUtils.testBit(__isset_bitfield, __REPLICATE_ON_WRITE_ISSET_ID); + } + + public void setReplicate_on_writeIsSet(boolean value) { + __isset_bitfield = EncodingUtils.setBit(__isset_bitfield, __REPLICATE_ON_WRITE_ISSET_ID, value); + } + /** * @deprecated */ @@ -1785,14 +1835,6 @@ public class CfDef implements org.apache.thrift.TBase, jav } break; - case REPLICATE_ON_WRITE: - if (value == null) { - unsetReplicate_on_write(); - } else { - setReplicate_on_write((Boolean)value); - } - break; - case KEY_VALIDATION_CLASS: if (value == null) { unsetKey_validation_class(); @@ -1905,6 +1947,14 @@ public class CfDef implements org.apache.thrift.TBase, jav } break; + case CELLS_PER_ROW_TO_CACHE: + if (value == null) { + unsetCells_per_row_to_cache(); + } else { + setCells_per_row_to_cache((String)value); + } + break; + case ROW_CACHE_SIZE: if (value == null) { unsetRow_cache_size(); @@ -1961,6 +2011,14 @@ public class CfDef implements org.apache.thrift.TBase, jav } break; + case REPLICATE_ON_WRITE: + if (value == null) { + unsetReplicate_on_write(); + } else { + setReplicate_on_write((Boolean)value); + } + break; + case MERGE_SHARDS_CHANCE: if (value == null) { unsetMerge_shards_chance(); @@ -2029,9 +2087,6 @@ public class CfDef implements org.apache.thrift.TBase, jav case MAX_COMPACTION_THRESHOLD: return Integer.valueOf(getMax_compaction_threshold()); - case REPLICATE_ON_WRITE: - return Boolean.valueOf(isReplicate_on_write()); - case KEY_VALIDATION_CLASS: return getKey_validation_class(); @@ -2074,6 +2129,9 @@ public class CfDef implements org.apache.thrift.TBase, jav case TRIGGERS: return getTriggers(); + case CELLS_PER_ROW_TO_CACHE: + return getCells_per_row_to_cache(); + case ROW_CACHE_SIZE: return Double.valueOf(getRow_cache_size()); @@ -2095,6 +2153,9 @@ public class CfDef implements org.apache.thrift.TBase, jav case MEMTABLE_OPERATIONS_IN_MILLIONS: return Double.valueOf(getMemtable_operations_in_millions()); + case REPLICATE_ON_WRITE: + return Boolean.valueOf(isReplicate_on_write()); + case MERGE_SHARDS_CHANCE: return Double.valueOf(getMerge_shards_chance()); @@ -2141,8 +2202,6 @@ public class CfDef implements org.apache.thrift.TBase, jav return isSetMin_compaction_threshold(); case MAX_COMPACTION_THRESHOLD: return isSetMax_compaction_threshold(); - case REPLICATE_ON_WRITE: - return isSetReplicate_on_write(); case KEY_VALIDATION_CLASS: return isSetKey_validation_class(); case KEY_ALIAS: @@ -2171,6 +2230,8 @@ public class CfDef implements org.apache.thrift.TBase, jav return isSetSpeculative_retry(); case TRIGGERS: return isSetTriggers(); + case CELLS_PER_ROW_TO_CACHE: + return isSetCells_per_row_to_cache(); case ROW_CACHE_SIZE: return isSetRow_cache_size(); case KEY_CACHE_SIZE: @@ -2185,6 +2246,8 @@ public class CfDef implements org.apache.thrift.TBase, jav return isSetMemtable_throughput_in_mb(); case MEMTABLE_OPERATIONS_IN_MILLIONS: return isSetMemtable_operations_in_millions(); + case REPLICATE_ON_WRITE: + return isSetReplicate_on_write(); case MERGE_SHARDS_CHANCE: return isSetMerge_shards_chance(); case ROW_CACHE_PROVIDER: @@ -2325,15 +2388,6 @@ public class CfDef implements org.apache.thrift.TBase, jav 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; - } - boolean this_present_key_validation_class = true && this.isSetKey_validation_class(); boolean that_present_key_validation_class = true && that.isSetKey_validation_class(); if (this_present_key_validation_class || that_present_key_validation_class) { @@ -2460,6 +2514,15 @@ public class CfDef implements org.apache.thrift.TBase, jav return false; } + boolean this_present_cells_per_row_to_cache = true && this.isSetCells_per_row_to_cache(); + boolean that_present_cells_per_row_to_cache = true && that.isSetCells_per_row_to_cache(); + if (this_present_cells_per_row_to_cache || that_present_cells_per_row_to_cache) { + if (!(this_present_cells_per_row_to_cache && that_present_cells_per_row_to_cache)) + return false; + if (!this.cells_per_row_to_cache.equals(that.cells_per_row_to_cache)) + return false; + } + boolean this_present_row_cache_size = true && this.isSetRow_cache_size(); boolean that_present_row_cache_size = true && that.isSetRow_cache_size(); if (this_present_row_cache_size || that_present_row_cache_size) { @@ -2523,6 +2586,15 @@ public class CfDef implements org.apache.thrift.TBase, jav 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; + } + boolean this_present_merge_shards_chance = true && this.isSetMerge_shards_chance(); boolean that_present_merge_shards_chance = true && that.isSetMerge_shards_chance(); if (this_present_merge_shards_chance || that_present_merge_shards_chance) { @@ -2622,11 +2694,6 @@ public class CfDef implements org.apache.thrift.TBase, jav if (present_max_compaction_threshold) builder.append(max_compaction_threshold); - 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); - boolean present_key_validation_class = true && (isSetKey_validation_class()); builder.append(present_key_validation_class); if (present_key_validation_class) @@ -2697,6 +2764,11 @@ public class CfDef implements org.apache.thrift.TBase, jav if (present_triggers) builder.append(triggers); + boolean present_cells_per_row_to_cache = true && (isSetCells_per_row_to_cache()); + builder.append(present_cells_per_row_to_cache); + if (present_cells_per_row_to_cache) + builder.append(cells_per_row_to_cache); + boolean present_row_cache_size = true && (isSetRow_cache_size()); builder.append(present_row_cache_size); if (present_row_cache_size) @@ -2732,6 +2804,11 @@ public class CfDef implements org.apache.thrift.TBase, jav 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); + boolean present_merge_shards_chance = true && (isSetMerge_shards_chance()); builder.append(present_merge_shards_chance); if (present_merge_shards_chance) @@ -2888,16 +2965,6 @@ public class CfDef implements org.apache.thrift.TBase, jav return lastComparison; } } - lastComparison = Boolean.valueOf(isSetReplicate_on_write()).compareTo(other.isSetReplicate_on_write()); - if (lastComparison != 0) { - return lastComparison; - } - if (isSetReplicate_on_write()) { - lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.replicate_on_write, other.replicate_on_write); - if (lastComparison != 0) { - return lastComparison; - } - } lastComparison = Boolean.valueOf(isSetKey_validation_class()).compareTo(other.isSetKey_validation_class()); if (lastComparison != 0) { return lastComparison; @@ -3038,6 +3105,16 @@ public class CfDef implements org.apache.thrift.TBase, jav return lastComparison; } } + lastComparison = Boolean.valueOf(isSetCells_per_row_to_cache()).compareTo(other.isSetCells_per_row_to_cache()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetCells_per_row_to_cache()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.cells_per_row_to_cache, other.cells_per_row_to_cache); + if (lastComparison != 0) { + return lastComparison; + } + } lastComparison = Boolean.valueOf(isSetRow_cache_size()).compareTo(other.isSetRow_cache_size()); if (lastComparison != 0) { return lastComparison; @@ -3108,6 +3185,16 @@ public class CfDef implements org.apache.thrift.TBase, jav return lastComparison; } } + lastComparison = Boolean.valueOf(isSetReplicate_on_write()).compareTo(other.isSetReplicate_on_write()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetReplicate_on_write()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.replicate_on_write, other.replicate_on_write); + if (lastComparison != 0) { + return lastComparison; + } + } lastComparison = Boolean.valueOf(isSetMerge_shards_chance()).compareTo(other.isSetMerge_shards_chance()); if (lastComparison != 0) { return lastComparison; @@ -3263,12 +3350,6 @@ public class CfDef implements org.apache.thrift.TBase, jav sb.append(this.max_compaction_threshold); first = false; } - if (isSetReplicate_on_write()) { - if (!first) sb.append(", "); - sb.append("replicate_on_write:"); - sb.append(this.replicate_on_write); - first = false; - } if (isSetKey_validation_class()) { if (!first) sb.append(", "); sb.append("key_validation_class:"); @@ -3385,6 +3466,16 @@ public class CfDef implements org.apache.thrift.TBase, jav } first = false; } + if (isSetCells_per_row_to_cache()) { + if (!first) sb.append(", "); + sb.append("cells_per_row_to_cache:"); + if (this.cells_per_row_to_cache == null) { + sb.append("null"); + } else { + sb.append(this.cells_per_row_to_cache); + } + first = false; + } if (isSetRow_cache_size()) { if (!first) sb.append(", "); sb.append("row_cache_size:"); @@ -3427,6 +3518,12 @@ public class CfDef implements org.apache.thrift.TBase, jav 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; + } if (isSetMerge_shards_chance()) { if (!first) sb.append(", "); sb.append("merge_shards_chance:"); @@ -3615,14 +3712,6 @@ public class CfDef implements org.apache.thrift.TBase, jav org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); } break; - case 24: // REPLICATE_ON_WRITE - if (schemeField.type == org.apache.thrift.protocol.TType.BOOL) { - struct.replicate_on_write = iprot.readBool(); - struct.setReplicate_on_writeIsSet(true); - } else { - org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); - } - break; case 26: // KEY_VALIDATION_CLASS if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { struct.key_validation_class = iprot.readString(); @@ -3770,6 +3859,14 @@ public class CfDef implements org.apache.thrift.TBase, jav org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); } break; + case 44: // CELLS_PER_ROW_TO_CACHE + if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { + struct.cells_per_row_to_cache = iprot.readString(); + struct.setCells_per_row_to_cacheIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; case 9: // ROW_CACHE_SIZE if (schemeField.type == org.apache.thrift.protocol.TType.DOUBLE) { struct.row_cache_size = iprot.readDouble(); @@ -3826,6 +3923,14 @@ public class CfDef implements org.apache.thrift.TBase, jav org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); } break; + case 24: // REPLICATE_ON_WRITE + if (schemeField.type == org.apache.thrift.protocol.TType.BOOL) { + struct.replicate_on_write = iprot.readBool(); + struct.setReplicate_on_writeIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; case 25: // MERGE_SHARDS_CHANCE if (schemeField.type == org.apache.thrift.protocol.TType.DOUBLE) { struct.merge_shards_chance = iprot.readDouble(); @@ -4115,6 +4220,13 @@ public class CfDef implements org.apache.thrift.TBase, jav oprot.writeFieldEnd(); } } + if (struct.cells_per_row_to_cache != null) { + if (struct.isSetCells_per_row_to_cache()) { + oprot.writeFieldBegin(CELLS_PER_ROW_TO_CACHE_FIELD_DESC); + oprot.writeString(struct.cells_per_row_to_cache); + oprot.writeFieldEnd(); + } + } oprot.writeFieldStop(); oprot.writeStructEnd(); } @@ -4168,49 +4280,49 @@ public class CfDef implements org.apache.thrift.TBase, jav if (struct.isSetMax_compaction_threshold()) { optionals.set(10); } - if (struct.isSetReplicate_on_write()) { + if (struct.isSetKey_validation_class()) { optionals.set(11); } - if (struct.isSetKey_validation_class()) { + if (struct.isSetKey_alias()) { optionals.set(12); } - if (struct.isSetKey_alias()) { + if (struct.isSetCompaction_strategy()) { optionals.set(13); } - if (struct.isSetCompaction_strategy()) { + if (struct.isSetCompaction_strategy_options()) { optionals.set(14); } - if (struct.isSetCompaction_strategy_options()) { + if (struct.isSetCompression_options()) { optionals.set(15); } - if (struct.isSetCompression_options()) { + if (struct.isSetBloom_filter_fp_chance()) { optionals.set(16); } - if (struct.isSetBloom_filter_fp_chance()) { + if (struct.isSetCaching()) { optionals.set(17); } - if (struct.isSetCaching()) { + if (struct.isSetDclocal_read_repair_chance()) { optionals.set(18); } - if (struct.isSetDclocal_read_repair_chance()) { + if (struct.isSetPopulate_io_cache_on_flush()) { optionals.set(19); } - if (struct.isSetPopulate_io_cache_on_flush()) { + if (struct.isSetMemtable_flush_period_in_ms()) { optionals.set(20); } - if (struct.isSetMemtable_flush_period_in_ms()) { + if (struct.isSetDefault_time_to_live()) { optionals.set(21); } - if (struct.isSetDefault_time_to_live()) { + if (struct.isSetIndex_interval()) { optionals.set(22); } - if (struct.isSetIndex_interval()) { + if (struct.isSetSpeculative_retry()) { optionals.set(23); } - if (struct.isSetSpeculative_retry()) { + if (struct.isSetTriggers()) { optionals.set(24); } - if (struct.isSetTriggers()) { + if (struct.isSetCells_per_row_to_cache()) { optionals.set(25); } if (struct.isSetRow_cache_size()) { @@ -4234,16 +4346,19 @@ public class CfDef implements org.apache.thrift.TBase, jav if (struct.isSetMemtable_operations_in_millions()) { optionals.set(32); } - if (struct.isSetMerge_shards_chance()) { + if (struct.isSetReplicate_on_write()) { optionals.set(33); } - if (struct.isSetRow_cache_provider()) { + if (struct.isSetMerge_shards_chance()) { optionals.set(34); } - if (struct.isSetRow_cache_keys_to_save()) { + if (struct.isSetRow_cache_provider()) { optionals.set(35); } - oprot.writeBitSet(optionals, 36); + if (struct.isSetRow_cache_keys_to_save()) { + optionals.set(36); + } + oprot.writeBitSet(optionals, 37); if (struct.isSetColumn_type()) { oprot.writeString(struct.column_type); } @@ -4283,9 +4398,6 @@ public class CfDef implements org.apache.thrift.TBase, jav if (struct.isSetMax_compaction_threshold()) { oprot.writeI32(struct.max_compaction_threshold); } - if (struct.isSetReplicate_on_write()) { - oprot.writeBool(struct.replicate_on_write); - } if (struct.isSetKey_validation_class()) { oprot.writeString(struct.key_validation_class); } @@ -4348,6 +4460,9 @@ public class CfDef implements org.apache.thrift.TBase, jav } } } + if (struct.isSetCells_per_row_to_cache()) { + oprot.writeString(struct.cells_per_row_to_cache); + } if (struct.isSetRow_cache_size()) { oprot.writeDouble(struct.row_cache_size); } @@ -4369,6 +4484,9 @@ public class CfDef implements org.apache.thrift.TBase, jav if (struct.isSetMemtable_operations_in_millions()) { oprot.writeDouble(struct.memtable_operations_in_millions); } + if (struct.isSetReplicate_on_write()) { + oprot.writeBool(struct.replicate_on_write); + } if (struct.isSetMerge_shards_chance()) { oprot.writeDouble(struct.merge_shards_chance); } @@ -4387,7 +4505,7 @@ public class CfDef implements org.apache.thrift.TBase, jav struct.setKeyspaceIsSet(true); struct.name = iprot.readString(); struct.setNameIsSet(true); - BitSet incoming = iprot.readBitSet(36); + BitSet incoming = iprot.readBitSet(37); if (incoming.get(0)) { struct.column_type = iprot.readString(); struct.setColumn_typeIsSet(true); @@ -4443,22 +4561,18 @@ public class CfDef implements org.apache.thrift.TBase, jav struct.setMax_compaction_thresholdIsSet(true); } if (incoming.get(11)) { - struct.replicate_on_write = iprot.readBool(); - struct.setReplicate_on_writeIsSet(true); - } - if (incoming.get(12)) { struct.key_validation_class = iprot.readString(); struct.setKey_validation_classIsSet(true); } - if (incoming.get(13)) { + if (incoming.get(12)) { struct.key_alias = iprot.readBinary(); struct.setKey_aliasIsSet(true); } - if (incoming.get(14)) { + if (incoming.get(13)) { struct.compaction_strategy = iprot.readString(); struct.setCompaction_strategyIsSet(true); } - if (incoming.get(15)) { + if (incoming.get(14)) { { org.apache.thrift.protocol.TMap _map135 = new org.apache.thrift.protocol.TMap(org.apache.thrift.protocol.TType.STRING, org.apache.thrift.protocol.TType.STRING, iprot.readI32()); struct.compaction_strategy_options = new HashMap(2*_map135.size); @@ -4473,7 +4587,7 @@ public class CfDef implements org.apache.thrift.TBase, jav } struct.setCompaction_strategy_optionsIsSet(true); } - if (incoming.get(16)) { + if (incoming.get(15)) { { org.apache.thrift.protocol.TMap _map139 = new org.apache.thrift.protocol.TMap(org.apache.thrift.protocol.TType.STRING, org.apache.thrift.protocol.TType.STRING, iprot.readI32()); struct.compression_options = new HashMap(2*_map139.size); @@ -4488,39 +4602,39 @@ public class CfDef implements org.apache.thrift.TBase, jav } struct.setCompression_optionsIsSet(true); } - if (incoming.get(17)) { + if (incoming.get(16)) { struct.bloom_filter_fp_chance = iprot.readDouble(); struct.setBloom_filter_fp_chanceIsSet(true); } - if (incoming.get(18)) { + if (incoming.get(17)) { struct.caching = iprot.readString(); struct.setCachingIsSet(true); } - if (incoming.get(19)) { + if (incoming.get(18)) { struct.dclocal_read_repair_chance = iprot.readDouble(); struct.setDclocal_read_repair_chanceIsSet(true); } - if (incoming.get(20)) { + if (incoming.get(19)) { struct.populate_io_cache_on_flush = iprot.readBool(); struct.setPopulate_io_cache_on_flushIsSet(true); } - if (incoming.get(21)) { + if (incoming.get(20)) { struct.memtable_flush_period_in_ms = iprot.readI32(); struct.setMemtable_flush_period_in_msIsSet(true); } - if (incoming.get(22)) { + if (incoming.get(21)) { struct.default_time_to_live = iprot.readI32(); struct.setDefault_time_to_liveIsSet(true); } - if (incoming.get(23)) { + if (incoming.get(22)) { struct.index_interval = iprot.readI32(); struct.setIndex_intervalIsSet(true); } - if (incoming.get(24)) { + if (incoming.get(23)) { struct.speculative_retry = iprot.readString(); struct.setSpeculative_retryIsSet(true); } - if (incoming.get(25)) { + if (incoming.get(24)) { { org.apache.thrift.protocol.TList _list143 = new org.apache.thrift.protocol.TList(org.apache.thrift.protocol.TType.STRUCT, iprot.readI32()); struct.triggers = new ArrayList(_list143.size); @@ -4534,6 +4648,10 @@ public class CfDef implements org.apache.thrift.TBase, jav } struct.setTriggersIsSet(true); } + if (incoming.get(25)) { + struct.cells_per_row_to_cache = iprot.readString(); + struct.setCells_per_row_to_cacheIsSet(true); + } if (incoming.get(26)) { struct.row_cache_size = iprot.readDouble(); struct.setRow_cache_sizeIsSet(true); @@ -4563,14 +4681,18 @@ public class CfDef implements org.apache.thrift.TBase, jav struct.setMemtable_operations_in_millionsIsSet(true); } if (incoming.get(33)) { + struct.replicate_on_write = iprot.readBool(); + struct.setReplicate_on_writeIsSet(true); + } + if (incoming.get(34)) { struct.merge_shards_chance = iprot.readDouble(); struct.setMerge_shards_chanceIsSet(true); } - if (incoming.get(34)) { + if (incoming.get(35)) { struct.row_cache_provider = iprot.readString(); struct.setRow_cache_providerIsSet(true); } - if (incoming.get(35)) { + if (incoming.get(36)) { struct.row_cache_keys_to_save = iprot.readI32(); struct.setRow_cache_keys_to_saveIsSet(true); } diff --git a/interface/thrift/gen-java/org/apache/cassandra/thrift/cassandraConstants.java b/interface/thrift/gen-java/org/apache/cassandra/thrift/cassandraConstants.java index 81b0ffbae2..a795fc44da 100644 --- a/interface/thrift/gen-java/org/apache/cassandra/thrift/cassandraConstants.java +++ b/interface/thrift/gen-java/org/apache/cassandra/thrift/cassandraConstants.java @@ -56,6 +56,6 @@ import org.slf4j.LoggerFactory; public class cassandraConstants { - public static final String VERSION = "19.39.0"; + public static final String VERSION = "19.38.1"; } diff --git a/pylib/cqlshlib/cql3handling.py b/pylib/cqlshlib/cql3handling.py index c4fa97dcfa..1e1b4d5880 100644 --- a/pylib/cqlshlib/cql3handling.py +++ b/pylib/cqlshlib/cql3handling.py @@ -63,6 +63,7 @@ class Cql3ParsingRuleSet(CqlParsingRuleSet): columnfamily_layout_options = ( ('bloom_filter_fp_chance', None), ('caching', None), + ('rows_per_partition_to_cache', None), ('comment', None), ('dclocal_read_repair_chance', 'local_read_repair_chance'), ('gc_grace_seconds', None), diff --git a/src/java/org/apache/cassandra/cli/CliClient.java b/src/java/org/apache/cassandra/cli/CliClient.java index e842636e78..b30bee1f2b 100644 --- a/src/java/org/apache/cassandra/cli/CliClient.java +++ b/src/java/org/apache/cassandra/cli/CliClient.java @@ -140,6 +140,7 @@ public class CliClient INDEX_INTERVAL, MEMTABLE_FLUSH_PERIOD_IN_MS, CACHING, + CELLS_PER_ROW_TO_CACHE, DEFAULT_TIME_TO_LIVE, SPECULATIVE_RETRY, POPULATE_IO_CACHE_ON_FLUSH, @@ -1348,6 +1349,9 @@ public class CliClient case CACHING: cfDef.setCaching(CliUtils.unescapeSQLString(mValue)); break; + case CELLS_PER_ROW_TO_CACHE: + cfDef.setCells_per_row_to_cache(mValue); + break; case DEFAULT_TIME_TO_LIVE: cfDef.setDefault_time_to_live(Integer.parseInt(mValue)); break; @@ -1819,6 +1823,7 @@ public class CliClient writeAttr(output, false, "replicate_on_write", cfDef.replicate_on_write); writeAttr(output, false, "compaction_strategy", cfDef.compaction_strategy); writeAttr(output, false, "caching", cfDef.caching); + writeAttr(output, false, "cells_per_row_to_cache", cfDef.cells_per_row_to_cache); writeAttr(output, false, "default_time_to_live", cfDef.default_time_to_live); writeAttr(output, false, "speculative_retry", cfDef.speculative_retry); diff --git a/src/java/org/apache/cassandra/config/CFMetaData.java b/src/java/org/apache/cassandra/config/CFMetaData.java index eeb5c68554..f600b839e0 100644 --- a/src/java/org/apache/cassandra/config/CFMetaData.java +++ b/src/java/org/apache/cassandra/config/CFMetaData.java @@ -84,6 +84,7 @@ public final class CFMetaData public final static SpeculativeRetry DEFAULT_SPECULATIVE_RETRY = new SpeculativeRetry(SpeculativeRetry.RetryType.PERCENTILE, 0.99); public final static int DEFAULT_INDEX_INTERVAL = 128; public final static boolean DEFAULT_POPULATE_IO_CACHE_ON_FLUSH = false; + public final static RowsPerPartitionToCache DEFAULT_ROWS_PER_PARTITION_TO_CACHE = new RowsPerPartitionToCache(100, RowsPerPartitionToCache.Type.HEAD); // Note that this is the default only for user created tables public final static String DEFAULT_COMPRESSOR = LZ4Compressor.class.getCanonicalName(); @@ -145,6 +146,7 @@ public final class CFMetaData + "populate_io_cache_on_flush boolean," + "index_interval int," + "dropped_columns map," + + "rows_per_partition_to_cache text," + "PRIMARY KEY (keyspace_name, columnfamily_name)" + ") WITH COMMENT='ColumnFamily definitions' AND gc_grace_seconds=8640"); @@ -311,6 +313,49 @@ public final class CFMetaData } } + public static class RowsPerPartitionToCache + { + public enum Type + { + ALL, HEAD + } + public final int rowsToCache; + public final Type type; + + private RowsPerPartitionToCache(int rowsToCache, Type type) + { + this.rowsToCache = rowsToCache; + this.type = type; + } + + public static RowsPerPartitionToCache fromString(String rpptc) + { + if (rpptc.equalsIgnoreCase("all")) + return new RowsPerPartitionToCache(Integer.MAX_VALUE, Type.ALL); + return new RowsPerPartitionToCache(Integer.parseInt(rpptc), Type.HEAD); + } + + public boolean cacheFullPartitions() + { + return type == Type.ALL; + } + + public String toString() + { + if (rowsToCache == Integer.MAX_VALUE) + return "ALL"; + return String.valueOf(rowsToCache); + } + + public boolean equals(Object rhs) + { + if (!(rhs instanceof RowsPerPartitionToCache)) + return false; + RowsPerPartitionToCache rppc = (RowsPerPartitionToCache)rhs; + return rowsToCache == rppc.rowsToCache && type == rppc.type; + } + } + public static class SpeculativeRetry { public enum RetryType @@ -407,7 +452,7 @@ public final class CFMetaData private volatile Map droppedColumns = new HashMap<>(); private volatile Map triggers = new HashMap<>(); private volatile boolean isPurged = false; - + private volatile RowsPerPartitionToCache rowsPerPartitionToCache = DEFAULT_ROWS_PER_PARTITION_TO_CACHE; /* * All CQL3 columns definition are stored in the columnMetadata map. * On top of that, we keep separated collection of each kind of definition, to @@ -450,6 +495,7 @@ public final class CFMetaData public CFMetaData populateIoCacheOnFlush(boolean prop) {populateIoCacheOnFlush = prop; return this;} public CFMetaData droppedColumns(Map cols) {droppedColumns = cols; return this;} public CFMetaData triggers(Map prop) {triggers = prop; return this;} + public CFMetaData rowsPerPartitionToCache(RowsPerPartitionToCache prop) { rowsPerPartitionToCache = prop; return this; } /** * Create new ColumnFamily metadata with generated random ID. @@ -636,6 +682,7 @@ public final class CFMetaData .populateIoCacheOnFlush(oldCFMD.populateIoCacheOnFlush) .droppedColumns(new HashMap<>(oldCFMD.droppedColumns)) .triggers(new HashMap<>(oldCFMD.triggers)) + .rowsPerPartitionToCache(oldCFMD.rowsPerPartitionToCache) .rebuild(); } @@ -813,6 +860,11 @@ public final class CFMetaData return caching; } + public RowsPerPartitionToCache getRowsPerPartitionToCache() + { + return rowsPerPartitionToCache; + } + public int getIndexInterval() { return indexInterval; @@ -876,6 +928,7 @@ public final class CFMetaData if (populateIoCacheOnFlush != rhs.populateIoCacheOnFlush) return false; if (Objects.equal(droppedColumns, rhs.droppedColumns)) return false; if (Objects.equal(triggers, rhs.triggers)) return false; + if (!Objects.equal(rowsPerPartitionToCache, rhs.rowsPerPartitionToCache)) return false; return true; } @@ -908,6 +961,7 @@ public final class CFMetaData .append(populateIoCacheOnFlush) .append(droppedColumns) .append(triggers) + .append(rowsPerPartitionToCache) .toHashCode(); } @@ -964,6 +1018,8 @@ public final class CFMetaData cf_def.setDefault_time_to_live(CFMetaData.DEFAULT_DEFAULT_TIME_TO_LIVE); if (!cf_def.isSetDclocal_read_repair_chance()) cf_def.setDclocal_read_repair_chance(CFMetaData.DEFAULT_DCLOCAL_READ_REPAIR_CHANCE); + if (!cf_def.isSetCells_per_row_to_cache()) + cf_def.setCells_per_row_to_cache(CFMetaData.DEFAULT_ROWS_PER_PARTITION_TO_CACHE.toString()); } public static CFMetaData fromThrift(org.apache.cassandra.thrift.CfDef cf_def) throws InvalidRequestException, ConfigurationException @@ -1022,6 +1078,8 @@ public final class CFMetaData newCFMD.populateIoCacheOnFlush(cf_def.populate_io_cache_on_flush); if (cf_def.isSetTriggers()) newCFMD.triggers(TriggerDefinition.fromThrift(cf_def.triggers)); + if (cf_def.isSetCells_per_row_to_cache()) + newCFMD.rowsPerPartitionToCache(RowsPerPartitionToCache.fromString(cf_def.cells_per_row_to_cache)); CompressionParameters cp = CompressionParameters.create(cf_def.compression_options); @@ -1117,6 +1175,7 @@ public final class CFMetaData bloomFilterFpChance = cfm.bloomFilterFpChance; memtableFlushPeriod = cfm.memtableFlushPeriod; caching = cfm.caching; + rowsPerPartitionToCache = cfm.rowsPerPartitionToCache; defaultTimeToLive = cfm.defaultTimeToLive; speculativeRetry = cfm.speculativeRetry; populateIoCacheOnFlush = cfm.populateIoCacheOnFlush; @@ -1265,6 +1324,7 @@ public final class CFMetaData def.setIndex_interval(indexInterval); def.setMemtable_flush_period_in_ms(memtableFlushPeriod); def.setCaching(caching.toString()); + def.setCells_per_row_to_cache(rowsPerPartitionToCache.toString()); def.setDefault_time_to_live(defaultTimeToLive); def.setSpeculative_retry(speculativeRetry.toString()); def.setTriggers(TriggerDefinition.toThrift(triggers)); @@ -1619,6 +1679,7 @@ public final class CFMetaData adder.add("memtable_flush_period_in_ms", memtableFlushPeriod); adder.add("caching", caching.toString()); + adder.add("rows_per_partition_to_cache", rowsPerPartitionToCache.toString()); adder.add("default_time_to_live", defaultTimeToLive); adder.add("compaction_strategy_class", compactionStrategyClass.getName()); adder.add("compression_parameters", json(compressionParameters.asThriftOptions())); @@ -1684,6 +1745,8 @@ public final class CFMetaData if (result.has("memtable_flush_period_in_ms")) cfm.memtableFlushPeriod(result.getInt("memtable_flush_period_in_ms")); cfm.caching(Caching.valueOf(result.getString("caching"))); + if (result.has("rows_per_partition_to_cache")) + cfm.rowsPerPartitionToCache(RowsPerPartitionToCache.fromString(result.getString("rows_per_partition_to_cache"))); if (result.has("default_time_to_live")) cfm.defaultTimeToLive(result.getInt("default_time_to_live")); if (result.has("speculative_retry")) @@ -2163,6 +2226,7 @@ public final class CFMetaData .append("bloomFilterFpChance", bloomFilterFpChance) .append("memtable_flush_period_in_ms", memtableFlushPeriod) .append("caching", caching) + .append("rowsPerPartitionToCache", rowsPerPartitionToCache) .append("defaultTimeToLive", defaultTimeToLive) .append("speculative_retry", speculativeRetry) .append("indexInterval", indexInterval) diff --git a/src/java/org/apache/cassandra/cql/AlterTableStatement.java b/src/java/org/apache/cassandra/cql/AlterTableStatement.java index b5ac46490c..2fda2126a1 100644 --- a/src/java/org/apache/cassandra/cql/AlterTableStatement.java +++ b/src/java/org/apache/cassandra/cql/AlterTableStatement.java @@ -180,6 +180,7 @@ public class AlterTableStatement cfm.minCompactionThreshold(minCompactionThreshold); cfm.maxCompactionThreshold(maxCompactionThreshold); cfm.caching(CFMetaData.Caching.fromString(cfProps.getPropertyString(CFPropDefs.KW_CACHING, cfm.getCaching().toString()))); + cfm.rowsPerPartitionToCache(CFMetaData.RowsPerPartitionToCache.fromString(cfProps.getPropertyString(CFPropDefs.KW_ROWS_PER_PARTITION_TO_CACHE, cfm.getRowsPerPartitionToCache().toString()))); cfm.defaultTimeToLive(cfProps.getPropertyInt(CFPropDefs.KW_DEFAULT_TIME_TO_LIVE, cfm.getDefaultTimeToLive())); cfm.speculativeRetry(CFMetaData.SpeculativeRetry.fromString(cfProps.getPropertyString(CFPropDefs.KW_SPECULATIVE_RETRY, cfm.getSpeculativeRetry().toString()))); cfm.populateIoCacheOnFlush(cfProps.getPropertyBoolean(CFPropDefs.KW_POPULATE_IO_CACHE_ON_FLUSH, cfm.populateIoCacheOnFlush())); diff --git a/src/java/org/apache/cassandra/cql/CFPropDefs.java b/src/java/org/apache/cassandra/cql/CFPropDefs.java index 2131d06d9d..42e840ea4d 100644 --- a/src/java/org/apache/cassandra/cql/CFPropDefs.java +++ b/src/java/org/apache/cassandra/cql/CFPropDefs.java @@ -49,6 +49,7 @@ public class CFPropDefs { public static final String KW_MAXCOMPACTIONTHRESHOLD = "max_compaction_threshold"; public static final String KW_COMPACTION_STRATEGY_CLASS = "compaction_strategy_class"; public static final String KW_CACHING = "caching"; + public static final String KW_ROWS_PER_PARTITION_TO_CACHE = "rows_per_partition_to_cache"; public static final String KW_DEFAULT_TIME_TO_LIVE = "default_time_to_live"; public static final String KW_SPECULATIVE_RETRY = "speculative_retry"; public static final String KW_POPULATE_IO_CACHE_ON_FLUSH = "populate_io_cache_on_flush"; @@ -91,6 +92,7 @@ public class CFPropDefs { keywords.add(KW_MAXCOMPACTIONTHRESHOLD); keywords.add(KW_COMPACTION_STRATEGY_CLASS); keywords.add(KW_CACHING); + keywords.add(KW_ROWS_PER_PARTITION_TO_CACHE); keywords.add(KW_DEFAULT_TIME_TO_LIVE); keywords.add(KW_SPECULATIVE_RETRY); keywords.add(KW_POPULATE_IO_CACHE_ON_FLUSH); diff --git a/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java b/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java index 7b5cbafe00..e568dd7dcb 100644 --- a/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java +++ b/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java @@ -197,6 +197,7 @@ public class CreateColumnFamilyStatement .compactionStrategyOptions(cfProps.compactionStrategyOptions) .compressionParameters(CompressionParameters.create(cfProps.compressionParameters)) .caching(CFMetaData.Caching.fromString(getPropertyString(CFPropDefs.KW_CACHING, CFMetaData.DEFAULT_CACHING_STRATEGY.toString()))) + .rowsPerPartitionToCache(CFMetaData.RowsPerPartitionToCache.fromString(cfProps.getPropertyString(CFPropDefs.KW_ROWS_PER_PARTITION_TO_CACHE, CFMetaData.DEFAULT_ROWS_PER_PARTITION_TO_CACHE.toString()))) .speculativeRetry(CFMetaData.SpeculativeRetry.fromString(getPropertyString(CFPropDefs.KW_SPECULATIVE_RETRY, CFMetaData.DEFAULT_SPECULATIVE_RETRY.toString()))) .bloomFilterFpChance(getPropertyDouble(CFPropDefs.KW_BF_FP_CHANCE, null)) .memtableFlushPeriod(getPropertyInt(CFPropDefs.KW_MEMTABLE_FLUSH_PERIOD, 0)) diff --git a/src/java/org/apache/cassandra/cql3/statements/CFPropDefs.java b/src/java/org/apache/cassandra/cql3/statements/CFPropDefs.java index 3929f3c863..50fabf3373 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CFPropDefs.java +++ b/src/java/org/apache/cassandra/cql3/statements/CFPropDefs.java @@ -25,6 +25,7 @@ import org.apache.cassandra.db.compaction.AbstractCompactionStrategy; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.SyntaxException; import org.apache.cassandra.io.compress.CompressionParameters; +import org.apache.cassandra.service.CacheService; public class CFPropDefs extends PropertyDefinitions { @@ -35,6 +36,7 @@ public class CFPropDefs extends PropertyDefinitions public static final String KW_MINCOMPACTIONTHRESHOLD = "min_threshold"; public static final String KW_MAXCOMPACTIONTHRESHOLD = "max_threshold"; public static final String KW_CACHING = "caching"; + public static final String KW_ROWS_PER_PARTITION_TO_CACHE = "rows_per_partition_to_cache"; public static final String KW_DEFAULT_TIME_TO_LIVE = "default_time_to_live"; public static final String KW_INDEX_INTERVAL = "index_interval"; public static final String KW_SPECULATIVE_RETRY = "speculative_retry"; @@ -57,6 +59,7 @@ public class CFPropDefs extends PropertyDefinitions keywords.add(KW_DCLOCALREADREPAIRCHANCE); keywords.add(KW_GCGRACESECONDS); keywords.add(KW_CACHING); + keywords.add(KW_ROWS_PER_PARTITION_TO_CACHE); keywords.add(KW_DEFAULT_TIME_TO_LIVE); keywords.add(KW_INDEX_INTERVAL); keywords.add(KW_SPECULATIVE_RETRY); @@ -153,6 +156,11 @@ public class CFPropDefs extends PropertyDefinitions cfm.minCompactionThreshold(minCompactionThreshold); cfm.maxCompactionThreshold(maxCompactionThreshold); cfm.caching(CFMetaData.Caching.fromString(getString(KW_CACHING, cfm.getCaching().toString()))); + CFMetaData.RowsPerPartitionToCache newRppc = CFMetaData.RowsPerPartitionToCache.fromString(getString(KW_ROWS_PER_PARTITION_TO_CACHE, cfm.getRowsPerPartitionToCache().toString())); + // we need to invalidate row cache if the amount of rows cached changes, otherwise we might serve out bad data. + if (!cfm.getRowsPerPartitionToCache().equals(newRppc)) + CacheService.instance.invalidateRowCacheForCf(cfm.cfId); + cfm.rowsPerPartitionToCache(newRppc); cfm.defaultTimeToLive(getInt(KW_DEFAULT_TIME_TO_LIVE, cfm.getDefaultTimeToLive())); cfm.speculativeRetry(CFMetaData.SpeculativeRetry.fromString(getString(KW_SPECULATIVE_RETRY, cfm.getSpeculativeRetry().toString()))); cfm.memtableFlushPeriod(getInt(KW_MEMTABLE_FLUSH_PERIOD, cfm.getMemtableFlushPeriod())); diff --git a/src/java/org/apache/cassandra/db/ColumnFamily.java b/src/java/org/apache/cassandra/db/ColumnFamily.java index 00f0e35bec..610e869919 100644 --- a/src/java/org/apache/cassandra/db/ColumnFamily.java +++ b/src/java/org/apache/cassandra/db/ColumnFamily.java @@ -42,6 +42,7 @@ import org.apache.cassandra.config.Schema; import org.apache.cassandra.db.composites.CellName; import org.apache.cassandra.db.composites.CellNameType; import org.apache.cassandra.db.composites.CellNames; +import org.apache.cassandra.db.filter.ColumnCounter; import org.apache.cassandra.db.filter.ColumnSlice; import org.apache.cassandra.io.sstable.ColumnNameHelper; import org.apache.cassandra.io.sstable.ColumnStats; @@ -87,6 +88,14 @@ public abstract class ColumnFamily implements Iterable, IRowCacheEntry return metadata.cfType; } + public int liveCQL3RowCount(long now) + { + ColumnCounter counter = getComparator().isDense() + ? new ColumnCounter(now) + : new ColumnCounter.GroupByPrefix(now, getComparator(), metadata.clusteringColumns().size()); + return counter.countAll(this).live(); + } + /** * Clones the column map. */ diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java index 14b35718d2..18f0612d6e 100644 --- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java +++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java @@ -58,6 +58,7 @@ import org.apache.cassandra.db.filter.ExtendedFilter; import org.apache.cassandra.db.filter.IDiskAtomFilter; import org.apache.cassandra.db.filter.QueryFilter; import org.apache.cassandra.db.composites.CellName; +import org.apache.cassandra.db.composites.Composites; import org.apache.cassandra.db.composites.CellNameType; import org.apache.cassandra.db.composites.Composite; import org.apache.cassandra.db.index.SecondaryIndex; @@ -372,7 +373,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean data.unreferenceSSTables(); indexManager.invalidate(); - invalidateCaches(); + CacheService.instance.invalidateRowCacheForCf(metadata.cfId); } /** @@ -1446,12 +1447,16 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean } /** - * fetch the row given by filter.key if it is in the cache; if not, read it from disk and cache it + * Fetch the row and columns given by filter.key if it is in the cache; if not, read it from disk and cache it + * + * If row is cached, and the filter given is within its bounds, we return from cache, otherwise from disk + * + * If row is not cached, we figure out what filter is "biggest", read that from disk, then + * filter the result and either cache that or return it. + * * @param cfId the column family to read the row from - * @param filter the columns being queried. Note that we still cache entire rows, but if a row is uncached - * and we race to cache it, only the winner will read the entire row - * @return the entire row for filter.key, if present in the cache (or we can cache it), or just the column - * specified by filter otherwise + * @param filter the columns being queried. + * @return the requested data for the filter provided */ private ColumnFamily getThroughCache(UUID cfId, QueryFilter filter) { @@ -1462,6 +1467,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean // attempt a sentinel-read-cache sequence. if a write invalidates our sentinel, we'll return our // (now potentially obsolete) data, but won't cache it. see CASSANDRA-3862 + // TODO: don't evict entire rows on writes (#2864) IRowCacheEntry cached = CacheService.instance.rowCache.get(key); if (cached != null) { @@ -1469,32 +1475,135 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean { // Some other read is trying to cache the value, just do a normal non-caching read Tracing.trace("Row cache miss (race)"); + metric.rowCacheMiss.inc(); return getTopLevelColumns(filter, Integer.MIN_VALUE); } - Tracing.trace("Row cache hit"); - return (ColumnFamily) cached; + + ColumnFamily cachedCf = (ColumnFamily)cached; + if (isFilterFullyCoveredBy(filter.filter, cachedCf, filter.timestamp)) + { + metric.rowCacheHit.inc(); + Tracing.trace("Row cache hit"); + return cachedCf; + } + + metric.rowCacheHitOutOfRange.inc(); + Tracing.trace("Ignoring row cache as cached value could not satisfy query"); + return getTopLevelColumns(filter, Integer.MIN_VALUE); } + metric.rowCacheMiss.inc(); Tracing.trace("Row cache miss"); RowCacheSentinel sentinel = new RowCacheSentinel(); boolean sentinelSuccess = CacheService.instance.rowCache.putIfAbsent(key, sentinel); - + ColumnFamily data = null; + ColumnFamily toCache = null; try { - ColumnFamily data = getTopLevelColumns(QueryFilter.getIdentityFilter(filter.key, name, filter.timestamp), - Integer.MIN_VALUE); - if (sentinelSuccess && data != null) - CacheService.instance.rowCache.replace(key, sentinel, data); + // If we are explicitely asked to fill the cache with full partitions, we go ahead and query the whole thing + if (metadata.getRowsPerPartitionToCache().cacheFullPartitions()) + { + data = getTopLevelColumns(QueryFilter.getIdentityFilter(filter.key, name, filter.timestamp), Integer.MIN_VALUE); + toCache = data; + Tracing.trace("Populating row cache with the whole partition"); + if (sentinelSuccess && toCache != null) + CacheService.instance.rowCache.replace(key, sentinel, toCache); + return filterColumnFamily(data, filter); + } - return data; + // Otherwise, if we want to cache the result of the query we're about to do, we must make sure this query + // covers what needs to be cached. And if the user filter does not satisfy that, we sometimes extend said + // filter so we can populate the cache but only if: + // 1) we can guarantee it is a strict extension, i.e. that we will still fetch the data asked by the user. + // 2) the extension does not make us query more than getRowsPerPartitionToCache() (as a mean to limit the + // amount of extra work we'll do on a user query for the purpose of populating the cache). + // + // In practice, we can only guarantee those 2 points if the filter is one that queries the head of the + // partition (and if that filter actually counts CQL3 rows since that's what we cache and it would be + // bogus to compare the filter count to the 'rows to cache' otherwise). + if (filter.filter.isHeadFilter() && filter.filter.countCQL3Rows(metadata.comparator)) + { + SliceQueryFilter sliceFilter = (SliceQueryFilter)filter.filter; + int rowsToCache = metadata.getRowsPerPartitionToCache().rowsToCache; + + SliceQueryFilter cacheSlice = readFilterForCache(); + QueryFilter cacheFilter = new QueryFilter(filter.key, name, cacheSlice, filter.timestamp); + + // If the filter count is less than the number of rows cached, we simply extend it to make sure we do cover the + // number of rows to cache, and if that count is greater than the number of rows to cache, we simply filter what + // needs to be cached afterwards. + if (sliceFilter.count < rowsToCache) + { + toCache = getTopLevelColumns(cacheFilter, Integer.MIN_VALUE); + if (toCache != null) + { + Tracing.trace("Populating row cache ({} rows cached)", cacheSlice.lastCounted()); + data = filterColumnFamily(toCache, filter); + } + } + else + { + data = getTopLevelColumns(filter, Integer.MIN_VALUE); + if (data != null) + { + // The filter limit was greater than the number of rows to cache. But, if the filter had a non-empty + // finish bound, we may have gotten less than what needs to be cached, in which case we shouldn't cache it + // (otherwise a cache hit would assume the whole partition is cached which is not the case). + if (sliceFilter.finish().isEmpty() || sliceFilter.lastCounted() >= rowsToCache) + { + toCache = filterColumnFamily(data, cacheFilter); + Tracing.trace("Caching {} rows (out of {} requested)", cacheSlice.lastCounted(), sliceFilter.count); + } + else + { + Tracing.trace("Not populating row cache, not enough rows fetched ({} fetched but {} required for the cache)", sliceFilter.lastCounted(), rowsToCache); + } + } + } + + if (sentinelSuccess && toCache != null) + CacheService.instance.rowCache.replace(key, sentinel, toCache); + return data; + } + else + { + Tracing.trace("Fetching data but not populating cache as query does not query from the start of the partition"); + return getTopLevelColumns(filter, Integer.MIN_VALUE); + } } finally { - if (sentinelSuccess && data == null) + if (sentinelSuccess && toCache == null) invalidateCachedRow(key); } } + public SliceQueryFilter readFilterForCache() + { + // We create a new filter everytime before for now SliceQueryFilter is unfortunatly mutable. + return new SliceQueryFilter(ColumnSlice.ALL_COLUMNS_ARRAY, false, metadata.getRowsPerPartitionToCache().rowsToCache, metadata.clusteringColumns().size()); + } + + public boolean isFilterFullyCoveredBy(IDiskAtomFilter filter, ColumnFamily cachedCf, long now) + { + // We can use the cached value only if we know that no data it doesn't contain could be covered + // by the query filter, that is if: + // 1) either the whole partition is cached + // 2) or we can ensure than any data the filter selects are in the cached partition + + // When counting rows to decide if the whole row is cached, we should be careful with expiring + // columns: if we use a timestamp newer than the one that was used when populating the cache, we might + // end up deciding the whole partition is cached when it's really not (just some rows expired since the + // cf was cached). This is the reason for Integer.MIN_VALUE below. + boolean wholePartitionCached = cachedCf.liveCQL3RowCount(Integer.MIN_VALUE) < metadata.getRowsPerPartitionToCache().rowsToCache; + + // Contrarily to the "wholePartitionCached" check above, we do want isFullyCoveredBy to take the + // timestamp of the query into account when dealing with expired columns. Otherwise, we could think + // the cached partition has enough live rows to satisfy the filter when it doesn't because some + // are now expired. + return wholePartitionCached || filter.isFullyCoveredBy(cachedCf, now); + } + public int gcBefore(long now) { return (int) (now / 1000) - metadata.getGcGraceSeconds(); @@ -1527,7 +1636,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean return null; } - result = filterColumnFamily(cached, filter); + result = cached; } else { @@ -2085,14 +2194,12 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean return null; IRowCacheEntry cached = CacheService.instance.rowCache.getInternal(new RowCacheKey(metadata.cfId, key)); - return cached == null || cached instanceof RowCacheSentinel ? null : (ColumnFamily) cached; + return cached == null || cached instanceof RowCacheSentinel ? null : (ColumnFamily)cached; } private void invalidateCaches() { - for (RowCacheKey key : CacheService.instance.rowCache.getKeySet()) - if (key.cfId == metadata.cfId) - invalidateCachedRow(key); + CacheService.instance.invalidateRowCacheForCf(metadata.cfId); if (metadata.isCounter()) for (CounterCacheKey key : CacheService.instance.counterCache.getKeySet()) @@ -2100,6 +2207,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean CacheService.instance.counterCache.remove(key); } + /** * @return true if @param key is contained in the row cache */ @@ -2238,7 +2346,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean index.truncateBlocking(truncatedAt); SystemKeyspace.saveTruncationRecord(ColumnFamilyStore.this, truncatedAt, replayAfter); - + logger.debug("cleaning out row cache"); invalidateCaches(); } }; diff --git a/src/java/org/apache/cassandra/db/RowIteratorFactory.java b/src/java/org/apache/cassandra/db/RowIteratorFactory.java index 93698a0eb4..7077944725 100644 --- a/src/java/org/apache/cassandra/db/RowIteratorFactory.java +++ b/src/java/org/apache/cassandra/db/RowIteratorFactory.java @@ -23,6 +23,7 @@ import org.apache.cassandra.db.columniterator.IColumnIteratorFactory; import org.apache.cassandra.db.columniterator.LazyColumnIterator; import org.apache.cassandra.db.columniterator.OnDiskAtomIterator; import org.apache.cassandra.db.filter.QueryFilter; +import org.apache.cassandra.db.filter.IDiskAtomFilter; import org.apache.cassandra.io.sstable.SSTableReader; import org.apache.cassandra.io.sstable.SSTableScanner; import org.apache.cassandra.utils.CloseableIterator; @@ -93,16 +94,18 @@ public class RowIteratorFactory protected Row getReduced() { - // First check if this row is in the rowCache. If it is we can skip the rest + // First check if this row is in the rowCache. If it is and it covers our filter, we can skip the rest ColumnFamily cached = cfs.getRawCachedRow(key); - if (cached == null) + IDiskAtomFilter filter = range.columnFilter(key.key); + + if (cached == null || !cfs.isFilterFullyCoveredBy(filter, cached, now)) { // not cached: collate - QueryFilter.collateOnDiskAtom(returnCF, colIters, range.columnFilter(key.key), gcBefore, now); + QueryFilter.collateOnDiskAtom(returnCF, colIters, filter, gcBefore, now); } else { - QueryFilter keyFilter = new QueryFilter(key, cfs.name, range.columnFilter(key.key), now); + QueryFilter keyFilter = new QueryFilter(key, cfs.name, filter, now); returnCF = cfs.filterColumnFamily(cached, keyFilter); } diff --git a/src/java/org/apache/cassandra/db/filter/IDiskAtomFilter.java b/src/java/org/apache/cassandra/db/filter/IDiskAtomFilter.java index 2755afd85a..17967a8ecd 100644 --- a/src/java/org/apache/cassandra/db/filter/IDiskAtomFilter.java +++ b/src/java/org/apache/cassandra/db/filter/IDiskAtomFilter.java @@ -25,6 +25,7 @@ import java.util.Iterator; import org.apache.cassandra.db.*; import org.apache.cassandra.db.columniterator.OnDiskAtomIterator; +import org.apache.cassandra.db.composites.CellName; import org.apache.cassandra.db.composites.CellNameType; import org.apache.cassandra.db.composites.Composite; import org.apache.cassandra.io.IVersionedSerializer; @@ -79,7 +80,17 @@ public interface IDiskAtomFilter public IDiskAtomFilter cloneShallow(); public boolean maySelectPrefix(Comparator cmp, Composite prefix); - boolean shouldInclude(SSTableReader sstable); + public boolean shouldInclude(SSTableReader sstable); + + public boolean countCQL3Rows(CellNameType comparator); + + public boolean isHeadFilter(); + + /** + * Whether the provided cf, that is assumed to contain the head of the + * partition, contains enough data to cover this filter. + */ + public boolean isFullyCoveredBy(ColumnFamily cf, long now); public static class Serializer implements IVersionedSerializer { diff --git a/src/java/org/apache/cassandra/db/filter/NamesQueryFilter.java b/src/java/org/apache/cassandra/db/filter/NamesQueryFilter.java index 6f577b9e6b..208bbdf313 100644 --- a/src/java/org/apache/cassandra/db/filter/NamesQueryFilter.java +++ b/src/java/org/apache/cassandra/db/filter/NamesQueryFilter.java @@ -145,11 +145,32 @@ public class NamesQueryFilter implements IDiskAtomFilter return true; } - public boolean countCQL3Rows() + public boolean isFullyCoveredBy(ColumnFamily cf, long now) + { + // cf will cover all the requested columns if the range it covers include + // all said columns + CellName first = cf.iterator(ColumnSlice.ALL_COLUMNS_ARRAY).next().name(); + CellName last = cf.reverseIterator(ColumnSlice.ALL_COLUMNS_ARRAY).next().name(); + + return cf.getComparator().compare(first, columns.first()) <= 0 + && cf.getComparator().compare(columns.last(), last) <= 0; + } + + public boolean isHeadFilter() + { + return false; + } + + public boolean countCQL3Rows(CellNameType comparator) { return countCQL3Rows; } + public boolean countCQL3Rows() + { + return countCQL3Rows(null); + } + public ColumnCounter columnCounter(CellNameType comparator, long now) { return countCQL3Rows diff --git a/src/java/org/apache/cassandra/db/filter/SliceQueryFilter.java b/src/java/org/apache/cassandra/db/filter/SliceQueryFilter.java index 300a21d276..26f6d9df4c 100644 --- a/src/java/org/apache/cassandra/db/filter/SliceQueryFilter.java +++ b/src/java/org/apache/cassandra/db/filter/SliceQueryFilter.java @@ -31,6 +31,7 @@ import org.apache.cassandra.db.*; import org.apache.cassandra.db.columniterator.OnDiskAtomIterator; import org.apache.cassandra.db.columniterator.SSTableSliceIterator; import org.apache.cassandra.db.composites.CType; +import org.apache.cassandra.db.composites.CellName; import org.apache.cassandra.db.composites.CellNameType; import org.apache.cassandra.db.composites.Composite; import org.apache.cassandra.io.IVersionedSerializer; @@ -340,6 +341,41 @@ public class SliceQueryFilter implements IDiskAtomFilter return false; } + public boolean isHeadFilter() + { + return slices.length == 1 && slices[0].start.isEmpty() && !reversed; + } + + public boolean countCQL3Rows(CellNameType comparator) + { + // If comparator is dense a cell == a CQL3 rows so we're always counting CQL3 rows + // in particular. Otherwise, we do so only if we group the cells into CQL rows. + return comparator.isDense() || compositesToGroup >= 0; + } + + public boolean isFullyCoveredBy(ColumnFamily cf, long now) + { + // cf is the beginning of a partition. It covers this filter if: + // 1) either this filter requests the head of the partition and request less + // than what cf has to offer (note: we do need to use getLiveCount() for that + // as it knows if the filter count cells or CQL3 rows). + // 2) the start and finish bound of this filter are included in cf. + if (isHeadFilter() && count <= getLiveCount(cf, now)) + return true; + + if (start().isEmpty() || finish().isEmpty() || cf.getColumnCount() == 0) + return false; + + Composite low = isReversed() ? finish() : start(); + Composite high = isReversed() ? start() : finish(); + + CellName first = cf.iterator(ColumnSlice.ALL_COLUMNS_ARRAY).next().name(); + CellName last = cf.reverseIterator(ColumnSlice.ALL_COLUMNS_ARRAY).next().name(); + + return cf.getComparator().compare(first, low) <= 0 + && cf.getComparator().compare(high, last) <= 0; + } + public static class Serializer implements IVersionedSerializer { private CType type; diff --git a/src/java/org/apache/cassandra/metrics/ColumnFamilyMetrics.java b/src/java/org/apache/cassandra/metrics/ColumnFamilyMetrics.java index f6e3030025..b41462877a 100644 --- a/src/java/org/apache/cassandra/metrics/ColumnFamilyMetrics.java +++ b/src/java/org/apache/cassandra/metrics/ColumnFamilyMetrics.java @@ -89,6 +89,12 @@ public class ColumnFamilyMetrics public final Histogram liveScannedHistogram; /** Disk space used by snapshot files which */ public final Gauge trueSnapshotsSize; + /** Row cache hits, but result out of range */ + public final Counter rowCacheHitOutOfRange; + /** Number of row cache hits */ + public final Counter rowCacheHit; + /** Number of row cache misses */ + public final Counter rowCacheMiss; public final Timer coordinatorReadLatency; public final Timer coordinatorScanLatency; @@ -104,6 +110,7 @@ public class ColumnFamilyMetrics @Deprecated public final EstimatedHistogram sstablesPerRead = new EstimatedHistogram(35); @Deprecated public final EstimatedHistogram recentSSTablesPerRead = new EstimatedHistogram(35); + /** * Creates metrics for given {@link ColumnFamilyStore}. * @@ -347,6 +354,9 @@ public class ColumnFamilyMetrics return cfs.trueSnapshotsSize(); } }); + rowCacheHitOutOfRange = Metrics.newCounter(factory.createMetricName("RowCacheHitOutOfRange")); + rowCacheHit = Metrics.newCounter(factory.createMetricName("RowCacheHit")); + rowCacheMiss = Metrics.newCounter(factory.createMetricName("RowCacheMiss")); } public void updateSSTableIterated(int count) @@ -390,6 +400,9 @@ public class ColumnFamilyMetrics Metrics.defaultRegistry().removeMetric(factory.createMetricName("CoordinatorReadLatency")); Metrics.defaultRegistry().removeMetric(factory.createMetricName("CoordinatorScanLatency")); Metrics.defaultRegistry().removeMetric(factory.createMetricName("SnapshotsSize")); + Metrics.defaultRegistry().removeMetric(factory.createMetricName("RowCacheHitOutOfRange")); + Metrics.defaultRegistry().removeMetric(factory.createMetricName("RowCacheHit")); + Metrics.defaultRegistry().removeMetric(factory.createMetricName("RowCacheHitMiss")); } class ColumnFamilyMetricNameFactory implements MetricNameFactory diff --git a/src/java/org/apache/cassandra/service/CacheService.java b/src/java/org/apache/cassandra/service/CacheService.java index d8c26bcaff..4d82f1f077 100644 --- a/src/java/org/apache/cassandra/service/CacheService.java +++ b/src/java/org/apache/cassandra/service/CacheService.java @@ -24,7 +24,9 @@ import java.lang.management.ManagementFactory; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; import java.util.List; +import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -45,8 +47,10 @@ import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.Schema; import org.apache.cassandra.db.*; import org.apache.cassandra.db.composites.CellName; +import org.apache.cassandra.db.composites.Composites; import org.apache.cassandra.db.context.CounterContext; import org.apache.cassandra.db.filter.QueryFilter; +import org.apache.cassandra.db.filter.SliceQueryFilter; import org.apache.cassandra.io.sstable.Descriptor; import org.apache.cassandra.io.sstable.SSTableReader; import org.apache.cassandra.utils.ByteBufferUtil; @@ -287,6 +291,17 @@ public class CacheService implements CacheServiceMBean rowCache.clear(); } + public void invalidateRowCacheForCf(UUID cfId) + { + Iterator rowCacheIterator = rowCache.getKeySet().iterator(); + while (rowCacheIterator.hasNext()) + { + RowCacheKey rowCacheKey = rowCacheIterator.next(); + if (rowCacheKey.cfId.equals(cfId)) + rowCacheIterator.remove(); + } + } + public void invalidateCounterCache() { counterCache.clear(); @@ -427,7 +442,8 @@ public class CacheService implements CacheServiceMBean public Pair call() throws Exception { DecoratedKey key = cfs.partitioner.decorateKey(buffer); - ColumnFamily data = cfs.getTopLevelColumns(QueryFilter.getIdentityFilter(key, cfs.name, Long.MIN_VALUE), Integer.MIN_VALUE); + QueryFilter cacheFilter = new QueryFilter(key, cfs.getColumnFamilyName(), cfs.readFilterForCache(), Integer.MIN_VALUE); + ColumnFamily data = cfs.getTopLevelColumns(cacheFilter, Integer.MIN_VALUE); return Pair.create(new RowCacheKey(cfs.metadata.cfId, key), (IRowCacheEntry) data); } }); diff --git a/src/resources/org/apache/cassandra/cli/CliHelp.yaml b/src/resources/org/apache/cassandra/cli/CliHelp.yaml index 0202d19e32..3c167bcebe 100644 --- a/src/resources/org/apache/cassandra/cli/CliHelp.yaml +++ b/src/resources/org/apache/cassandra/cli/CliHelp.yaml @@ -794,7 +794,11 @@ commands: - KEYS_ONLY - ROWS_ONLY - NONE; + - cells_per_row_to_cache: State the number of cells per row to cache. + Defaults to 100. Set to "ALL" if you want the old cache behaviour. + + Will not be used if row caching is not enabled. - speculative_retry: Speculative retry is used to speculate a read failure. Speculative retry will execute additional read on a different nodes when diff --git a/test/unit/org/apache/cassandra/SchemaLoader.java b/test/unit/org/apache/cassandra/SchemaLoader.java index 774efebcd1..5efc266fad 100644 --- a/test/unit/org/apache/cassandra/SchemaLoader.java +++ b/test/unit/org/apache/cassandra/SchemaLoader.java @@ -233,7 +233,8 @@ public class SchemaLoader simple, opts_rf1, standardCFMD(ks_rcs, "CFWithoutCache").caching(CFMetaData.Caching.NONE), - standardCFMD(ks_rcs, "CachedCF").caching(CFMetaData.Caching.ALL))); + standardCFMD(ks_rcs, "CachedCF").caching(CFMetaData.Caching.ALL).rowsPerPartitionToCache(CFMetaData.RowsPerPartitionToCache.fromString("ALL")), + standardCFMD(ks_rcs, "CachedIntCF").defaultValidator(IntegerType.instance).caching(CFMetaData.Caching.ALL))); // CounterCacheSpace schema.add(KSMetaData.testMetadata(ks_ccs, diff --git a/test/unit/org/apache/cassandra/db/RowCacheTest.java b/test/unit/org/apache/cassandra/db/RowCacheTest.java index 238f61ec48..e21f586763 100644 --- a/test/unit/org/apache/cassandra/db/RowCacheTest.java +++ b/test/unit/org/apache/cassandra/db/RowCacheTest.java @@ -19,15 +19,20 @@ package org.apache.cassandra.db; import java.net.InetAddress; +import java.nio.ByteBuffer; import java.util.Collection; +import java.util.SortedSet; +import java.util.TreeSet; import org.junit.AfterClass; import org.junit.Test; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.Util; +import org.apache.cassandra.cache.RowCacheKey; import org.apache.cassandra.db.composites.*; import org.apache.cassandra.db.compaction.CompactionManager; +import org.apache.cassandra.db.filter.QueryFilter; import org.apache.cassandra.dht.BytesToken; import org.apache.cassandra.locator.TokenMetadata; import org.apache.cassandra.service.CacheService; @@ -153,6 +158,79 @@ public class RowCacheTest extends SchemaLoader rowCacheLoad(100, 50, 0); CacheService.instance.setRowCacheCapacityInMB(0); } + @Test + public void testRowCacheRange() + { + CompactionManager.instance.disableAutoCompaction(); + + Keyspace keyspace = Keyspace.open(KEYSPACE); + String cf = "CachedIntCF"; + ColumnFamilyStore cachedStore = keyspace.getColumnFamilyStore(cf); + long startRowCacheHits = cachedStore.metric.rowCacheHit.count(); + long startRowCacheOutOfRange = cachedStore.metric.rowCacheHitOutOfRange.count(); + // empty the row cache + CacheService.instance.invalidateRowCache(); + + // set global row cache size to 1 MB + CacheService.instance.setRowCacheCapacityInMB(1); + + ByteBuffer key = ByteBufferUtil.bytes("rowcachekey"); + DecoratedKey dk = cachedStore.partitioner.decorateKey(key); + RowCacheKey rck = new RowCacheKey(cachedStore.metadata.cfId, dk); + Mutation mutation = new Mutation(KEYSPACE, key); + for (int i = 0; i < 200; i++) + mutation.add(cf, Util.cellname(i), ByteBufferUtil.bytes("val" + i), System.currentTimeMillis()); + mutation.applyUnsafe(); + + // populate row cache, we should not get a row cache hit; + cachedStore.getColumnFamily(QueryFilter.getSliceFilter(dk, cf, + Composites.EMPTY, + Composites.EMPTY, + false, 10, System.currentTimeMillis())); + assertEquals(startRowCacheHits, cachedStore.metric.rowCacheHit.count()); + + // do another query, limit is 20, which is < 100 that we cache, we should get a hit and it should be in range + cachedStore.getColumnFamily(QueryFilter.getSliceFilter(dk, cf, + Composites.EMPTY, + Composites.EMPTY, + false, 20, System.currentTimeMillis())); + assertEquals(++startRowCacheHits, cachedStore.metric.rowCacheHit.count()); + assertEquals(startRowCacheOutOfRange, cachedStore.metric.rowCacheHitOutOfRange.count()); + + // get a slice from 95 to 105, 95->99 are in cache, we should not get a hit and then row cache is out of range + cachedStore.getColumnFamily(QueryFilter.getSliceFilter(dk, cf, + CellNames.simpleDense(ByteBufferUtil.bytes(95)), + CellNames.simpleDense(ByteBufferUtil.bytes(105)), + false, 10, System.currentTimeMillis())); + assertEquals(startRowCacheHits, cachedStore.metric.rowCacheHit.count()); + assertEquals(++startRowCacheOutOfRange, cachedStore.metric.rowCacheHitOutOfRange.count()); + + // get a slice with limit > 100, we should get a hit out of range. + cachedStore.getColumnFamily(QueryFilter.getSliceFilter(dk, cf, + Composites.EMPTY, + Composites.EMPTY, + false, 101, System.currentTimeMillis())); + assertEquals(startRowCacheHits, cachedStore.metric.rowCacheHit.count()); + assertEquals(++startRowCacheOutOfRange, cachedStore.metric.rowCacheHitOutOfRange.count()); + + + CacheService.instance.invalidateRowCache(); + + // try to populate row cache with a limit > rows to cache, we should still populate row cache; + cachedStore.getColumnFamily(QueryFilter.getSliceFilter(dk, cf, + Composites.EMPTY, + Composites.EMPTY, + false, 105, System.currentTimeMillis())); + assertEquals(startRowCacheHits, cachedStore.metric.rowCacheHit.count()); + // validate the stuff in cache; + ColumnFamily cachedCf = (ColumnFamily)CacheService.instance.rowCache.get(rck); + assertEquals(cachedCf.getColumnCount(), 100); + int i = 0; + for(Cell c : cachedCf) + { + assertEquals(c.name, Util.cellname(i++)); + } + } public void rowCacheLoad(int totalKeys, int keysToSave, int offset) throws Exception {