Common concepts

Before we review the available implementations, we need to introduce two concepts:

Host distance

For each host, the policy computes a distance that determines how the driver will establish connections
to it:

LOCAL and REMOTE are “active” distances, meaning that the driver will keep open connections to the host. They
differ in the number of connections opened, depending on your pooling options. Also, the
control connection will favor local nodes if possible.

IGNORED, as the name suggests, means that the driver will not attempt to connect.

Typically, the distance will reflect network topology (e.g. local vs. remote datacenter), although that is entirely up
to your policy. The distance can be dynamic: the driver re-checks it whenever connection pools are created (e.g. at
startup or if a node was down and just came back up); you can also trigger it with refreshConnectedHosts:

// Re-evaluate all host distances:cluster.getConfiguration().getPoolingOptions().refreshConnectedHosts();// Re-evaluate the distance for a given host:cluster.getConfiguration().getPoolingOptions().refreshConnectedHost(host);

Query plan

Each time the driver executes a query, it asks the policy to compute a query plan, which is a list of hosts. The
driver will then try each host in sequence, according to the retry policy and
speculative execution policy.

The contents and order of query plans are entirely up to your policy, but implementations typically return plans that:

are different for each query, in order to balance the load across the cluster;

only contain hosts that are known to be able to process queries, i.e. neither ignored nor down;

favor local hosts over remote ones.

The next sections describe the implementations that are provided with the driver.

This policy queries nodes of the local data-center in a round-robin fashion; optionally, it can also try a configurable
number of hosts in remote data centers if all local hosts failed.

Call withLocalDc to specify the name of your local datacenter. You can also leave it out, and the driver will use the
datacenter of the first contact point that was reached at initialization. However,
remember that the driver shuffles the initial list of contact points, so this assumes that all contact points are in the
local datacenter. In general, providing the datacenter name explicitly is a safer option.

Hosts belonging to the local datacenter are at distance LOCAL, and appear first in query plans (in a round-robin
fashion).

If you call withUsedHostsPerRemoteDc, the policy will pick that number of hosts for each remote DC, and add them at
the end of query plans. To illustrate this, let’s assume that the value is 2, there are 3 datacenters and 3 hosts in the
local datacenter. Query plans would look like this:

Hosts selected by this option are at distance REMOTE. Note that they always appear in the same order.

Finally, allowRemoteDCsForLocalConsistencyLevel controls whether remote hosts included by the previous option are
included when the consistency level of the query is LOCAL_ONE or LOCAL_QUORUM. By default, it is off (remote hosts
are not included for local CLs).

This policy adds token awareness on top of another policy: requests will be routed in priority to the local replicas
that own the data that is being queried.

Requirements

In order for token awareness to work, you should first ensure that metadata is enabled in the driver. That is the case
by default, unless it’s been explicitly disabled by QueryOptions#setMetadataEnabled.

Then you need to consider whether routing information (provided by Statement#getKeyspace and
Statement#getRoutingKey) can be computed automatically for your statements; if not, you may provide it yourself (if a
statement has no routing information, the query will still be executed, but token awareness will not work, so the driver
might not pick the best coordinator).

SimpleStatementstatement=newSimpleStatement("SELECT * FROM testKs.sensor_data WHERE id = 1 and year = 2016");// No routing info available:assertstatement.getKeyspace()==null;assertstatement.getRoutingKey()==null;// Set the keyspace manually:statement.setKeyspace("testKs");// Set the routing key manually: serialize each partition key component to its target CQL typeProtocolVersionprotocolVersion=cluster.getConfiguration().getProtocolOptions().getProtocolVersionEnum();statement.setRoutingKey(TypeCodec.cint().serialize(1,protocolVersion),TypeCodec.cint().serialize(2016,protocolVersion));session.execute(statement);

For built statements, the keyspace is available if it was provided while building the query; the
routing key is available only if the statement was built using the table metadata, and all components of the partition
key appear in the query:

TableMetadatatableMetadata=cluster.getMetadata().getKeyspace("testKs").getTable("sensor_data");// Built from metadata: all info availableBuiltStatementstatement1=select().from(tableMetadata).where(eq("id",1)).and(eq("year",2016));assertstatement1.getKeyspace()!=null;assertstatement1.getRoutingKey()!=null;// Built from keyspace and table name: only keyspace availableBuiltStatementstatement2=select().from("testKs","sensor").where(eq("id",1)).and(eq("year",2016));assertstatement2.getKeyspace()!=null;assertstatement2.getRoutingKey()==null;

For bound statements, the keyspace is always available; the routing key is only available if
all components of the partition key are bound as variables:

// All components bound: all info availablePreparedStatementpst1=session.prepare("SELECT * FROM testKs.sensor_data WHERE id = :id and year = :year");BoundStatementstatement1=pst1.bind(1,2016);assertstatement1.getKeyspace()!=null;assertstatement1.getRoutingKey()!=null;// 'id' hard-coded, only 'year' is bound: only keyspace availablePreparedStatementpst2=session.prepare("SELECT * FROM testKs.sensor_data WHERE id = 1 and year = :year");BoundStatementstatement2=pst2.bind(2016);assertstatement2.getKeyspace()!=null;assertstatement2.getRoutingKey()==null;

For batch statements, the routing information of each child statement is inspected; the first
non-null keyspace is used as the keyspace of the batch, and the first non-null routing key as its routing key (the idea
is that all childs should have the same routing information, since batches are supposed to operate on a single
partition). All children might have null information, in which case you need to provide the information manually as
shown previously.

Behavior

For any host, the distance returned by TokenAwarePolicy is always the same as its child policy.

When the policy computes a query plan, it will first inspect the statement’s routing information. If there is none, the
policy simply acts as a pass-through, and returns the query plan computed by its child policy.

If the statement has routing information, the policy uses it to determine the replicas that hold the corresponding data.
Then it returns a query plan containing:

the replicas for which the child policy returns distance LOCAL, shuffled in a random order;

followed by the query plan of the child policy, skipping any host that were already returned by the previous step.

Finally, the shuffleReplicas constructor parameter allows you to control whether the policy shuffles the replicas in
step 1:

newTokenAwarePolicy(anotherPolicy,false);// no shuffling

Shuffling will distribute writes better, and can alleviate hotspots caused by “fat” partitions. On the other hand,
setting it to false might increase the effectiveness of caching, since data will always be retrieved from the
“primary” replica. Shuffling is enabled by default.

This policy adds latency awareness on top of another policy: it collects the latencies of queries to each host, and
will exclude the worst-performing hosts from query plans.

The builder allow you to customize various aspects of the policy:

the exclusion threshold controls how much worse a host must perform (compared to the fastest
host) in order to be excluded. For example, 2 means that hosts that are twice slower will be excluded;

since a host’s performance can vary over time, its score is computed with a time-weighted average; the
scale controls how fast the weight given to older latencies decreases over time;

the retry period is the duration for which a slow host will be penalized;

the update rate defines how often the minimum average latency (i.e. the fastest host) is recomputed;

the minimum measurements threshold guarantees that we have enough measurements before we
start excluding a host. This prevents skewing the measurements during a node restart, where JVM warm-up will influence
latencies.

For any host, the distance returned by the policy is always the same as its child policy.

Query plans are based on the child policy’s, except that hosts that are currently excluded for being too slow are moved
to the end of the plan.

Filtering policies

WhiteListPolicy wraps another policy with a white list, to ensure that the driver will only ever connect to a
pre-defined subset of the cluster. The distance will be that of the child policy for hosts that are in the white list,
and IGNORED otherwise. Query plans are guaranteed to only contain white-listed hosts.

HostFilterPolicy is a generalization of that concept, where you provide the predicate that will determine if a host is
included or not.

Implementing your own

If none of the provided policies fit your use case, you can write your own. This is an advanced topic, so we recommend
studying the existing implementations first: RoundRobinPolicy is a good place to start, then you can look at more
complex ones like DCAwareRoundRobinPolicy.

DataStax, Titan, and TitanDB are registered trademark of DataStax, Inc.
and its subsidiaries in the United States and/or other countries.

Apache Cassandra, Apache, Tomcat, Lucene, Solr, Hadoop, Spark, TinkerPop,
and Cassandra are trademarks of the Apache
Software Foundation or its subsidiaries in Canada, the United States
and/or other countries