Skip to content

AxonOps — AI-Native Control Plane for Open Source Data Platforms

Multi-Datacenter Deployments

Apache Cassandra provides native support for multi-datacenter deployments, enabling geographic distribution, disaster recovery, and read locality without external replication tools.


Multi-datacenter deployment distributes data across geographically separated locations. Cassandra treats this as a first-class feature—replication across datacenters uses the same mechanisms as replication within a datacenter.

Use CaseBenefit
Geographic read localityUsers read from nearby nodes (latency varies by network topology)
Disaster recoverySurvive complete datacenter failure
Regulatory complianceKeep data within geographic boundaries
Follow-the-sun operationsShift load to active regions
Read scalingIsolate analytics workloads to dedicated DC
CapabilityDescription
Asynchronous replicationWith LOCAL_* consistency levels, writes replicate to remote DCs without blocking the coordinator response
Per-DC replication factorConfigure replicas independently per datacenter
LOCAL consistency levelsQueries execute within local DC only
Automatic topology awarenessDrivers discover and route to local nodes
No external coordinationNo ZooKeeper, no consensus protocols for replication

All datacenters accept both reads and writes. This is Cassandra's natural operating mode.

Active-active topology across three datacentersActive-active topology across three datacentersActive-Active Multi-DCDC: us-eastDC: eu-westDC: ap-southNode 1Node 2Node 3Node 4Node 5Node 6Node 7Node 8Node 9RF=3 per DCLOCAL_QUORUM for reads/writesasync replicationasync replicationasync replication

Characteristics:

  • All DCs serve production traffic
  • Writes in any DC replicate to all DCs asynchronously
  • Use LOCAL_QUORUM for local-DC-only latency
  • Conflict resolution via last-write-wins (LWW) timestamps

Configuration:

CREATE KEYSPACE ecommerce WITH replication = {
'class': 'NetworkTopologyStrategy',
'us-east': 3,
'eu-west': 3,
'ap-south': 3
};

One or more DCs serve only reads, typically for analytics or reporting workloads.

Active-passive topology with a read-only analytics datacenterActive-passive topology with a read-only analytics datacenterActive-PassiveDC: productionDC: analyticsNode 1-3Node 4-6Read-only workloadsNo production trafficasync replication

Use cases:

  • Heavy analytics queries isolated from production
  • Reporting systems with eventual consistency tolerance
  • Data science workloads

A standby DC for failover, receiving writes but not serving traffic until needed.

ModeProduction DCDR DCFailover
Hot standbyActive R/WReceives writes, no trafficInstant
Warm standbyActive R/WReceives writes, periodic validationMinutes
Cold standbyActive R/WBackup restore onlyHours

Routing is Driver-Level, Not CQL-Level

Cassandra does not support query-level hints or directives for datacenter routing in CQL. All routing decisions are made by the client driver based on its configured policies.

Driver load balancing routing a query to the local datacenterDriver load balancing routing a query to the local datacenterCassandra DriverCassandra ClusterLoad Balancing PolicyConnection PoolDC: localDC: remoteApplicationDCAwareRoundRobinPolicy+ TokenAwarePolicyqueryselect noderoute to local DCexecutecross-DCreplication
ResponsibilityMechanism
Datacenter selectionwithLocalDatacenter("dc-name") at driver init
Node selection within DCToken-aware routing to replica nodes
Failover to remote DCConfigurable via usedHostsPerRemoteDc
Latency optimizationLatency-aware policy for lowest-latency node
  • No USE DATACENTER directive
  • No query hints like /* dc=us-east */
  • No session-level DC switching
  • No per-query DC override

Routing is controlled through:

  1. Driver configuration (datacenter selection)
  2. Consistency level (LOCAL_* restricts to local DC)
  3. Application architecture (deploy app instances per region)

