Skip to main content
Load balancing policies determine which nodes and shards the driver contacts for each request. The policy constructs a query plan - an ordered list of nodes to try when executing a statement.

Overview

When preparing to send a request to ScyllaDB/Cassandra, the load balancing policy constructs a plan of targets (node + optional shard) to contact. The first elements in the plan are the preferred targets (e.g., those with lowest latency or those that are replicas for the data).

LoadBalancingPolicy Trait

Any type implementing the LoadBalancingPolicy trait can be used:
pub trait LoadBalancingPolicy: Send + Sync + std::fmt::Debug {
    fn pick<'a>(
        &'a self,
        request: &'a RoutingInfo,
        cluster: &'a ClusterState,
    ) -> Option<(NodeRef<'a>, Option<Shard>)>;

    fn fallback<'a>(
        &'a self,
        request: &'a RoutingInfo,
        cluster: &'a ClusterState,
    ) -> FallbackPlan<'a>;

    fn name(&self) -> String;
}

Key Methods

  • pick(): Returns the first (best) node to contact for a request
  • fallback(): Returns the remaining nodes to try if the first fails
  • on_request_success(): Called after each successful request
  • on_request_failure(): Called after each failed request

DefaultPolicy

The driver provides DefaultPolicy, a sophisticated policy with multiple features:

Features

  • Token awareness: Routes requests to replicas that own the data
  • Datacenter awareness: Prefers nodes in a specified datacenter
  • Rack awareness: Can prefer nodes in a specific rack within a datacenter
  • Datacenter failover: Optionally allows requests to remote datacenters
  • LWT optimization: Routes LWT queries deterministically to avoid Paxos conflicts

Basic Configuration

use scylla::policies::load_balancing::DefaultPolicy;

let policy = DefaultPolicy::builder()
    .prefer_datacenter("dc1".to_string())
    .token_aware(true)
    .permit_dc_failover(true)
    .build();

Datacenter Awareness

Prefer nodes in a specific datacenter:
let policy = DefaultPolicy::builder()
    .prefer_datacenter("us-east-1".to_string())
    .build();
When a preferred datacenter is set:
  • Nodes in that datacenter are treated as “local”
  • Other nodes are treated as “remote”
  • By default, remote nodes are excluded from plans

Rack Awareness

Prefer nodes in a specific rack within a datacenter:
let policy = DefaultPolicy::builder()
    .prefer_datacenter_and_rack(
        "us-east-1".to_string(),
        "rack-1".to_string()
    )
    .build();

Token Awareness

Token awareness routes requests to replicas that own the data:
let policy = DefaultPolicy::builder()
    .token_aware(true)  // Enabled by default
    .build();
Benefits:
  • Reduced latency by avoiding coordinator hops
  • Better load distribution
  • Improved throughput

Datacenter Failover

Allow requests to remote datacenters when local nodes are unavailable:
let policy = DefaultPolicy::builder()
    .prefer_datacenter("dc1".to_string())
    .permit_dc_failover(true)
    .build();

Latency Awareness

Latency awareness penalizes slow nodes (use with caution):
use scylla::policies::load_balancing::LatencyAwarenessBuilder;
use std::time::Duration;

let latency_awareness = LatencyAwarenessBuilder::new()
    .exclusion_threshold(2.0)  // Exclude nodes 2x slower than minimum
    .update_rate(Duration::from_secs(10))
    .retry_period(Duration::from_secs(60))
    .build();

let policy = DefaultPolicy::builder()
    .latency_awareness(latency_awareness)
    .build();
Warning: Latency awareness is NOT recommended for most use cases. It can interact poorly with other mechanisms like LWT optimization and may not improve performance.

Replica Shuffling

Control whether replicas are shuffled:
let policy = DefaultPolicy::builder()
    .enable_shuffling_replicas(false)  // Use consistent ordering
    .build();
Disabling shuffling can improve database-side cache effectiveness but may create hotspots.

Using with Execution Profiles

Attach a load balancing policy via execution profile:
use scylla::client::execution_profile::ExecutionProfile;
use std::sync::Arc;

let profile = ExecutionProfile::builder()
    .load_balancing_policy(Arc::new(policy))
    .build();

Custom Load Balancing Policy

Implement the LoadBalancingPolicy trait for custom behavior:
use scylla::policies::load_balancing::{
    LoadBalancingPolicy, RoutingInfo, FallbackPlan
};
use scylla::cluster::{ClusterState, NodeRef};
use scylla::routing::Shard;

#[derive(Debug)]
struct MyPolicy;

impl LoadBalancingPolicy for MyPolicy {
    fn pick<'a>(
        &'a self,
        query: &'a RoutingInfo,
        cluster: &'a ClusterState,
    ) -> Option<(NodeRef<'a>, Option<Shard>)> {
        // Your logic here
        todo!()
    }

    fn fallback<'a>(
        &'a self,
        query: &'a RoutingInfo,
        cluster: &'a ClusterState,
    ) -> FallbackPlan<'a> {
        // Your logic here
        todo!()
    }

    fn name(&self) -> String {
        "MyPolicy".to_string()
    }
}

Routing Information

The policy receives routing information about the statement:
pub struct RoutingInfo<'a> {
    pub consistency: Consistency,
    pub serial_consistency: Option<SerialConsistency>,
    pub token: Option<Token>,
    pub table: Option<&'a TableSpec<'a>>,
    pub is_confirmed_lwt: bool,
}

Best Practices

  • Enable token awareness for better performance
  • Set preferred datacenter to match your application’s location
  • Enable datacenter failover for better availability
  • Avoid latency awareness unless benchmarks prove it beneficial
  • Use rack awareness when deploying across availability zones

Next Steps

Build docs developers (and LLMs) love