Skip to content
 
 

Repository files navigation

SSTable Tools

CI

SSTable Tools queries and mutates explicitly selected, stopped Cassandra SSTables with the matching installed Cassandra release and its stock cqlsh. It is under active development and is not yet an operator-ready production tool.

Quick Start

Install either Linux package from a release, or unpack the standalone archive:

# Debian or Ubuntu
sudo dpkg -i sstable-tools_1.2.3-1_all.deb

# RHEL, Rocky Linux, AlmaLinux, or another RPM-based distribution
sudo rpm -i sstable-tools-1.2.3-1.noarch.rpm

# Standalone archive
tar -xzf sstable-tools-1.2.3.tar.gz

The packages deliberately do not declare a Java or Cassandra package dependency. The machine must already have a Java runtime supported by the selected Cassandra release. The packaged command is /usr/bin/sstable-tools; the archive contains ./sstable-tools.

Point the launcher at Cassandra once. Packaged Cassandra normally uses /usr/share/cassandra; a tarball installation normally uses its lib directory:

export CASSANDRA_LIB_DIR=/usr/share/cassandra
# Or: export CASSANDRA_LIB_DIR=/opt/apache-cassandra-5.0.8/lib

sstable-tools --version
sstable-tools runtime preflight

Prepare these inputs before opening an SSTable:

  • A stopped table directory, completed snapshot, or backup copied outside the live Cassandra data directories.
  • A CQL schema bundle containing the exact keyspace, table, table ID, primary key, columns, and referenced UDTs for those SSTables.
  • Every SSTable that contributes to the logical table. Cassandra may spread one table across multiple data_file_directories.

Query every SSTable in one complete table directory without publishing files:

TABLE_DIR=/archive/acme/users-7ad54392bcdd35a684174e047860b377
SCHEMA=/archive/acme-users.cql

sstable-tools \
  --output-dir "$TABLE_DIR" \
  --schema "$SCHEMA" \
  cqlsh --execute \
  "SELECT id, name FROM acme.users WHERE id = 1;"

Update that table by supplying a timestamp in Unix microseconds:

NOW_MICROS=$(date +%s%6N)

sstable-tools \
  --output-dir "$TABLE_DIR" \
  --schema "$SCHEMA" \
  cqlsh --execute \
  "UPDATE acme.users USING TIMESTAMP $NOW_MICROS SET name = 'Grace' WHERE id = 1;"

Use the current clock only when it is ahead of the source timestamps. If the source contains future-dated cells, choose a value above the reported source maximum; see Write timestamps.

The update never modifies an existing SSTable. It publishes one new verified component set beside the originals and reports it as published.sstables=<descriptor>. Running the SELECT command again with the same complete --output-dir reads the originals and the new delta together.

For a table split across two Cassandra data directories, select both complete table directories and name exactly one publication directory:

PRIMARY=/data/acme/users-7ad54392bcdd35a684174e047860b377
SECONDARY=/log/data/acme/users-7ad54392bcdd35a684174e047860b377
NOW_MICROS=$(date +%s%6N)

sstable-tools \
  --sstables "$PRIMARY" \
  --sstables "$SECONDARY" \
  --output-dir "$PRIMARY" \
  --schema "$SCHEMA" \
  cqlsh --execute \
  "UPDATE acme.users USING TIMESTAMP $NOW_MICROS SET name = 'Grace' WHERE id = 1;"

sstable-tools \
  --sstables "$PRIMARY" \
  --sstables "$SECONDARY" \
  --output-dir "$PRIMARY" \
  --schema "$SCHEMA" \
  cqlsh --execute \
  "SELECT id, name FROM acme.users WHERE id = 1;"

The second command is the required read-back pattern: it includes the source SSTables from both directories plus the delta published into PRIMARY. For an empty destination, use the first-SSTable example. For system.local, including a table spread across multiple directories, use the system-table example.

Launcher and Automatic Adapter Selection

Use sstable-tools from the DEB/RPM, or ./sstable-tools from the standalone archive. Point it at Cassandra's tarball lib directory or its packaged runtime root with either --cassandra-lib-dir or CASSANDRA_LIB_DIR:

