Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions conf/cassandra.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,52 @@ sstable_preemptive_open_interval: 50MiB
# and eventually get removed from the configuration.
uuid_sstable_identifiers_enabled: false

# Anticompact an sstable by splitting it - copying its compression chunks verbatim - instead of rewriting every
# row, when its full/transient/unrepaired partitions form contiguous token runs. Anything else, notably the
# interleaved ranges vnodes produce, falls back to the normal rewrite. The copy is bounded by
# compaction_throughput, appears in nodetool compactionstats as an ANTICOMPACTION, and is stopped by nodetool
# stop ANTICOMPACTION, TRUNCATE and DROP.
#
# NOTE: a verbatim copy cannot purge tombstones, so anticompaction stops dropping droppable tombstones and
# shadowed data for the sstables it handles. That is retention only, never data loss, but disk usage after
# anticompaction can be higher until the children are compacted normally.
#
# NOTE: the children's sstable statistics are inherited rather than recomputed, since recomputing them means
# deserializing every row - the cost this path avoids. Each child inherits the parent's whole-sstable cell
# count, row count and tombstone-drop histogram, while its partition count and partition size histogram are
# exact. Expect per-table aggregates to over-report by roughly the number of children, and single-sstable
# tombstone compaction to fire less readily than tombstone_threshold suggests. All conservative in direction.
#
# NOTE: children written from the middle or end of the parent carry a dead prefix at the head of their Data.db.
# Every read path tolerates it, but a node running an older build will fail nodetool verify on such an sstable.
#
# Tables with a secondary index are refused and anticompact the old way, so no configuration is needed. Not
# supported on JBOD: children are always written into the parent sstable's own directory with no free-space
# check, so they cannot move to a disk that has room and splitting a parent larger than the free space on its
# own disk will fill that disk.
# zero_copy_anticompaction_enabled: false

# Let the zero-copy splitter share each child's Data.db extents with the parent instead of copying them, using
# the Linux FICLONERANGE ioctl ("reflink"). Where it works a split writes no data blocks and needs no additional
# disk space, since the parent's extents become the children's when the parent is unlinked. Requires xfs
# formatted with `-m reflink=1` or btrfs; elsewhere the first attempt per data directory fails, is logged once,
# and every split from then on copies exactly as before. Both paths produce identical children.
#
# NOTE: sharing costs up to 64 KiB of alignment padding at the head of each child's Data.db, so children smaller
# than 1 MiB are copied regardless. Shared extents are counted once per file by `du` but once in total by `df`,
# so per-table disk usage over-reports until the parent is unlinked.
# zero_copy_split_reflink_enabled: true

# Write Digest.crc32 for the children of a zero-copy split. Producing it is one full sequential read of every
# child, which once the extents above are shared is the entire remaining cost of a split. Nothing requires the
# component, and a compressed sstable is self-checking without it because every chunk carries an inline CRC32
# that this path preserves and every read verifies.
#
# NOTE: what it costs is verification speed, not strength. `nodetool verify` and `nodetool import
# --verify-sstables` treat a missing digest as a reason to run a full extended verification instead of a
# whole-file CRC. `nodetool verify -q` never looks at it.
# zero_copy_split_digest_enabled: true

# When enabled, permits Cassandra to zero-copy stream entire eligible
# SSTables between nodes, including every component.
# This speeds up the network transfer significantly subject to
Expand Down
5 changes: 5 additions & 0 deletions doc/modules/cassandra/pages/operating/metrics.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,11 @@ read during validation.
anticompacting because the sstable was fully contained in the repaired
range.

|BytesZeroCopyAnticompaction |Counter |How many Data.db bytes we copied
verbatim during anticompaction instead of rewriting them, because the
sstable's full/transient/unrepaired partitions formed contiguous token
runs. A subset of BytesAnticompacted.