// Java Driver 4.x - withLocalDatacenter() configures DC-aware routing
CqlSession session = CqlSession.builder()
.addContactPoint(new InetSocketAddress("cassandra-us-east.example.com", 9042))
.withLocalDatacenter("us-east") // Critical: sets local DC for routing
.build();
// All queries route to us-east by default
// Use LOCAL_QUORUM for local-DC-only execution
SimpleStatement stmt = SimpleStatement.builder("SELECT * FROM users WHERE id = ?")
.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM)
.build();
from cassandra.cluster import Cluster
from cassandra.policies import DCAwareRoundRobinPolicy, TokenAwarePolicy
from cassandra import ConsistencyLevel
# Configure DC-aware routing
load_balancing_policy = TokenAwarePolicy(
DCAwareRoundRobinPolicy(local_dc='us-east')
)
cluster = Cluster(
contact_points=['cassandra-us-east.example.com'],
load_balancing_policy=load_balancing_policy
)
session = cluster.connect('ecommerce')
# Set default consistency level
session.default_consistency_level = ConsistencyLevel.LOCAL_QUORUM
# Execute query (routes to us-east)
rows = session.execute("SELECT * FROM users WHERE id = %s", [user_id])
package main
import (
"github.com/gocql/gocql"
"log"
)
func main() {
cluster := gocql.NewCluster("cassandra-us-east.example.com")
cluster.Keyspace = "ecommerce"
// Configure DC-aware routing
cluster.PoolConfig.HostSelectionPolicy = gocql.TokenAwareHostPolicy(
gocql.DCAwareRoundRobinPolicy("us-east"),
)
// Set consistency level
cluster.Consistency = gocql.LocalQuorum
session, err := cluster.CreateSession()
if err != nil {
log.Fatal(err)
}
defer session.Close()
// Queries route to us-east with LOCAL_QUORUM
var name string
err = session.Query("SELECT name FROM users WHERE id = ?", userID).Scan(&name)
}
const cassandra = require('cassandra-driver');
const client = new cassandra.Client({
contactPoints: ['cassandra-us-east.example.com'],
localDataCenter: 'us-east', // Critical: sets local DC
keyspace: 'ecommerce',
policies: {
loadBalancing: new cassandra.policies.loadBalancing.TokenAwarePolicy(
new cassandra.policies.loadBalancing.DCAwareRoundRobinPolicy('us-east')
)
},
queryOptions: {
consistency: cassandra.types.consistencies.localQuorum
}
});
// All queries route to us-east
const query = 'SELECT * FROM users WHERE id = ?';
const result = await client.execute(query, [userId], { prepare: true });

LevelScopeLatencyDurability
LOCAL_ONE1 replica in local DCLowestSingle node (replicates to remote DCs asynchronously)
LOCAL_QUORUMQuorum in local DCLowLocal DC durable (replicates to remote DCs asynchronously)
QUORUMQuorum across all DCsHigh (cross-region RTT)Global durable
EACH_QUORUMQuorum in each DCHighestStrongest
ALLAll replicas everywhereHighestComplete
WorkloadWrite CLRead CLTrade-off
Low-latency readsLOCAL_QUORUMLOCAL_ONEMay read stale
Balanced (recommended)LOCAL_QUORUMLOCAL_QUORUMLocal consistency
Strong consistencyQUORUMQUORUMCross-DC latency
Critical writesEACH_QUORUMLOCAL_QUORUMWrite waits for all DCs

Latency values are workload and network dependent. Representative ranges for illustrative purposes:

LOCAL_QUORUM (same region): Low latency (network RTT within region)
QUORUM (cross-region): Higher latency (requires cross-region round trips)
EACH_QUORUM (all regions): Highest latency (slowest DC determines latency)

QUORUM in Multi-DC

With QUORUM across 3 DCs (RF=3 each, 9 total replicas), quorum requires 5 responses. This likely spans multiple DCs, adding cross-region latency to every operation.