tar -xzf sstable-tools-1.2.3.tar.gz
cd sstable-tools-1.2.3

./sstable-tools \
  --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
  --version

The launcher scans the selected directory plus its nested or adjacent lib directory. It finds exactly one apache-cassandra-<version>.jar or cassandra-all-<version>.jar, maps versions 3.11.x, 4.0.x, 4.1.x, and 5.0.x to the matching adapter, derives the Cassandra home from the core JAR location, and forwards the remaining arguments. This supports tarballs such as /opt/apache-cassandra-5.0.8/lib and DEB/RPM installations rooted at /usr/share/cassandra. Multiple or unsupported core JARs fail instead of guessing.

The launcher chooses Java in this order: SSTABLE_TOOLS_JAVA, JAVA_HOME/bin/java, then java from PATH. It always enables headless mode and UTF-8. Add whitespace-delimited launcher-JVM options with SSTABLE_TOOLS_JAVA_OPTS:

CASSANDRA_LIB_DIR=/opt/apache-cassandra-5.0.8/lib \
SSTABLE_TOOLS_JAVA_OPTS="-Xms256m -Xmx2g" \
  sstable-tools --version

sstable-tools is the only launcher command. The release-specific adapter JARs are internal implementation artifacts selected automatically. Prefix the command with ./ when running from an unpacked release archive.

Direct CQLSH

The primary interface is one command. Use --sstables to select explicit Data.db/TOC.txt files or complete table directories, plus a schema bundle and the matching Cassandra installation. If the selected SSTables span more than one Cassandra data directory, also use --output-dir to choose the table directory where new SSTables will be published. --output-dir can still be used alone to treat one complete directory as both the baseline and the publication destination. The tool opens stock cqlsh; on a clean exit after a mutation it publishes verified new SSTable component sets into the chosen directory.

sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
  --sstables /archive/acme/users-7ad54392bcdd35a684174e047860b377/nb-42-big-Data.db \
  --schema /archive/acme-users.cql \
  cqlsh

The schema bundle must describe the exact keyspace, table, primary key, column types, table ID, and any UDTs used by the selected SSTables.

Partitioner limitation: SSTable Tools currently supports only org.apache.cassandra.dht.Murmur3Partitioner. SSTables produced with RandomPartitioner, ByteOrderedPartitioner, LocalPartitioner, or another partitioner are not supported.

Run a read-only query

Use --execute to run one statement without opening an interactive shell. A read-only session validates and queries the selected data but publishes no new SSTables:

sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
  --sstables /archive/acme/users-7ad54392bcdd35a684174e047860b377/nb-42-big-Data.db \
  --schema /archive/acme-users.cql \
  cqlsh --execute "SELECT id, name FROM acme.users WHERE id = 1;"

Select multiple SSTables

Repeat --sstables or provide a comma-separated list. SSTables for one logical table may come from any number of Cassandra data_file_directories. When they span physical directories, supply --output-dir as the single publication destination:

sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
  --sstables /data/acme/users-7ad54392bcdd35a684174e047860b377/nb-41-big-Data.db \
  --sstables /data/acme/users-7ad54392bcdd35a684174e047860b377/nb-42-big-Data.db \
  --sstables /log/data/acme/users-7ad54392bcdd35a684174e047860b377/nb-43-big-Data.db \
  --sstables /log/data/acme/users-7ad54392bcdd35a684174e047860b377/nb-44-big-Data.db \
  --output-dir /data/acme/users-7ad54392bcdd35a684174e047860b377 \
  --schema /archive/acme-users.cql \
  cqlsh --execute "SELECT id, name FROM acme.users;"

The direct command accepts SELECT plus non-conditional INSERT and UPDATE for the selected table. It does not scan data roots, keyspaces, or unrelated tables. Every selected descriptor must belong to the same logical table and match the supplied schema. When --sstables and --output-dir are combined, every pre-existing SSTable in the output directory must be part of the selection; the tool checks that target subset again before publication. With --sstables alone, all selected SSTables must still reside in one directory so the publication destination is unambiguous.

Create the first SSTable in an output directory

An INSERT does not need an existing SSTable. Use --output-dir instead of --sstables when the destination table directory is the source of truth:

