Skip to content
Merged
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
209 changes: 209 additions & 0 deletions .github/workflows/integration-cassandra.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
# Copyright 2026 ExtendDB contributors
# SPDX-License-Identifier: Apache-2.0
name: Cassandra Integration Tests

on:
pull_request:
merge_group:
push:
branches: [main, feat/storage-cassandra-in-tree]

permissions:
contents: read

# The Cassandra backend's integration tests (crates/storage-cassandra/tests)
# talk to a live node at 127.0.0.1:9042 with cassandra/cassandra credentials
# and are not feature-gated — without this workflow they never run in CI and
# every green result is a local claim. The TTL claim protocol in particular
# fails silently if the stored item encoding drifts, so these tests are the
# only automated signal for a whole class of regressions.
#
# A single-node container is enough: the tests create their own keyspaces
# (RF=1) and run serially because the sweep and reconciliation passes are
# global.
#
# The cassandra:4.1 image does not honour CASSANDRA_AUTHENTICATOR, so each job
# patches cassandra.yaml inside the running container and restarts it before
# running any tests. The health-check on the service container uses the default
# AllowAllAuthenticator credentials (no auth challenge), so the container is
# healthy before the patch step runs; after the restart a second wait loop
# confirms the node is back up with PasswordAuthenticator active.

jobs:
cassandra-storage:
runs-on: ubuntu-latest
services:
cassandra:
image: cassandra:4.1

@robinnsc robinnsc Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cassandra:4.1 is a mutable tag. Can pin by digest, matching #345, could also be a follow up later

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's okay, I'm going to defer this, in part because I'm not sure that the trade-offs are a net positive. I get the benefit for test repeatability, but I'm less sold on the cost of being locked to an arbitrary tag, which our consumers may or may not be.

ports:
- 9042:9042
env:
CASSANDRA_CLUSTER_NAME: extenddb-ci
HEAP_NEWSIZE: 128M
MAX_HEAP_SIZE: 1024M
options: >-
--health-cmd "cqlsh -e 'SELECT release_version FROM system.local'"
--health-interval 15s
--health-timeout 10s
--health-retries 20
--health-start-period 60s
steps:
- uses: actions/checkout@v6

@robinnsc robinnsc Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action pins are floating (@v6, @stable, @v2, @v5). Consistent with integration-mongodb.yml, but the release workflows pin by SHA; the integration workflows are the outliers, though not blocking

- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
- name: Configure Cassandra authentication
run: |
docker exec ${{ job.services.cassandra.id }} sed -i \
's/^authenticator: .*/authenticator: PasswordAuthenticator/' \
/etc/cassandra/cassandra.yaml
docker restart ${{ job.services.cassandra.id }}
for i in $(seq 1 30); do

@robinnsc robinnsc Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Post-restart wait may be too short. docker restart resets the startup clock, and this loop allows 30×2s = 60 seconds. Cassandra on a loaded shared runner can exceed that, and a timeout here fails the job before any test runs. Can give it 3 - 5 minutes. (Same loop at lines 116 and 166.)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if docker exec ${{ job.services.cassandra.id }} \
cqlsh -u cassandra -p cassandra \
-e 'SELECT release_version FROM system.local' \
>/dev/null 2>&1; then
echo "Cassandra ready with PasswordAuthenticator after ${i}s"
exit 0
fi
sleep 2
done
echo "Cassandra did not become ready" >&2; exit 1
- name: Unit tests
run: cargo test -p extenddb-storage-cassandra --lib
- name: TTL integration tests
run: cargo test -p extenddb-storage-cassandra --test ttl_integration -- --test-threads=1
env:
EXTENDDB_TEST_CASSANDRA: required
- name: Direct storage-trait integration tests
# Ported from the plug-in repo's tests/rust suite. Parallel-safe:
# each test provisions its own account and keyspace; the shared
# control-plane setting test restores what it mutates.
run: cargo test -p extenddb-storage-cassandra --test direct_integration
env:
EXTENDDB_TEST_CASSANDRA: required

cassandra-pytest:
runs-on: ubuntu-latest
services:
cassandra:
image: cassandra:4.1
ports:
- 9042:9042
env:
CASSANDRA_CLUSTER_NAME: extenddb-ci
HEAP_NEWSIZE: 128M
MAX_HEAP_SIZE: 1024M
options: >-
--health-cmd "cqlsh -e 'SELECT release_version FROM system.local'"
--health-interval 15s
--health-timeout 10s
--health-retries 20
--health-start-period 60s
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
- name: Configure Cassandra authentication
run: |
docker exec ${{ job.services.cassandra.id }} sed -i \
's/^authenticator: .*/authenticator: PasswordAuthenticator/' \
/etc/cassandra/cassandra.yaml
docker restart ${{ job.services.cassandra.id }}
for i in $(seq 1 30); do
if docker exec ${{ job.services.cassandra.id }} \
cqlsh -u cassandra -p cassandra \
-e 'SELECT release_version FROM system.local' \
>/dev/null 2>&1; then
echo "Cassandra ready with PasswordAuthenticator after ${i}s"
exit 0
fi
sleep 2
done
echo "Cassandra did not become ready" >&2; exit 1
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install Python dependencies
run: pip install -r requirements.txt
- name: Build release (cassandra backend)
run: cargo build --release --no-default-features --features cassandra
- name: Run Cassandra pytest suite
run: devtools/run-cassandra-tests -- --pytest --comprehensive --parallel

cassandra-rust:
runs-on: ubuntu-latest
services:
cassandra:
image: cassandra:4.1
ports:
- 9042:9042
env:
CASSANDRA_CLUSTER_NAME: extenddb-ci
HEAP_NEWSIZE: 128M
MAX_HEAP_SIZE: 1024M
options: >-
--health-cmd "cqlsh -e 'SELECT release_version FROM system.local'"
--health-interval 15s
--health-timeout 10s
--health-retries 20
--health-start-period 60s
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
- name: Configure Cassandra authentication
run: |
docker exec ${{ job.services.cassandra.id }} sed -i \
's/^authenticator: .*/authenticator: PasswordAuthenticator/' \
/etc/cassandra/cassandra.yaml
docker restart ${{ job.services.cassandra.id }}
for i in $(seq 1 30); do
if docker exec ${{ job.services.cassandra.id }} \
cqlsh -u cassandra -p cassandra \
-e 'SELECT release_version FROM system.local' \
>/dev/null 2>&1; then
echo "Cassandra ready with PasswordAuthenticator after ${i}s"
exit 0
fi
sleep 2
done
echo "Cassandra did not become ready" >&2; exit 1
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install Python dependencies
run: pip install -r requirements.txt
- name: Build release (cassandra backend)
run: cargo build --release --no-default-features --features cassandra
- name: Run Cassandra rust integration suite
run: devtools/run-cassandra-tests -- --rust --rust-integration

cassandra-production-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
- name: Check production Cassandra feature graph
run: cargo check --release --no-default-features --features cassandra

cassandra-integration:
runs-on: ubuntu-latest
needs: [cassandra-storage, cassandra-pytest, cassandra-rust, cassandra-production-build]
if: always()
steps:
- run: |
if [ "${{ needs.cassandra-storage.result }}" != "success" ] || \
[ "${{ needs.cassandra-pytest.result }}" != "success" ] || \
[ "${{ needs.cassandra-rust.result }}" != "success" ] || \
[ "${{ needs.cassandra-production-build.result }}" != "success" ]; then
exit 1
fi
Loading
Loading