/* * Copyright (C) 2012-2015 DataStax Inc. * * Licensed 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 com.datastax.driver.core.schemabuilder; import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.base.Strings; import java.util.ArrayList; import java.util.List; /** * The table options used in a CREATE TABLE or ALTER TABLE statement. * <p/> * Implementation notes: this class is abstract and not meant to use directly. * The type parameter {@code T} allows usage of <strong>covariant return type</strong> and makes the builder pattern work for different sub-classes. * * @param <T> the concrete sub-class of {@link com.datastax.driver.core.schemabuilder.TableOptions} * @see <a href="http://docs.datastax.com/en/cql/3.1/cql/cql_reference/tabProp.html" target="_blank">details on table options</a> */ public abstract class TableOptions<T extends TableOptions> extends SchemaStatement { private StatementStart statementStart; private Optional<SchemaBuilder.Caching> cassandra20Caching = Optional.absent(); private Optional<SchemaBuilder.KeyCaching> cassandra21KeyCaching = Optional.absent(); private Optional<CachingRowsPerPartition> cassandra21RowCaching = Optional.absent(); private Optional<Double> bloomFilterFPChance = Optional.absent(); private Optional<String> comment = Optional.absent(); private Optional<CompressionOptions> compressionOptions = Optional.absent(); private Optional<CompactionOptions> compactionOptions = Optional.absent(); private Optional<Double> dcLocalReadRepairChance = Optional.absent(); private Optional<Integer> defaultTTL = Optional.absent(); private Optional<Integer> gcGraceSeconds = Optional.absent(); private Optional<Integer> indexInterval = Optional.absent(); private Optional<Integer> minIndexInterval = Optional.absent(); private Optional<Integer> maxIndexInterval = Optional.absent(); private Optional<Integer> memtableFlushPeriodInMillis = Optional.absent(); private Optional<Boolean> populateIOOnCacheFlush = Optional.absent(); private Optional<Double> readRepairChance = Optional.absent(); private Optional<Boolean> replicateOnWrite = Optional.absent(); private Optional<SpeculativeRetryValue> speculativeRetry = Optional.absent(); private Optional<Boolean> cdc = Optional.absent(); private List<String> customOptions = new ArrayList<String>(); @SuppressWarnings("unchecked") private final T self = (T) this; TableOptions(StatementStart statementStart) { this.statementStart = statementStart; } /** * Define the caching type for Cassandra 2.0.x. * <p/> * If no call is made to this method, the default value set by Cassandra is {@link SchemaBuilder.Caching#KEYS_ONLY}. * * @param caching the caching type (all enum values are allowed). * @return this {@code TableOptions} object. */ public T caching(SchemaBuilder.Caching caching) { this.cassandra20Caching = Optional.fromNullable(caching); return self; } /** * Define the caching options for Cassandra 2.1.x. * <p/> * If no call is made to this method, the default values set by Cassandra are keys = {@link SchemaBuilder.Caching#ALL} and * rows_per_partition = {@link com.datastax.driver.core.schemabuilder.SchemaBuilder#noRows()}. * * @param keys the key cache type. * @param rowsPerPartition defines the number of rows to be cached per partition when Row Caching is enabled. * To create instances, use * {@link SchemaBuilder#noRows()}, * {@link SchemaBuilder#allRows()} or * {@link SchemaBuilder#rows(int)}. * @return this {@code TableOptions} object. */ public T caching(SchemaBuilder.KeyCaching keys, CachingRowsPerPartition rowsPerPartition) { this.cassandra21KeyCaching = Optional.fromNullable(keys); this.cassandra21RowCaching = Optional.fromNullable(rowsPerPartition); return self; } /** * Define the desired false-positive probability for SSTable Bloom filters. * <p/> * If no call is made to this method, the default value set by Cassandra is: * <ul> * <li><strong>0.01</strong> for the size-tiered compaction strategy;</li> * <li><strong>0.1</strong> for the leveled compaction strategy.</li> * </ul> * * @param fpChance the false positive change. This value should be between 0 and 1.0. * @return this {@code TableOptions} object. */ public T bloomFilterFPChance(Double fpChance) { validateRateValue(fpChance, "Bloom filter false positive change"); this.bloomFilterFPChance = Optional.fromNullable(fpChance); return self; } /** * Define a human readable comment describing the table. * * @param comment the comment. * @return this {@code TableOptions} object. */ public T comment(String comment) { this.comment = Optional.fromNullable(comment); return self; } /** * Define the compression options. * <p/> * If no call is made to this method, the default value set by Cassandra is {@link SchemaBuilder#lz4()}. * * @param compressionOptions the compression options. To create instances, use * {@link SchemaBuilder#noCompression()}, * {@link SchemaBuilder#lz4()}, * {@link SchemaBuilder#snappy()} or * {@link SchemaBuilder#deflate()}. * @return this {@code TableOptions} object. */ public T compressionOptions(CompressionOptions compressionOptions) { this.compressionOptions = Optional.fromNullable(compressionOptions); return self; } /** * Define the compaction options. * <p/> * If no call is made to this method, the default value set by Cassandra is {@link SchemaBuilder#sizedTieredStategy()}. * * @param compactionOptions the compaction options. To create instances, use * {@link SchemaBuilder#sizedTieredStategy()}, * {@link SchemaBuilder#leveledStrategy()} or * {@link SchemaBuilder#dateTieredStrategy()} * @return this {@code TableOptions} object. */ public T compactionOptions(CompactionOptions compactionOptions) { this.compactionOptions = Optional.fromNullable(compactionOptions); return self; } /** * Define the probability of read repairs being invoked over all replicas in the current data center. * <p/> * If no call is made to this method, the default value set by Cassandra is 0.0. * * @param dcLocalReadRepairChance the probability. * @return this {@code TableOptions} object. */ public T dcLocalReadRepairChance(Double dcLocalReadRepairChance) { validateRateValue(dcLocalReadRepairChance, "DC local read repair chance"); this.dcLocalReadRepairChance = Optional.fromNullable(dcLocalReadRepairChance); return self; } /** * Define the default expiration time in seconds for a table. * <p/> * <p/> * Used in MapReduce/Hive scenarios when you have no control of TTL. * <p/> * If no call is made to this method, the default value set by Cassandra is 0. * * @param defaultTimeToLive the default time to live in seconds for a table. * @return this {@code TableOptions} object. */ public T defaultTimeToLive(Integer defaultTimeToLive) { this.defaultTTL = Optional.fromNullable(defaultTimeToLive); return self; } /** * Define the time to wait before garbage collecting tombstones (deletion markers). * <p/> * The default value allows a great deal of time for consistency to be achieved prior to deletion. * In many deployments this interval can be reduced, and in a single-node cluster it can be safely set to zero. * <p/> * If no call is made to this method, the default value set by Cassandra is 864000 secs (10 days). * * @param gcGraceSeconds the grace period. * @return this {@code TableOptions} object. */ public T gcGraceSeconds(Integer gcGraceSeconds) { this.gcGraceSeconds = Optional.fromNullable(gcGraceSeconds); return self; } /** * Define the index interval for Cassandra 2.0. * <p/> * To control the sampling of entries from the primary row index, configure sample frequency of the partition summary by changing the index interval. * After changing the index interval, SSTables are written to disk with new information. The interval corresponds to the number of index entries that * are skipped between taking each sample. By default, Cassandra samples one row key out of every 128. The larger the interval, the smaller and less * effective the sampling. The larger the sampling, the more effective the index, but with increased memory usage. In Cassandra 2.0.x, generally, the * best trade off between memory usage and performance is a value between 128 and 512 in combination with a large table key cache. However, if you have * small rows (many to an OS page), you may want to increase the sample size, which often lowers memory usage without an impact on performance. For * large rows, decreasing the sample size may improve read performance. * <p/> * If no call is made to this method, the default value set by Cassandra is 128. * * @param indexInterval the index interval. * @return this {@code TableOptions} object. */ public T indexInterval(Integer indexInterval) { this.indexInterval = Optional.fromNullable(indexInterval); return self; } /** * Define the minimum index interval for Cassandra 2.1. * <p/> * If no call is made to this method, the default value set by Cassandra is 128. * * @param minIndexInterval the minimum index interval. * @return this {@code TableOptions} object. * @see #indexInterval(Integer) */ public T minIndexInterval(Integer minIndexInterval) { this.minIndexInterval = Optional.fromNullable(minIndexInterval); return self; } /** * Define the maximum index interval for Cassandra 2.1. * <p/> * If no call is made to this method, the default value set by Cassandra is 2048. * * @param maxIndexInterval the maximum index interval. * @return this {@code TableOptions} object. * @see #indexInterval(Integer) */ public T maxIndexInterval(Integer maxIndexInterval) { this.maxIndexInterval = Optional.fromNullable(maxIndexInterval); return self; } /** * Define the memtable flush period. * <p/> * If set, this forces flushing of the memtable after the specified time elapses. * <p/> * If no call is made to this method, the default value set by Cassandra is 0. * * @param memtableFlushPeriodInMillis the memtable flush period in milliseconds. * @return this {@code TableOptions} object. */ public T memtableFlushPeriodInMillis(Integer memtableFlushPeriodInMillis) { this.memtableFlushPeriodInMillis = Optional.fromNullable(memtableFlushPeriodInMillis); return self; } /** * Define whether to populate IO cache on flush of sstables. * <p/> * If set, Cassandra adds newly flushed or compacted sstables to the operating system page cache, potentially evicting other cached data to make room. * Enable when all data in the table is expected to fit in memory. * <p/> * If no call is made to this method, the default value set by Cassandra is {@code false}. * * @param populateIOOnCacheFlush whether to populate IO cache on flush of sstables. * @return this {@code TableOptions} object. * @see <a href="http://docs.datastax.com/en/cassandra/2.0/cassandra/configuration/configCassandra_yaml_r.html?scroll=reference_ds_qfg_n1r_1k__compaction_preheat_key_cache">the global option compaction_preheat_key_cache</a> */ public T populateIOCacheOnFlush(Boolean populateIOOnCacheFlush) { this.populateIOOnCacheFlush = Optional.fromNullable(populateIOOnCacheFlush); return self; } /** * Define the probability with which read repairs should be invoked on non-quorum reads. The value must be between 0 and 1. * <p/> * If no call is made to this method, the default value set by Cassandra is 0.1. * * @param readRepairChance the read repair chance. * @return this {@code TableOptions} object. */ public T readRepairChance(Double readRepairChance) { validateRateValue(readRepairChance, "Read repair chance"); this.readRepairChance = Optional.fromNullable(readRepairChance); return self; } /** * Define whether to replicate data on write (Cassandra 2.0.x only). * <p/> * When set to {@code true}, replicates writes to all affected replicas regardless of the consistency level specified by the client for a write request. * For counter tables, this should always be set to {@code true}. * <p/> * If no call is made to this method, the default value set by Cassandra is {@code true}. * * @param replicateOnWrite whether to replicate data on write. * @return this {@code TableOptions} object. */ public T replicateOnWrite(Boolean replicateOnWrite) { this.replicateOnWrite = Optional.fromNullable(replicateOnWrite); return self; } /** * To override normal read timeout when read_repair_chance is not 1.0, sending another request to read, choose one of these values and use the property to create * or alter the table: * <ul> * <li>ALWAYS: Retry reads of all replicas.</li> * <li>Xpercentile: Retry reads based on the effect on throughput and latency.</li> * <li>Yms: Retry reads after specified milliseconds.</li> * <li>NONE: Do not retry reads.</li> * </ul> * <p/> * Using the speculative retry property, you can configure rapid read protection in Cassandra 2.0.2 and later. * Use this property to retry a request after some milliseconds have passed or after a percentile of the typical read latency has been reached, * which is tracked per table. * <p/> * <p/> * If no call is made to this method, the default value set by Cassandra is {code 99percentile}. * * @param speculativeRetry the speculative retry. To create instances, use * {@link SchemaBuilder#noSpeculativeRetry()}, * {@link SchemaBuilder#always()}, * {@link SchemaBuilder#percentile(int)} or * {@link SchemaBuilder#millisecs(int)}. * @return this {@code TableOptions} object. */ public T speculativeRetry(SpeculativeRetryValue speculativeRetry) { this.speculativeRetry = Optional.fromNullable(speculativeRetry); return self; } /** * Define whether or not change data capture is enabled on this table. * <p/> * Note that using this option with a version of Apache Cassandra less than 3.8 will raise a syntax error. * <p/> * If no call is made to this method, the default value set by Cassandra is {@code false}. * * @param cdc Whether or not change data capture should be enabled for this table. * @return this {@code TableOptions} object. */ public T cdc(Boolean cdc) { this.cdc = Optional.fromNullable(cdc); return self; } /** * Define a free-form option as a key/value pair. * <p/> * This method is provided as a fallback if the SchemaBuilder is used with a more recent version of Cassandra that has new, unsupported options. * * @param key the name of the option. * @param value the value of the option. If it's a {@code String}, it will be included in single quotes, otherwise the result of invoking its * {@code toString} method will be used unquoted. * @return this {@code TableOptions} object. */ public T freeformOption(String key, Object value) { if (Strings.isNullOrEmpty(key)) { throw new IllegalArgumentException("Key for custom option should not be null or blank"); } customOptions.add(buildCustomOption(key, value)); return self; } private static String buildCustomOption(String key, Object value) { return String.format("%s = %s", key, (value instanceof String) ? "'" + value + "'" : value.toString()); } private List<String> buildCommonOptions() { List<String> options = new ArrayList<String>(); buildCachingOptions(options); if (bloomFilterFPChance.isPresent()) { options.add("bloom_filter_fp_chance = " + bloomFilterFPChance.get()); } if (comment.isPresent()) { options.add("comment = '" + comment.get() + "'"); } if (compressionOptions.isPresent()) { options.add("compression = " + compressionOptions.get().build()); } if (compactionOptions.isPresent()) { options.add("compaction = " + compactionOptions.get().build()); } if (dcLocalReadRepairChance.isPresent()) { options.add("dclocal_read_repair_chance = " + dcLocalReadRepairChance.get()); } if (defaultTTL.isPresent()) { options.add("default_time_to_live = " + defaultTTL.get()); } if (gcGraceSeconds.isPresent()) { options.add("gc_grace_seconds = " + gcGraceSeconds.get()); } if (indexInterval.isPresent()) { options.add("index_interval = " + indexInterval.get()); } if (minIndexInterval.isPresent()) { options.add("min_index_interval = " + minIndexInterval.get()); } if (maxIndexInterval.isPresent()) { options.add("max_index_interval = " + maxIndexInterval.get()); } if (memtableFlushPeriodInMillis.isPresent()) { options.add("memtable_flush_period_in_ms = " + memtableFlushPeriodInMillis.get()); } if (populateIOOnCacheFlush.isPresent()) { options.add("populate_io_cache_on_flush = " + populateIOOnCacheFlush.get()); } if (readRepairChance.isPresent()) { options.add("read_repair_chance = " + readRepairChance.get()); } if (replicateOnWrite.isPresent()) { options.add("replicate_on_write = " + replicateOnWrite.get()); } if (speculativeRetry.isPresent()) { options.add("speculative_retry = " + speculativeRetry.get().value()); } if (cdc.isPresent()) { options.add("cdc = " + cdc.get()); } options.addAll(customOptions); return options; } private void buildCachingOptions(List<String> options) { if (cassandra20Caching.isPresent() && cassandra21KeyCaching.isPresent()) { throw new IllegalStateException("Can't use Cassandra 2.0 and 2.1 caching at the same time, you must call only one version of caching()"); } else if (cassandra20Caching.isPresent()) { options.add("caching = " + cassandra20Caching.get().value()); } else if (cassandra21KeyCaching.isPresent() && cassandra21RowCaching.isPresent()) { options.add(String.format("caching = {'keys' : %s, 'rows_per_partition' : %s}", cassandra21KeyCaching.get().value(), cassandra21RowCaching.get().value())); } } protected abstract void addSpecificOptions(List<String> options); @Override public final String buildInternal() { List<String> options = buildCommonOptions(); addSpecificOptions(options); return statementStart.buildInternal() + STATEMENT_START + "WITH " + Joiner.on(" AND ").join(options); } static void validateRateValue(Double rateValue, String property) { if (rateValue != null && (rateValue < 0 || rateValue > 1.0)) { throw new IllegalArgumentException(property + " should be between 0 and 1"); } } /** * Compaction options for a CREATE or ALTER TABLE statement. * <p/> * To create instances, use * {@link SchemaBuilder#sizedTieredStategy()}, * {@link SchemaBuilder#leveledStrategy()} or * {@link SchemaBuilder#dateTieredStrategy()} */ public static abstract class CompactionOptions<T extends CompactionOptions> { private Strategy strategy; private Optional<Boolean> enabled = Optional.absent(); private Optional<Integer> tombstoneCompactionIntervalInDay = Optional.absent(); private Optional<Double> tombstoneThreshold = Optional.absent(); private Optional<Boolean> uncheckedTombstoneCompaction = Optional.absent(); private List<String> customOptions = new ArrayList<String>(); @SuppressWarnings("unchecked") private final T self = (T) this; CompactionOptions(Strategy compactionStrategy) { this.strategy = compactionStrategy; } /** * Enable or disable background compaction. * <p/> * If no call is made to this method, the default value set by Cassandra is {code true}. * * @param enabled whether to enable background compaction for the table. * @return this object (for call chaining). */ public T enabled(Boolean enabled) { this.enabled = Optional.fromNullable(enabled); return self; } /** * Define the minimum number of days to wait after an SSTable creation time before considering the SSTable for tombstone compaction. * Tombstone compaction is the compaction triggered if the SSTable has more garbage-collectable tombstones than tombstone_threshold. * <p/> * If no call is made to this method, the default value set by Cassandra is 1. * * @param tombstoneCompactionInterval the tombstone compaction interval in day. * @return this object (for call chaining). */ public T tombstoneCompactionIntervalInDay(Integer tombstoneCompactionInterval) { this.tombstoneCompactionIntervalInDay = Optional.fromNullable(tombstoneCompactionInterval); return self; } /** * Define the ratio of garbage-collectable tombstones to all contained columns, * which if exceeded by the SSTable triggers compaction (with no other SSTables) for the purpose of purging the tombstones. * <p/> * If no call is made to this method, the default value set by Cassandra is 0.2. * * @param tombstoneThreshold the threshold. * @return this object (for call chaining). */ public T tombstoneThreshold(Double tombstoneThreshold) { validateRateValue(tombstoneThreshold, "Tombstone threshold"); this.tombstoneThreshold = Optional.fromNullable(tombstoneThreshold); return self; } /** * Enables more aggressive than normal tombstone compactions. A single SSTable tombstone compaction runs without * checking the likelihood of success (Cassandra 2.0.9 and later). * <p/> * If no call is made to this method, the default value set by Cassandra is {code false}. * * @param uncheckedTombstoneCompaction whether to enable the feature. * @return this object (for call chaining). */ public T uncheckedTombstoneCompaction(Boolean uncheckedTombstoneCompaction) { this.uncheckedTombstoneCompaction = Optional.fromNullable(uncheckedTombstoneCompaction); return self; } /** * Define a free-form option as a key/value pair. * <p/> * This method is provided as a fallback if the SchemaBuilder is used with a more recent version of Cassandra that has new, unsupported options. * * @param key the name of the option. * @param value the value of the option. If it's a {@code CharSequence}, it will be included in single quotes, otherwise the result of invoking its * {@code toString} method will be used unquoted. * @return this object (for call chaining). */ public T freeformOption(String key, Object value) { if (Strings.isNullOrEmpty(key)) { throw new IllegalArgumentException("Key for custom option should not be null or blank"); } customOptions.add(buildCustomOption(key, value)); return self; } private static String buildCustomOption(String key, Object value) { return String.format("'%s' : %s", key, (value instanceof CharSequence) ? "'" + value + "'" : value.toString()); } List<String> buildCommonOptions() { List<String> options = new ArrayList<String>(); options.add("'class' : " + strategy.strategyClass()); if (enabled.isPresent()) { options.add("'enabled' : " + enabled.get()); } if (tombstoneCompactionIntervalInDay.isPresent()) { options.add("'tombstone_compaction_interval' : " + tombstoneCompactionIntervalInDay.get()); } if (tombstoneThreshold.isPresent()) { options.add("'tombstone_threshold' : " + tombstoneThreshold.get()); } if (uncheckedTombstoneCompaction.isPresent()) { options.add("'unchecked_tombstone_compaction' : " + uncheckedTombstoneCompaction.get()); } options.addAll(customOptions); return options; } public abstract String build(); /** * Compaction options specific to SizeTiered strategy */ public static class SizeTieredCompactionStrategyOptions extends CompactionOptions<SizeTieredCompactionStrategyOptions> { private Optional<Double> bucketHigh = Optional.absent(); private Optional<Double> bucketLow = Optional.absent(); private Optional<Double> coldReadsRatioToOmit = Optional.absent(); private Optional<Integer> minThreshold = Optional.absent(); private Optional<Integer> maxThreshold = Optional.absent(); private Optional<Long> minSSTableSizeInBytes = Optional.absent(); SizeTieredCompactionStrategyOptions() { super(Strategy.SIZED_TIERED); } /** * Size-tiered compaction strategy (STCS) considers SSTables to be within the same bucket if the SSTable size diverges by 50% * or less from the default bucket_low and default bucket_high values: [average-size × bucket_low, average-size × bucket_high]. * <p/> * If no call is made to this method, the default value set by Cassandra is 1.5. * * @param bucketHigh the new value. * @return this object (for call chaining). */ public SizeTieredCompactionStrategyOptions bucketHigh(Double bucketHigh) { this.bucketHigh = Optional.fromNullable(bucketHigh); return this; } /** * Size-tiered compaction strategy (STCS) considers SSTables to be within the same bucket if the SSTable size diverges by 50% * or less from the default bucket_low and default bucket_high values: [average-size × bucket_low, average-size × bucket_high]. * <p/> * If no call is made to this method, the default value set by Cassandra is 0.5. * * @param bucketLow the new value. * @return this object (for call chaining). */ public SizeTieredCompactionStrategyOptions bucketLow(Double bucketLow) { this.bucketLow = Optional.fromNullable(bucketLow); return this; } /** * The maximum percentage of reads/sec that ignored SSTables may account for. * The recommended range of values is 0.0 and 1.0. * In Cassandra 2.0.3 and later, you can enable the cold_reads_to_omit property to tune performace per table. * The <a href="http://www.datastax.com/dev/blog/optimizations-around-cold-sstables">Optimizations around Cold SSTables</a> blog includes detailed information tuning performance using this property, * which avoids compacting cold SSTables. Use the ALTER TABLE command to configure cold_reads_to_omit. * <p/> * If no call is made to this method, the default value set by Cassandra is 0.0 (disabled). * * @param coldReadsRatio the new value. * @return this object (for call chaining). */ public SizeTieredCompactionStrategyOptions coldReadsRatioToOmit(Double coldReadsRatio) { validateRateValue(coldReadsRatio, "Cold read ratio to omit "); this.coldReadsRatioToOmit = Optional.fromNullable(coldReadsRatio); return this; } /** * Sets the minimum number of SSTables to trigger a minor compaction * <p/> * If no call is made to this method, the default value set by Cassandra is 4. * * @param minThreshold the new value. * @return this object (for call chaining). */ public SizeTieredCompactionStrategyOptions minThreshold(Integer minThreshold) { this.minThreshold = Optional.fromNullable(minThreshold); return this; } /** * Sets the maximum number of SSTables to allow in a minor compaction. * In LeveledCompactionStrategy (LCS), it applies to L0 when L0 gets behind, that is, when L0 accumulates more than MAX_COMPACTING_L0 SSTables. * <p/> * If no call is made to this method, the default value set by Cassandra is 32. * * @param maxThreshold the new value. * @return this object (for call chaining). */ public SizeTieredCompactionStrategyOptions maxThreshold(Integer maxThreshold) { this.maxThreshold = Optional.fromNullable(maxThreshold); return this; } /** * The SizeTieredCompactionStrategy groups SSTables for compaction into buckets. * The bucketing process groups SSTables that differ in size by less than 50%. This results in a bucketing process that is too fine grained for small SSTables. * If your SSTables are small, use min_sstable_size to define a size threshold (in bytes) below which all SSTables belong to one unique bucket * <p/> * If no call is made to this method, the default value set by Cassandra is 52428800 (50 MB). * * @param minSSTableSize the new value. * @return this object (for call chaining). */ public SizeTieredCompactionStrategyOptions minSSTableSizeInBytes(Long minSSTableSize) { this.minSSTableSizeInBytes = Optional.fromNullable(minSSTableSize); return this; } @Override public String build() { final List<String> generalOptions = super.buildCommonOptions(); List<String> options = new ArrayList<String>(generalOptions); if (bucketHigh.isPresent()) { options.add("'bucket_high' : " + bucketHigh.get()); } if (bucketLow.isPresent()) { options.add("'bucket_low' : " + bucketLow.get()); } if (coldReadsRatioToOmit.isPresent()) { options.add("'cold_reads_to_omit' : " + coldReadsRatioToOmit.get()); } if (minThreshold.isPresent()) { options.add("'min_threshold' : " + minThreshold.get()); } if (maxThreshold.isPresent()) { options.add("'max_threshold' : " + maxThreshold.get()); } if (minSSTableSizeInBytes.isPresent()) { options.add("'min_sstable_size' : " + minSSTableSizeInBytes.get()); } return "{" + Joiner.on(", ").join(options) + "}"; } } /** * Compaction options specific to Leveled strategy */ public static class LeveledCompactionStrategyOptions extends CompactionOptions<LeveledCompactionStrategyOptions> { private Optional<Integer> ssTableSizeInMB = Optional.absent(); LeveledCompactionStrategyOptions() { super(Strategy.LEVELED); } /** * The target size for SSTables that use the leveled compaction strategy. * Although SSTable sizes should be less or equal to sstable_size_in_mb, it is possible to have a larger SSTable during compaction. * This occurs when data for a given partition key is exceptionally large. The data is not split into two SSTables * <p/> * If no call is made to this method, the default value set by Cassandra is 160. * * @param ssTableSizeInMB the new value. * @return this object (for call chaining). */ public LeveledCompactionStrategyOptions ssTableSizeInMB(Integer ssTableSizeInMB) { this.ssTableSizeInMB = Optional.fromNullable(ssTableSizeInMB); return this; } @Override public String build() { final List<String> generalOptions = super.buildCommonOptions(); List<String> options = new ArrayList<String>(generalOptions); if (ssTableSizeInMB.isPresent()) { options.add("'sstable_size_in_mb' : " + ssTableSizeInMB.get()); } return "{" + Joiner.on(", ").join(options) + "}"; } } /** * Compaction options specific to the date-tiered strategy. */ public static class DateTieredCompactionStrategyOptions extends CompactionOptions<DateTieredCompactionStrategyOptions> { public enum TimeStampResolution {MICROSECONDS, MILLISECONDS} private Optional<Integer> baseTimeSeconds = Optional.absent(); private Optional<Integer> maxSSTableAgeDays = Optional.absent(); private Optional<Integer> minThreshold = Optional.absent(); private Optional<Integer> maxThreshold = Optional.absent(); private Optional<TimeStampResolution> timestampResolution = Optional.absent(); DateTieredCompactionStrategyOptions() { super(Strategy.DATE_TIERED); } /** * Sets the size of the first window. * <p/> * If no call is made to this method, the default value set by Cassandra is 3600 (1 hour). * * @param baseTimeSeconds the size of the first window. * @return this object (for call chaining). */ public DateTieredCompactionStrategyOptions baseTimeSeconds(Integer baseTimeSeconds) { this.baseTimeSeconds = Optional.fromNullable(baseTimeSeconds); return this; } /** * Stop compacting SSTables only having data older than these specified days. * <p/> * If no call is made to this method, the default value set by Cassandra is 365. * * @param maxSSTableAgeDays the maximum age of the SSTables to compact. * @return this object (for call chaining). */ public DateTieredCompactionStrategyOptions maxSSTableAgeDays(Integer maxSSTableAgeDays) { this.maxSSTableAgeDays = Optional.fromNullable(maxSSTableAgeDays); return this; } /** * Sets the minimum number of SSTables to trigger a minor compaction * <p/> * If no call is made to this method, the default value set by Cassandra is 4. * * @param minThreshold the new value. * @return this object (for call chaining). */ public DateTieredCompactionStrategyOptions minThreshold(Integer minThreshold) { this.minThreshold = Optional.fromNullable(minThreshold); return this; } /** * Sets the maximum number of SSTables to allow in a minor compaction. * In LeveledCompactionStrategy (LCS), it applies to L0 when L0 gets behind, that is, when L0 accumulates more than MAX_COMPACTING_L0 SSTables. * <p/> * If no call is made to this method, the default value set by Cassandra is 32. * * @param maxThreshold the new value. * @return this object (for call chaining). */ public DateTieredCompactionStrategyOptions maxThreshold(Integer maxThreshold) { this.maxThreshold = Optional.fromNullable(maxThreshold); return this; } /** * Sets the timestamp resolution, depending on the timestamp unit of the data you insert. * <p/> * If no call is made to this method, the default value set by Cassandra is {@code MICROSECONDS}. * * @param timestampResolution {@link TimeStampResolution#MICROSECONDS} or {@link TimeStampResolution#MILLISECONDS}. * @return this object (for call chaining). */ public DateTieredCompactionStrategyOptions timestampResolution(TimeStampResolution timestampResolution) { this.timestampResolution = Optional.fromNullable(timestampResolution); return this; } @Override public String build() { final List<String> generalOptions = super.buildCommonOptions(); List<String> options = new ArrayList<String>(generalOptions); if (baseTimeSeconds.isPresent()) { options.add("'base_time_seconds' : " + baseTimeSeconds.get()); } if (maxSSTableAgeDays.isPresent()) { options.add("'max_sstable_age_days' : " + maxSSTableAgeDays.get()); } if (minThreshold.isPresent()) { options.add("'min_threshold' : " + minThreshold.get()); } if (maxThreshold.isPresent()) { options.add("'max_threshold' : " + maxThreshold.get()); } if (timestampResolution.isPresent()) { options.add("'timestamp_resolution' : '" + timestampResolution.get() + "'"); } return "{" + Joiner.on(", ").join(options) + "}"; } } /** * Compaction strategies. Possible values: SIZED_TIERED, LEVELED & DATE_TIERED */ public static enum Strategy { SIZED_TIERED("'SizeTieredCompactionStrategy'"), LEVELED("'LeveledCompactionStrategy'"), DATE_TIERED("'DateTieredCompactionStrategy'"); private String strategyClass; Strategy(String strategyClass) { this.strategyClass = strategyClass; } public String strategyClass() { return strategyClass; } @Override public String toString() { return strategyClass; } } } /** * The compression options for a CREATE or ALTER TABLE statement. * <p/> * To create instances, use * {@link SchemaBuilder#noCompression()}, * {@link SchemaBuilder#lz4()}, * {@link SchemaBuilder#snappy()} or * {@link SchemaBuilder#deflate()}. */ public static class CompressionOptions { private Algorithm algorithm; private Optional<Integer> chunkLengthInKb = Optional.absent(); private Optional<Double> crcCheckChance = Optional.absent(); CompressionOptions(Algorithm algorithm) { this.algorithm = algorithm; } /** * On disk, SSTables are compressed by block to allow random reads. * This subproperty of compression defines the size (in KB) of the block. * Values larger than the default value might improve the compression rate, but increases the minimum size of data to be read from disk when a read occurs. * The default value is a good middle-ground for compressing tables. * Adjust compression size to account for read/write access patterns (how much data is typically requested at once) and the average size of rows in the table. * <p/> * If no call is made to this method, the default value set by Cassandra is 54. * * @param chunkLengthInKb the new value. * @return this object (for call chaining). */ public CompressionOptions withChunkLengthInKb(Integer chunkLengthInKb) { this.chunkLengthInKb = Optional.fromNullable(chunkLengthInKb); return this; } /** * When compression is enabled, each compressed block includes a checksum of that block for the purpose of detecting disk bitrate and avoiding the propagation * of corruption to other replica. This option defines the probability with which those checksums are checked during read. * By default they are always checked. Set to 0 to disable checksum checking and to 0.5, for instance, to check them on every other read. * <p/> * If no call is made to this method, the default value set by Cassandra is 1.0 (always check). * * @param crcCheckChance the new value. * @return this object (for call chaining). */ public CompressionOptions withCRCCheckChance(Double crcCheckChance) { validateRateValue(crcCheckChance, "CRC check chance"); this.crcCheckChance = Optional.fromNullable(crcCheckChance); return this; } public String build() { List<String> options = new ArrayList<String>(); options.add("'sstable_compression' : " + algorithm.value()); if (chunkLengthInKb.isPresent()) { options.add("'chunk_length_kb' : " + chunkLengthInKb.get()); } if (crcCheckChance.isPresent()) { options.add("'crc_check_chance' : " + crcCheckChance.get()); } return "{" + Joiner.on(", ").join(options) + "}"; } /** * Compression algorithms. Possible values: NONE, LZ4, SNAPPY, DEFLATE */ public static enum Algorithm { NONE("''"), LZ4("'LZ4Compressor'"), SNAPPY("'SnappyCompressor'"), DEFLATE("'DeflateCompressor'"); private String value; Algorithm(String value) { this.value = value; } public String value() { return value; } @Override public String toString() { return value; } } public static class NoCompression extends CompressionOptions { public NoCompression() { super(Algorithm.NONE); } @Override public CompressionOptions withChunkLengthInKb(Integer chunkLengthInKb) { return this; } @Override public CompressionOptions withCRCCheckChance(Double crcCheckChance) { return this; } } } /** * The speculative retry options. * <p/> * To create instances, use * {@link SchemaBuilder#noSpeculativeRetry()}, * {@link SchemaBuilder#always()}, * {@link SchemaBuilder#percentile(int)} or * {@link SchemaBuilder#millisecs(int)}. */ public static class SpeculativeRetryValue { private String value; SpeculativeRetryValue(String value) { this.value = value; } String value() { return value; } } /** * Define the number of rows to be cached per partition when row caching is enabled * (this feature is only applicable to Cassandra 2.1.x). * <p/> * To create instances, use * {@link SchemaBuilder#noRows()}, * {@link SchemaBuilder#allRows()} or * {@link SchemaBuilder#rows(int)}. */ public static class CachingRowsPerPartition { private String value; CachingRowsPerPartition(String value) { this.value = value; } public String value() { return value; } } }