mkdir -p /archive/acme/users-7ad54392bcdd35a684174e047860b377
NOW_MICROS=$(date +%s%6N)
INSERT_CQL="INSERT INTO acme.users (id, name) VALUES (1, 'Ada') USING TIMESTAMP $NOW_MICROS;"

sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
  --output-dir /archive/acme/users-7ad54392bcdd35a684174e047860b377 \
  --schema /archive/acme-users.cql \
  cqlsh --execute "$INSERT_CQL"

The directory must already exist and must not be a symlink. If it is empty, direct CQLSH requires --execute with an INSERT statement. Cassandra 5.0 uses UUID-style identifiers for the first SSTable; older release lines use a numeric identifier.

If the directory already contains complete SSTables, the tool inventories and imports all of them as the baseline. Numeric output is allocated at one above the highest numeric identifier in the directory. UUID-style output keeps the fresh Cassandra-generated identifier; UUIDs are not numerically incremented. The complete directory is checked again immediately before publication, and publication fails if it changed after the baseline was imported.

Insert or update a row

For automation, generate the CQL separately so the microsecond timestamp is obvious:

NOW_MICROS=$(date +%s%6N)
WRITE_CQL="UPDATE acme.users USING TIMESTAMP $NOW_MICROS SET name = 'Grace' WHERE id = 1;"

sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
  --tmp-dir /var/tmp/sstable-tools \
  --output-dir /archive/acme/users-7ad54392bcdd35a684174e047860b377 \
  --schema /archive/acme-users.cql \
  cqlsh --execute "$WRITE_CQL"

--tmp-dir changes the parent of the private temporary workspace. The default is /tmp/sstable-tools/. A successful invocation removes its private child; a failed invocation retains it and prints the path for diagnosis.

Write timestamps

INSERT and UPDATE statements must include an explicit USING TIMESTAMP. The value is a Unix timestamp in microseconds and must be greater than the maximum timestamp in the selected source SSTables. Cassandra's CQL now() function returns a timeuuid and cannot be used for this clause.

If the local clock is ahead of every source timestamp, generate a suitable current timestamp with date +%s%6N:

INSERT INTO acme.users (id, name)
VALUES (1, 'Ada')
USING TIMESTAMP 1785170000000000;

UPDATE acme.users
USING TIMESTAMP 1785170000000001
SET name = 'Grace'
WHERE id = 1;

The Cassandra 4.0, 4.1, and 5.0 guards reject a missing timestamp, or an explicit timestamp that is not greater than the imported source maximum. If a source contains future-dated cells, do not use the current clock value; choose a microsecond value above the reported source maximum instead.

Query or update a system table

Stopped SSTables from Cassandra's built-in system keyspace are supported. The schema bundle identifies exactly one fully qualified built-in table; the matching Cassandra runtime supplies its actual metadata.

NOW_MICROS=$(date +%s%6N)
SYSTEM_CQL="UPDATE system.local USING TIMESTAMP $NOW_MICROS SET cluster_name = 'recovered-cluster' WHERE key = 'local';"

sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
  --output-dir /archive/system/local-7ad54392bcdd35a684174e047860b377 \
  --schema /archive/system.local.cql \
  cqlsh --execute "$SYSTEM_CQL"

This inventories the complete stopped table directory, changes only the isolated copy, and publishes verified sibling SSTables into the same directory. It does not change a running node or Cassandra installation.

If a system table spans Cassandra data directories, select every physical table directory and choose one as the publication target:

sstable-tools --cassandra-lib-dir /opt/apache-cassandra-4.0.18/lib \
  --sstables /data/system/local-7ad54392bcdd35a684174e047860b377 \
  --sstables /log/data/system/local-7ad54392bcdd35a684174e047860b377 \
  --output-dir /data/system/local-7ad54392bcdd35a684174e047860b377 \
  --schema /archive/system.local.cql \
  cqlsh --execute "$SYSTEM_CQL"

Use SSTable Tools v1.0.5 or newer when mutating system.local. Version 1.0.5 added the sandbox topology overrides required when the imported node's datacenter or rack differs from the isolated worker's synthetic topology.

Cassandra 5.0 output format

SSTable Tools follows the selected Cassandra installation's cassandra.yaml; it does not offer a format-conversion mode. The effective output is:

