KandraMultiDcTestcontainers
A real two-datacenter, one-node-per-DC Cassandra topology for DC-aware load-balancing/failover tests (GH #84 / ISS-076), backed by multidc-docker-compose.yml (this module's src/main/resources) via Testcontainers' ComposeContainer. Mirrors KandraTestcontainers's lazy-singleton, JVM-shared style -- one topology per JVM, reused across every test class that touches it, rather than a fresh cluster per test.
Why one node per DC (2 containers total), not 2+ nodes per DC
A single node per DC is the minimum topology that has the property this fixture exists to test: two distinct datacenters a client can genuinely fail over between, with NetworkTopologyStrategy replication that means something (as opposed to a single-DC cluster where every "DC" in config is cosmetic). Adding a second node per DC would let within-DC token-aware routing be exercised too, but that's not what this issue is about -- it's about cross-DC failover/load-balancing -- and real multi-node gossip convergence is significantly slower and flakier under Testcontainers than a two-single-node topology (observed directly while building this fixture: a single node with no custom config already takes ~15-20s to reach Startup complete; two nodes gossiping into one cluster took closer to a minute). Doubling the node count per DC would roughly double that again, for coverage this issue doesn't ask for. If a future issue specifically needs multi-node-per-DC behavior (e.g. real token-range replica placement within a DC), scale multidc-docker-compose.yml up rather than replacing this fixture.
Topology
cassandra-dc1/cassandra-dc2, both plaincassandra:4.1(matching KandraTestcontainers's single-node convention -- no customcassandra.yaml).GossipingPropertyFileSnitchviaCASSANDRA_ENDPOINT_SNITCH, DC/rack viaCASSANDRA_DC/CASSANDRA_RACK, both nodes seeded fromcassandra-dc1viaCASSANDRA_SEEDS-- so they gossip into one cluster rather than forming two independent single-node clusters that happen to share a Docker network.cassandra-dc2waits forcassandra-dc1to reportservice_healthybefore its own container even starts, since racing dc2's bootstrap against dc1's gossip start-up is a likely source of flakiness in a from-scratch two-node cluster.See
multidc-docker-compose.ymlitself for the exact environment/healthcheck configuration.
Usage
class DcFailoverTest {
private val db = KandraMultiDcTestcontainers.freshNetworkTopologyKeyspace(User::class)
@AfterEach fun cleanup() { db.close(); KandraMultiDcTestcontainers.unpauseAll() }
@Test fun `fails over to dc2 when dc1 is unreachable`() {
KandraMultiDcTestcontainers.pause(KandraMultiDcTestcontainers.DC1)
// ... exercise install(Kandra) with loadBalancing.dcAwareFailover / failover.onLocalDcUnavailable ...
}
}Properties
The compose-managed topology -- started once per JVM, on first access, exactly like KandraTestcontainers.container's by lazy { ... start() } pattern. Startup timeouts are generous (see class doc: two-node gossip convergence from cold is meaningfully slower than the single-node case).
Functions
Creates a fresh keyspace with genuine NetworkTopologyStrategy replication across both DCs (default {'dc1': 1, 'dc2': 1} -- one real replica per DC, the most RF a one-node-per-DC topology can actually serve reads/writes at; pass a higher factor only for metadata-only assertions, mirroring KandraTestcontainers.freshKeyspace's identical caveat), registers classes, and creates their tables against the DC named by localDatacenter.
Unpauses both DCs -- a safe, idempotent reset to call from @AfterEach regardless of what a test paused.