For implementing Command Query Responsibility Segregation (CQRS) with multi-datacenter Cassandra, see the dedicated CQRS Pattern Guide.

CQRS aligns naturally with multi-DC deployments:

  • Command services deploy in primary DC with LOCAL_QUORUM writes
  • Query services deploy per region with LOCAL_ONE reads
  • Cassandra's async replication provides eventual consistency for read replicas

Driver failover to remote DCs requires explicit configuration. By default, most drivers do not contact remote DC nodes.

// Java Driver 4.x: Remote DC failover requires configuration in application.conf
// datastax-java-driver.basic.load-balancing-policy.slow-replica-avoidance = true
// Note: Cross-DC failover behavior is driver-version dependent
# Python: Configure remote DC fallback (explicit configuration required)
policy = DCAwareRoundRobinPolicy(
local_dc='us-east',
used_hosts_per_remote_dc=2 # Must set >0 to enable remote DC fallback
)
# Without used_hosts_per_remote_dc, remote DCs are not contacted
ScenarioDriver Behavior
Single node failureRoutes to other local DC nodes
Multiple node failuresContinues with remaining local nodes
Complete local DC failureFalls back to remote DC only if used_hosts_per_remote_dc > 0 (Python) or equivalent configured
Network partitionBehavior depends on which nodes are reachable and driver configuration

For planned failover or disaster recovery:

1. Verify remote DC health:

Terminal window
nodetool status # Check all DCs
nodetool describecluster # Verify schema agreement

2. Update application configuration:

# Update CASSANDRA_LOCAL_DC to new primary
cassandra:
local-datacenter: eu-west # Was us-east
contact-points: cassandra-eu.example.com

3. Rolling restart applications:

Terminal window
kubectl rollout restart deployment/order-service

4. Verify traffic routing:

Terminal window
# Check driver metrics or Cassandra logs for query sources
nodetool clientstats
StepAction
1. Restore failed DCBring nodes back online
2. Verify replicationRun nodetool repair if needed
3. Validate dataCheck for any conflicts
4. Gradual traffic shiftUpdate app configs in stages
5. MonitorWatch latency and error rates

Multi-region network topology with peered VPCsMulti-region network topology with peered VPCsMulti-Region NetworkRegion: us-eastVPCRegion: eu-westVPCApp TierCassandra (3 AZs)App TierCassandra (3 AZs)Private subnetsNo public IPsSecurity groupsVPC Peering /Transit Gateway
PortPurposeScope
9042CQL native protocolApp → Cassandra
7000Inter-node (unencrypted)Cassandra → Cassandra
7001Inter-node (TLS)Cassandra → Cassandra
7199JMXManagement only

Data Transfer Costs

Cross-region replication generates significant data transfer. Budget for:

  • Write amplification: Each write replicates to RF nodes per DC
  • Repair traffic: Cross-DC repair can be substantial
  • Typical cost: $0.02-0.09/GB depending on cloud provider and regions
RouteTypical Latency
Same AZ<1ms
Cross-AZ (same region)1-2ms
US East ↔ US West60-80ms
US ↔ Europe80-120ms
US ↔ Asia Pacific150-200ms
Europe ↔ Asia Pacific120-180ms

PracticeReason
Use NetworkTopologyStrategyRequired for multi-DC awareness
Set LOCAL_QUORUM as defaultBalances consistency and latency
Configure driver with explicit local DCPrevents routing to wrong DC
Use GossipingPropertyFileSnitchProduction-grade topology detection
Deploy RF ≥ 3 per DCSurvives node failures within DC
Run repairs regularlyMaintains consistency across DCs
Anti-PatternProblem
Using SimpleStrategyNo DC awareness
Using QUORUM for all operationsUnnecessary cross-DC latency
Relying on external load balancerLoses token awareness, adds latency
Mixing snitch typesCauses topology confusion
Skipping withLocalDatacenter()Driver may route to wrong DC
RF=1 in any DCNo fault tolerance