storage_compatibility_mode sstable.selected_format Output
CASSANDRA_4 big Big nb
UPGRADING big Big oa
NONE big Big oa
UPGRADING or NONE bti BTI da

CASSANDRA_4 with bti is invalid. The tool reads cassandra.yaml from CASSANDRA_CONF, the selected tarball's conf directory, or /etc/cassandra for a packaged /usr/share/cassandra runtime. An omitted storage_compatibility_mode defaults to CASSANDRA_4; an omitted sstable.selected_format defaults to big. If no configuration is available, the tool falls back to the selected SSTable inventory and refuses ambiguous mixed Big/BTI input.

--output-format is only an optional assertion. It cannot override cassandra.yaml; a mismatch fails before import or publication with a clear "format conversion is not supported" error. Normally, omit it:

NOW_MICROS=$(date +%s%6N)
INSERT_CQL="INSERT INTO acme.users (id, name) VALUES (2, 'Ada') USING TIMESTAMP $NOW_MICROS;"

sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
  --output-dir /archive/acme/users-7ad54392bcdd35a684174e047860b377 \
  --schema /archive/acme-users.cql \
  cqlsh --execute "$INSERT_CQL"

Original components are never modified. Every adapter allocates the next available numeric SSTable identifier above every descriptor present in the selected source directories and the publication directory when the baseline uses numeric identifiers. For Cassandra 5.0, selecting a descriptor with Cassandra's 28-character UUID/ULID-style identifier enables uuid_sstable_identifiers_enabled in the private sandbox and publishes the Cassandra-generated UUID-style delta unchanged. A mixed numeric and UUID-style selection uses UUID-style output. With --sstables, inference uses only the explicitly selected descriptors. With --output-dir, it uses every complete SSTable in that directory. Output format and Cassandra 5.0 format version come from the selected installation's cassandra.yaml.

After a successful direct mutation, later commands for that table should select both the original descriptors and every published sibling descriptor that should participate in reconciliation. Using the same --output-dir does this automatically.

Existing SSTable components are immutable, so the tool permits reading or copying selected files even when Cassandra has them open. The isolated worker uses only private storage and loopback networking.

DANGER: Publishing newly generated SSTables into a running Cassandra node's live table directory is different from reading existing components. It can race with Cassandra lifecycle operations and damage or confuse the node. Prefer a stopped node or a separate output directory.

Publish while Cassandra is running

If direct CQLSH produces no new SSTables, no live-output confirmation is needed. If it does produce new SSTables and the publication directory belongs to a running Cassandra process, an interactive terminal displays the matched PID and directory and requires typing yes.

For an intentional non-interactive workflow, acknowledge the same risk with --allow-live-cassandra-output:

sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
  --output-dir /var/lib/cassandra/data/acme/users-7ad54392bcdd35a684174e047860b377 \
  --schema /archive/acme-users.cql \
  --allow-live-cassandra-output \
  cqlsh --execute "$INSERT_CQL"

This option bypasses only the live-publication confirmation. It does not disable source inventory verification, output collision checks, or final publication verification.

Command-Line Reference

The general forms are:

sstable-tools --cassandra-lib-dir <path> [tool options] <command>
CASSANDRA_LIB_DIR=<path> sstable-tools [tool options] <command>

--cassandra-lib-dir is a launcher option and must immediately follow sstable-tools. Forwarded tool options may appear before or after the command.