|MutatedAnticompactionGauge |Gauge<Double> |Ratio of bytes mutated vs
total bytes repaired.
|===
Expand Down
28 changes: 28 additions & 0 deletions src/java/org/apache/cassandra/config/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,34 @@ public MemtableOptions()
@Replaces(oldName = "sstable_preemptive_open_interval_in_mb", converter = Converters.NEGATIVE_MEBIBYTES_DATA_STORAGE_INT, deprecated = true)
public volatile DataStorageSpec.IntMebibytesBound sstable_preemptive_open_interval = new DataStorageSpec.IntMebibytesBound("50MiB");

/**
* Anticompact by splitting an sstable with {@code ZeroCopySSTableSplitter} -- copying compression chunks
* verbatim -- when its full / transient / unrepaired partitions form contiguous token runs. Interleaved
* ranges, which is what vnodes produce, fall back to the normal rewrite.
* <p>
* A verbatim copy cannot purge tombstones, so anticompaction stops doing so for the sstables it handles
* (retention only, never data loss), and the children's per-sstable statistics are inherited rather than
* recomputed. Refused outright for tables with a secondary index; unsupported but NOT refused on JBOD, since
* children are always written into the parent's directory with no free-space check.
*/
public volatile boolean zero_copy_anticompaction_enabled = false;

/**
* Let the zero-copy splitter share a child's Data.db extents with its parent via {@code FICLONERANGE} rather
* than copying them, so a split writes no data blocks and uses no extra disk space. Needs xfs with
* {@code -m reflink=1} or btrfs, discovered by trying: elsewhere the first attempt per directory fails, is
* logged once, and every split from then on copies as before. Both paths produce identical children.
*/
public volatile boolean zero_copy_split_reflink_enabled = true;

/**
* Write Digest.crc32 for the children of a zero-copy split. Producing it is one full read of every child,
* which with the extents shared is the whole remaining cost of a split. Nothing requires the component and a
* compressed sstable is self-checking without it, but {@code Verifier} answers its absence with a full
* extended verification, so {@code nodetool verify} gets slower for those children.
*/
public volatile boolean zero_copy_split_digest_enabled = true;

public volatile boolean key_cache_migrate_during_compaction = true;
public volatile int key_cache_keys_to_save = Integer.MAX_VALUE;
@Replaces(oldName = "key_cache_size_in_mb", converter = Converters.MEBIBYTES_DATA_STORAGE_LONG, deprecated = true)
Expand Down
42 changes: 42 additions & 0 deletions src/java/org/apache/cassandra/config/DatabaseDescriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -3405,6 +3405,48 @@ public static void setSSTablePreemptiveOpenIntervalInMiB(int mib)
conf.sstable_preemptive_open_interval = new DataStorageSpec.IntMebibytesBound(mib);
}

/** @see Config#zero_copy_anticompaction_enabled */
public static boolean getZeroCopyAnticompactionEnabled()
{
return conf.zero_copy_anticompaction_enabled;
}

public static void setZeroCopyAnticompactionEnabled(boolean enabled)
{
if (conf.zero_copy_anticompaction_enabled != enabled)
logger.info("Changing zero_copy_anticompaction_enabled to {}", enabled);
conf.zero_copy_anticompaction_enabled = enabled;
}

/**
* @see Config#zero_copy_split_reflink_enabled -- filesystem support is discovered by trying, so true here does
* not mean any extent will actually be shared.
*/
public static boolean getZeroCopySplitReflinkEnabled()
{
return conf.zero_copy_split_reflink_enabled;
}

public static void setZeroCopySplitReflinkEnabled(boolean enabled)
{
if (conf.zero_copy_split_reflink_enabled != enabled)
logger.info("Changing zero_copy_split_reflink_enabled to {}", enabled);
conf.zero_copy_split_reflink_enabled = enabled;
}

/** @see Config#zero_copy_split_digest_enabled */
public static boolean getZeroCopySplitDigestEnabled()
{
return conf.zero_copy_split_digest_enabled;
}

public static void setZeroCopySplitDigestEnabled(boolean enabled)
{
if (conf.zero_copy_split_digest_enabled != enabled)
logger.info("Changing zero_copy_split_digest_enabled to {}", enabled);
conf.zero_copy_split_digest_enabled = enabled;
}

public static boolean getTrickleFsync()
{
return conf.trickle_fsync;
Expand Down
Loading