Cassandra 3.0.12 fixes legacy SSTable deletion during upgrades
A filename-prefix collision could make compaction delete unrelated legacy SSTables after a 2.1 upgrade. Cassandra 3.0.12 corrects the match while preserving transaction-log compatibility.
Deleting the inputs after a successful compaction requires an exact account of which files belong to those inputs. Cassandra 3.0.12 fixes a case where that account was too broad. With legacy SSTables from Cassandra 2.1, a filename prefix could identify several generations of files when the cleanup code intended to identify only one. The consequence was possible data loss during an upgrade to 3.0.
Michael Shuler's 10 March announcement identifies 3.0.12 as a bug-fix release for the 3.0 series. Its changelog puts CASSANDRA-13294 first. The fix adds a component separator to the absolute path used by a lifecycle log record. That small change deserves attention because file naming participates directly in the database's deletion decisions.
I find Cassandra attractive for systems that need its distributed deployment model. That makes the correctness of ordinary storage maintenance especially relevant to my assessment of it. A maintenance release can change the safety of an upgrade without adding anything to the query language.
A generation number is not a complete prefix
The original issue describes the failing sequence. After compaction finishes, Cassandra deletes the files it compacted away. The lifecycle code finds those files by selecting names that start with an absolute file path. For a legacy file, the example descriptor ends in keyspace1-standard1-ka-2. That prefix also matches legacy descriptors whose generation begins with 2.
A generation such as 20 therefore creates an ambiguity if the lookup stops immediately after the digit 2. The filenames represent distinct SSTables, but a string-prefix test has no knowledge of numeric boundaries. The defect concerns the set of files selected for cleanup. It does not require the file contents themselves to contain invalid data.
The release's LogRecord implementation documents the distinction between the legacy naming scheme and newer names. Legacy files have no separator after the generation number in the descriptor's base filename. Appending the component separator makes the boundary explicit before the prefix comparison takes place.
For the example in the issue, the relevant distinction can be expressed with these illustrative filename prefixes:
keyspace1-standard1-ka-2
keyspace1-standard1-ka-2-
The first prefix can select generations beginning with the same digits. The second requires the component separator immediately after generation 2. Appending the separator preserves prefix matching as the way to collect an SSTable's component files while making that match specific to the intended descriptor.
The correction must also read existing logs
Changing the in-memory path is only part of the implementation. The same class reads and writes lifecycle transaction records. Its record types distinguish added files, removed files, and the final commit or abort decision. The comments describe added files as retained on commit and removed files as retained on abort. These records therefore contain information needed to decide which files should survive a transaction's outcome.
The corrected constructor for an SSTable record appends Component.separator to the descriptor's base filename before canonicalizing the path. The parser also appends that separator when it reconstructs a record from an existing log line. Both newly created records and records recovered from disk acquire the more precise internal path.
Writing that extra character into the persisted representation would introduce a separate compatibility problem. Existing transaction files can remain after an unclean shutdown. Their checksums must still match when a maintenance release reads them. The implementation's absolutePath() method therefore removes the final separator when formatting a record for disk and when calculating its checksum.
This produces a deliberate difference between the internal representation and the serialized representation. File discovery uses the internal path with the separator. Serialization retains the prior spelling. The fix changes the deletion lookup without making previously written lifecycle records fail their checksum checks.
I regard that compatibility detail as the most instructive part of the patch. Correcting a filename comparison is straightforward in isolation. Correcting it in a database requires accounting for records written before the correction, including records that only become relevant after a process fails.
Upgrade safety includes maintenance after startup
The issue concerns upgrading from 2.1 to 3.0 because old SSTable names remain relevant to the new implementation. Successfully opening those files does not establish that every later maintenance operation interprets their names correctly. Compaction and its cleanup need to preserve the distinction among files created under the older convention.
The 3.0.12 upgrade notes retain the general advice to take a snapshot before an upgrade. They also explain that a newer Cassandra version can read the preceding version's files while the reverse need not hold. A binary rollback and a data-format rollback are consequently different operations. The deletion defect gives a concrete reason to consider the whole lifetime of legacy files during an upgrade.
The same release notes discuss otc_coalescing_strategy. They say its default changed from DISABLED in 2.1 to TIMEHORIZON in 2.2 and 3.0, and describe a performance regression with that value. Users upgrading to 3.0 are advised to consider DISABLED. This is a separate issue from SSTable deletion, but it illustrates another source of changed behavior when configuration crosses release lines.
The distinction matters when reading a patch-release summary. The release contains corrections to the 3.0 implementation, while the upgrade path also carries choices inherited from earlier versions. CASSANDRA-13294 specifically narrows the file set used by lifecycle cleanup. Existing transaction logs keep their serialized representation, and legacy SSTables gain an unambiguous component boundary in the path used to find their files.