Option Value and scope
--cassandra-lib-dir <path> Cassandra tarball lib directory or packaged runtime root used to detect the release and select an adapter. Equivalent to CASSANDRA_LIB_DIR.
--cassandra-home <path> Cassandra installation used by direct cqlsh, runtime commands, and workspace import, start, or cqlsh.
--java-home <path> Compatible Java installation for the selected Cassandra worker.
--sstables <path> Explicit Data.db, TOC.txt, or table-directory source. Repeat it or use comma-separated paths; one logical table may span physical directories. Valid only with direct cqlsh and workspace create.
--output-dir <path> Existing non-symlink publication directory for direct cqlsh. Used alone, it is also the complete baseline. Combined with --sstables, it selects where deltas are published and its existing SSTables must all be included in the selected source inventory.
--schema <path> UTF-8 CQL schema bundle. Required by direct cqlsh; recorded by workspace create.
--timestamp-policy <policy> wall-clock or after-source. Valid with direct cqlsh and workspace start; the choice is persisted for a managed workspace.
--output-format <format> Optional big or bti assertion for direct cqlsh and workspace create. It cannot override Cassandra 5.0 sstable.selected_format; mismatches fail rather than convert.
--tmp-dir <path> Parent for private direct-CQLSH workspaces. Default: /tmp/sstable-tools.
--execute <cql> Run one CQL statement and exit. Valid with direct cqlsh and workspace cqlsh.
--allow-live-cassandra-output Bypass the confirmation before direct CQLSH publishes new SSTables into a directory owned by a running Cassandra process. Intended for explicit non-interactive acknowledgement.
--mode <mode> delta or snapshot; required by workspace export.
--output <path> Atomic publication destination; required by workspace export.
--confirm-workspace-id <uuid> Exact manifest UUID required by workspace destroy.
--version Print the tool, adapter, and compile-target versions.
--help, -h Print command and option help.

after-source maintains a durable timestamp high-water above the imported source maximum for timestamp-free requests that support it. It does not remove the explicit USING TIMESTAMP requirement enforced by the Cassandra 4.0, 4.1, and 5.0 query guards.

Launcher environment

Variable Purpose
CASSANDRA_LIB_DIR Cassandra tarball lib directory or packaged runtime root; the launcher scans the selected directory and its nested or adjacent lib.
CASSANDRA_HOME Fallback Cassandra home; the launcher scans the home and its lib child.
CASSANDRA_CONF Optional Cassandra configuration directory (or cassandra.yaml path). On Cassandra 5.0, it has precedence when resolving storage_compatibility_mode and sstable.selected_format.
SSTABLE_TOOLS_JAVA Exact Java executable used by the launcher.
JAVA_HOME Supplies $JAVA_HOME/bin/java when SSTABLE_TOOLS_JAVA is unset.
SSTABLE_TOOLS_JAVA_OPTS Additional whitespace-delimited launcher-JVM options, such as -Xms256m -Xmx2g.
SSTABLE_TOOLS_HOME Override the installed JAR directory; normally /usr/share/sstable-tools.
SSTABLE_TOOLS_JAR Override the exact adapter JAR path for development or diagnosis.

The isolated Cassandra worker is started separately with -Xms512m, -Xmx512m, attach disabled, and the release-specific Java module options it requires. --java-home selects that worker's compatible Java installation; SSTABLE_TOOLS_JAVA_OPTS tunes only the small launcher JVM.

Commands

Command Purpose
cqlsh Query explicit SSTables, or inventory and publish verified mutations into --output-dir.
runtime inspect Print resolved Cassandra paths, versions, classpath, and JAR hashes.
runtime preflight Start a separate worker JVM and verify required Cassandra API linkage.
workspace create <path> Inventory sources and create a validated managed workspace.
workspace import <path> Validate and copy selected SSTables into the workspace.
workspace start <path> Start the isolated Cassandra sandbox.
workspace cqlsh <path> Launch the selected installation's stock cqlsh.
workspace status <path> Verify sources and display persisted lifecycle state.
workspace flush <path> Quiesce CQL and flush the workspace table.
workspace export <path> Publish a verified delta or self-contained snapshot.
workspace stop <path> Drain and stop the workspace sandbox.
workspace recover <path> Recover a failed workspace to its last stable state.
workspace destroy <path> Permanently delete an inactive workspace after UUID confirmation.

Runtime Preflight

Each thin JAR uses the Cassandra installation supplied by --cassandra-home. It does not package Cassandra server classes or attach to the installed Cassandra process. The installation's cassandra.yaml is neither required nor read. The tool generates a private sandbox configuration beneath its workspace. Optional distribution JVM options and client resources are discovered automatically from the selected installation; --java-home selects the worker JVM.

sstable-tools --cassandra-lib-dir /usr/share/cassandra \
  --java-home /usr/lib/jvm/java-11-openjdk \
  runtime preflight

runtime inspect prints the discovered runtime paths, Cassandra version, child classpath, and JAR identities. runtime preflight starts a separate worker JVM and verifies the Cassandra APIs required by that adapter before opening an SSTable.

Stock cqlsh is resolved from <cassandra-home>/bin/cqlsh for tarball installations. For the DEB/RPM /usr/share/cassandra layout, the tool uses /usr/bin/cqlsh (or the equivalent path beneath an extracted package root). It does not select an unrelated cqlsh from PATH.

Inspect runtime discovery without starting the worker:

sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
  --java-home /usr/lib/jvm/java-17-openjdk \
  runtime inspect

Managed Workspace Example

The direct cqlsh command is preferred. For diagnostic or recovery workflows that need explicit lifecycle control:

TOOL=sstable-tools
CASE=/var/tmp/sstable-tools-case
export CASSANDRA_LIB_DIR=/opt/apache-cassandra-5.0.8/lib

"$TOOL" \
  --sstables /archive/acme/users-7ad54392bcdd35a684174e047860b377/nb-42-big-Data.db \
  --schema /archive/acme-users.cql \
  workspace create "$CASE"

"$TOOL" workspace import "$CASE"

"$TOOL" \
  --timestamp-policy after-source \
  workspace start "$CASE"

"$TOOL" \
  workspace cqlsh "$CASE" \
  --execute "SELECT id, name FROM acme.users WHERE id = 1;"

"$TOOL" workspace flush "$CASE"
"$TOOL" workspace export "$CASE" \
  --mode delta \
  --output /archive/acme-users-delta
"$TOOL" workspace stop "$CASE"

Use --mode snapshot instead when the export must contain both the imported base and the new SSTables.

Compatibility

All release adapters currently require org.apache.cassandra.dht.Murmur3Partitioner.

The CI matrix creates stopped SSTables with stock Cassandra cqlsh, then runs the matching thin JAR and stock cqlsh direct workflow. Its source tables exercise scalar values, sets, maps, tuples, frozen UDTs, TTLs, and deleted cells. It verifies INSERT, UPDATE, DELETE, SELECT, flush, sibling publication, direct reopen, and unchanged source component hashes.

Artifact Supported Cassandra patches Java runtime Direct output
cassandra-3.11 3.11.19 8 Big me
cassandra-4.0 4.0.0-4.0.18 8-11 Big nb
cassandra-4.1 4.1.0-4.1.11 11 Big nb
cassandra-5.0 5.0.4-5.0.8 17 Big nb (CASSANDRA_4), Big oa/BTI da (UPGRADING or NONE)

The 4.0 adapter compiles against the first patch in its release line. The 4.1 adapter carries both native query-handler ABIs used across the 4.1 patch line. CI resolves Cassandra's real Maven runtime and starts the adapter against every declared 4.0.x, 4.1.x, and 5.0.x patch. Cassandra 4.0 is checked on both Java 8 and Java 11.

Real Cassandra producer/import coverage includes 3.11.0 and 3.11.19, 4.0.0 and 4.0.18, 4.1.0 and 4.1.11, and 5.0.4 and 5.0.8. Cassandra 3.11.0 is an older-format producer imported by the supported 3.11.19 runtime; it is not an additional supported runtime. The latest patch in each release line runs the full stopped-SSTable and stock-cqlsh workflow, and Cassandra 5.0.4 also runs the direct workflow as the minimum supported 5.0 runtime.

The Cassandra 5.0.8 acceptance job uses real two-data_file_directories system.local SSTables and tests each supported YAML-driven output:

storage_compatibility_mode sstable.selected_format Required output
CASSANDRA_4 big nb Big
UPGRADING big oa Big
NONE big oa Big
UPGRADING bti da BTI
NONE bti da BTI

Those tests reopen the combined table through stock Cassandra after publishing into one of the two data directories. The BTI path additionally rejects sets missing Partitions.db or Rows.db, repeats multi-directory imports to catch ordering races, and the Big path includes an uncompressed table.

Release-package CI installs the dependency-free DEB and RPM into separate clean Java 17 containers and invokes the installed universal launcher. See Real Cassandra CI coverage for the full test topology and the distinction between full-node and linkage coverage.

The direct workflow intentionally has no sstableloader, streaming, clean-node import, or broad filesystem discovery.

Building

This is one Maven multi-module build. Compile and run the shared and version-specific unit tests with:

mvn test

Build the four thin JARs and verify that they do not embed Cassandra runtime classes:

mvn package
scripts/verify-thin-jars

Artifacts are written to workers/cassandra-<line>/target/.

Release Process

The Git tag is the source of truth for a published version. A stable tag such as v1.2.3 builds JARs and packages with embedded version 1.2.3; there is no separate release-version file. Untagged development builds use the default 0.1.0-SNAPSHOT Maven revision.

Build a release candidate locally

Release packaging requires Java 17, Go 1.26.4 or newer, nFPM, dpkg-deb, rpm, rpm2cpio, and cpio. CI pins Go 1.26.4 and nFPM v2.47.0; install the same nFPM version with:

go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.47.0

Build and verify a candidate with the exact version that will be tagged:

RELEASE_VERSION=0.1.0

mvn clean verify -Drevision="$RELEASE_VERSION"
scripts/package-release \
  --output target/release \
  --version "$RELEASE_VERSION"
scripts/package-linux-packages \
  --release-dir "target/release/sstable-tools-$RELEASE_VERSION"
scripts/verify-linux-packages \
  "target/release/sstable-tools-$RELEASE_VERSION"
scripts/verify-release-bundle \
  "target/release/sstable-tools-$RELEASE_VERSION"

This creates one auto-detecting sstable-tools launcher, four internal adapter JARs, an architecture-independent DEB, and a noarch RPM under target/release/sstable-tools-$RELEASE_VERSION/. It also creates the executable-mode-preserving standalone archive target/release/sstable-tools-$RELEASE_VERSION.tar.gz. Both Linux packages install one command and deliberately declare no package-manager dependencies:

sstable-tools

Use --sign on scripts/package-linux-packages to create a detached SHA256SUMS.asc signature after the DEB and RPM have been included in SHA256SUMS. For a packaging-only rebuild of the same upstream version, use --package-release 2 (then increment it for later rebuilds).

Publish a tagged release

After the candidate is verified, the release commit is pushed, and CI is passing, create and push an annotated stable-version tag:

RELEASE_VERSION=0.1.0
git tag -a "v$RELEASE_VERSION" -m "SSTable Tools $RELEASE_VERSION"
git push origin "v$RELEASE_VERSION"

The Release workflow first requires a successful full CI workflow for the exact release commit. It reuses matching evidence when available; otherwise it dispatches CI for the release ref and waits for it to pass. Only then does it build and test every adapter with that exact Maven revision, reject mismatched embedded JAR versions, create and verify both Linux packages, run the release security gate, and publish the resulting directory as a workflow artifact and GitHub Release. After the security gate passes, it also publishes the DEB and RPM to the configured Google Artifact Registry Apt and Yum repositories.

The security gate scans the built release with ClamAV, analyzes Java source with CodeQL security-extended, checks source secrets and configuration with Trivy, records the full Maven build/compatibility graph, and scans the published SPDX SBOM for known dependency vulnerabilities. The full Maven graph is report-only because it includes Cassandra compatibility and provided dependencies that are not shipped. Malware, scanner errors, missing reports, or HIGH/CRITICAL CodeQL, source-security, or published-SBOM findings block publication. The workflow always retains diagnostic reports when the scan step runs; successful GitHub Releases include sstable-tools-<version>-security-reports.tar.gz with the Markdown summary, tool metadata, ClamAV logs, CodeQL SARIF, Trivy JSON, and Maven dependency trees.

If the RELEASE_GPG_PRIVATE_KEY repository secret is configured, the workflow imports it and signs the final checksum file. The workflow can also be started manually with an explicit stable version for release testing or recovery.

Google Artifact Registry publication requires the GCP_CREDENTIALS repository secret. The release publisher uploads directly to the AxonOps repositories axonops-public/europe/axonops-apt and axonops-public/europe/axonops-yum.

See the release guide for the complete artifact list and package-revision details.

Design Documents

The workspace ... commands remain an advanced diagnostic and recovery interface. They are not the normal operator workflow.

About

Tools for parsing, creating and doing other fun stuff with sstables

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages