Chapter 03 · Partitioners, Tokens, Vnodes, Token Rings, and Data Distribution
Partition Key Hashing and Murmur3Partitioner: From Key to Token
Follow one AtlasMart partition key from application value to Murmur3 token and replica placement, and learn why hashing cannot rescue a bad key design.
Learning outcomes
AtlasMart has three healthy Cassandra nodes, but “three nodes” says nothing about where a particular product partition lives. Cassandra first transforms the table's partition key into a token; only then can token ownership and the keyspace replication strategy determine the replicas. This lesson makes that transformation observable before the course talks about ring diagrams or scaling.
Explain the difference between a primary key, partition key, serialized partition-key value, token, token range, and physical node.
Explain why Murmur3Partitioner intentionally destroys lexical/monotonic ordering of ordinary partition-key values.
Use CQL token() to map deterministic AtlasMart keys to bigint tokens and compare repeatability.
Trace a token from the partitioner to natural replicas without treating the contacted node as the owner by definition.
Identify low-cardinality and constant partition keys as schema problems that hashing cannot repair.
Apache Cassandra 5.0.9 is the current GA 5.0 patch on the
official download page. The labs pin
cassandra:5.0.9 and explicitly set
CASSANDRA_NUM_TOKENS=16 so vnode behavior is
reproducible instead of inheriting an unnoticed
image/configuration default. Current Cassandra 5.0
documentation uses num_tokens: 16 as the modern
baseline; older Cassandra material often mentions 256 random
vnodes, so this chapter treats token count as a version- and
deployment-sensitive design choice rather than folklore.
The generation environment does not contain Docker or Cassandra, so Cassandra commands were checked against current official documentation but were not executed here. Exact token values, IP addresses, host IDs, ownership percentages, load, stream sizes, latency, and hot-partition samples must be captured on the learner's machine. Expected output is described as invariants or shapes, never presented as measured output.
1. Partition keys choose distribution; clustering keys do not
In Cassandra, a table's primary key can contain
two logical pieces. The
partition key identifies the partition and is
the input to the partitioner. Optional
clustering columns order rows
inside that partition. For products_by_id,
product_id is the whole primary key and therefore
the whole partition key. Later query-first tables may use a
composite partition key such as
(customer_id, month_bucket); Cassandra hashes the
serialized combination, not each component independently.
The default modern partitioner is
org.apache.cassandra.dht.Murmur3Partitioner. It
maps a partition-key value to a token represented by CQL as a
bigint. Hashing is deterministic: the same
correctly serialized key under the same partitioner maps to the
same token. It is also deliberately distribution-oriented:
nearby strings such as p-1001 and
p-1002 do not have to produce nearby tokens.
If every AtlasMart request uses the partition key
'global', Murmur3 hashes that one value to one
token. Adding servers creates more token owners elsewhere, but
the global partition still maps to the same token and its
replicas.
2. Observe the active partitioner before trusting a diagram
docker network create atlasmart-cassandradocker volume create atlasmart-cass-1-datadocker volume create atlasmart-cass-2-datadocker volume create atlasmart-cass-3-datadocker run -d --name atlasmart-cass-1 --hostname atlasmart-cass-1 --network atlasmart-cassandra -e CASSANDRA_CLUSTER_NAME=atlasmart-course -e CASSANDRA_DC=dc1 -e CASSANDRA_RACK=rack1 -e CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch -e CASSANDRA_NUM_TOKENS=16 -v atlasmart-cass-1-data:/var/lib/cassandra cassandra:5.0.9# Wait until node 1 accepts CQL before starting peers.docker run -d --name atlasmart-cass-2 --hostname atlasmart-cass-2 --network atlasmart-cassandra -e CASSANDRA_CLUSTER_NAME=atlasmart-course -e CASSANDRA_SEEDS=atlasmart-cass-1 -e CASSANDRA_DC=dc1 -e CASSANDRA_RACK=rack2 -e CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch -e CASSANDRA_NUM_TOKENS=16 -v atlasmart-cass-2-data:/var/lib/cassandra cassandra:5.0.9docker run -d --name atlasmart-cass-3 --hostname atlasmart-cass-3 --network atlasmart-cassandra -e CASSANDRA_CLUSTER_NAME=atlasmart-course -e CASSANDRA_SEEDS=atlasmart-cass-1 -e CASSANDRA_DC=dc1 -e CASSANDRA_RACK=rack3 -e CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch -e CASSANDRA_NUM_TOKENS=16 -v atlasmart-cass-3-data:/var/lib/cassandra cassandra:5.0.9# Wait for all nodes to become Up/Normal.docker exec atlasmart-cass-1 nodetool statusdocker exec atlasmart-cass-1 nodetool version
Query the server metadata and configuration rather than assuming the course baseline:
docker exec atlasmart-cass-1 cqlsh -e "SELECT cluster_name, partitioner, tokens FROM system.local;"docker exec atlasmart-cass-1 sh -lc "grep -E '^(partitioner:|num_tokens:|allocate_tokens_for_local_replication_factor:)' /etc/cassandra/cassandra.yaml"docker exec atlasmart-cass-1 nodetool status
Expected invariants: system.local.partitioner names
Murmur3Partitioner; the course container configuration shows 16
tokens; system.local.tokens contains multiple token
strings for node 1; and nodetool status reports
each physical node once while its Tokens column reflects the
vnode count. Exact token values are intentionally not
prescribed.
The tokens collection is local control-plane
metadata. It says which ring positions this node owns; it does
not by itself say which keyspace replicas contain a partition,
because replication strategy and rack/DC placement are
additional inputs.
3. Map AtlasMart partition keys to tokens with CQL
docker exec atlasmart-cass-1 cqlsh -e "CREATE KEYSPACE IF NOT EXISTS atlasmart_tokens WITH replication = {'class':'NetworkTopologyStrategy','dc1':3};"docker exec atlasmart-cass-1 cqlsh -e "CREATE TABLE IF NOT EXISTS atlasmart_tokens.products_by_id (product_id text PRIMARY KEY, category text, name text, price_cents int);"docker exec atlasmart-cass-1 cqlsh -e "INSERT INTO atlasmart_tokens.products_by_id (product_id,category,name,price_cents) VALUES ('p-1001','laptop','AtlasBook 14',129900); INSERT INTO atlasmart_tokens.products_by_id (product_id,category,name,price_cents) VALUES ('p-1002','camera','AtlasCam X',89900); INSERT INTO atlasmart_tokens.products_by_id (product_id,category,name,price_cents) VALUES ('p-1003','audio','AtlasPods',14900); INSERT INTO atlasmart_tokens.products_by_id (product_id,category,name,price_cents) VALUES ('p-2001','home','AtlasLamp',7900);"
CQL exposes the partitioner's mapping through
token(). Because functions are valid selectors, the
lab can display both the business key and the computed token:
docker exec atlasmart-cass-1 cqlsh -e "SELECT product_id, token(product_id) AS token_value, category FROM atlasmart_tokens.products_by_id;"# Compute the same token directly from a literal in a table-aware SELECT.docker exec atlasmart-cass-1 cqlsh -e "SELECT token(product_id) AS token_value FROM atlasmart_tokens.products_by_id WHERE product_id='p-1001';"
Record the token for each product. Re-run the SELECT from node
2. The token for p-1001 must not change just
because another coordinator executed the query. That
repeatability is the important evidence; whether its token is
positive or negative is not.
| Observation | What it proves | What it does not prove |
|---|---|---|
| Same key → same token | Deterministic partitioner mapping | Which coordinator the driver will choose |
| Different keys → apparently scattered tokens | Hash-based distribution intent | Perfect balance for a tiny sample |
| One token value | Ring position for that partition key | All physical replicas without keyspace replication metadata |
4. From token to replicas: partitioner plus replication strategy
For a write, the coordinator computes the token and consults
token metadata plus the keyspace's replication strategy. Under
NetworkTopologyStrategy with dc1:3,
the natural replica set contains three distinct physical nodes
while respecting rack placement where possible. The coordinator
may itself be a replica, but that is not required.
cqlsh in current Cassandra can expose the replica
set for a token with SHOW REPLICAS. Copy one token
value from the previous query and substitute it below:
# Replace TOKEN_VALUE with the bigint captured for p-1001.docker exec -it atlasmart-cass-1 cqlsh# In cqlsh:SHOW REPLICAS TOKEN_VALUE atlasmart_tokens;
If all three course nodes are replicas because RF=3 on a three-node DC, do not infer that every production keyspace behaves that way. Later Chapter 04 changes RF explicitly and derives placement math. The conceptual pipeline remains: partition-key bytes → partitioner → token → token range → replication strategy → natural replicas.
5. Wrong model: “monotonic IDs will distribute round-robin”
A practitioner familiar with sharded integer ranges might expect
1001, 1002, 1003 to move through nodes in order.
Murmur3 does not assign partitions round-robin and does not
preserve the original key's lexical ordering. That is usually
beneficial because regular business keys are spread over the
token space without requiring the application to know the node
count.
The more dangerous mistake is the opposite: believing the hash
function fixes a key with too few distinct values. A table
partitioned only by region with values
north, south, east, and
west has four partitions regardless of cluster
size. Under heavy traffic those four token positions can
dominate four replica sets. The repair is a query-first
partition-key design with sufficient cardinality and bounded
partitions—not a custom partitioner or more nodes.
Verification checklist
- You captured the active partitioner and vnode count from the running lab.
- You mapped at least four deterministic partition keys to tokens.
- The same key produced the same token through different coordinators.
- You resolved at least one token to its natural replicas.
- You can explain why low partition-key cardinality survives hashing as a hotspot risk.
Check your understanding
- What exact input does the partitioner receive conceptually?
- Does p-1002 have to hash next to p-1001?
- Does the token alone define all replicas?
- Can adding nodes fix one constant partition key?
- Why verify the running partitioner instead of assuming Murmur3?
Review the answers
1. The serialized partition-key value or composite partition-key components, not clustering columns or the full row.
2. No. Murmur3 is distribution-oriented and does not preserve lexical adjacency.
3. No. Token ownership plus the keyspace replication strategy and topology determine natural replicas.
4. No. One partition key still hashes to one token; its request concentration remains on that partition replica set.
5. Partitioner choice is cluster metadata/configuration and is version/deployment-sensitive; correctness depends on the actual cluster.
Cleanup
docker rm -f atlasmart-cass-4 atlasmart-cass-1 atlasmart-cass-2 atlasmart-cass-3 2>/dev/null || truedocker volume rm atlasmart-cass-4-data atlasmart-cass-1-data atlasmart-cass-2-data atlasmart-cass-3-data 2>/dev/null || truedocker network rm atlasmart-cassandra 2>/dev/null || true
docker rm -f atlasmart-cass-4 atlasmart-cass-1 atlasmart-cass-2 atlasmart-cass-3 2>$nulldocker volume rm atlasmart-cass-4-data atlasmart-cass-1-data atlasmart-cass-2-data atlasmart-cass-3-data 2>$nulldocker network rm atlasmart-cassandra 2>$null
Summary and next step
This lesson’s concepts, evidence path, failure boundaries, and production judgment should now be explicit enough to verify rather than assume. Re-run the check-your-understanding prompts and preserve any lab evidence you need before changing or cleaning up the environment.
Next, continue to Token Ranges, Ownership, Replicas, and Why the Ring Is a Useful Mental Model.
Authoritative references
- Apache Cassandra 5.0 documentation — Official documentation entry point for the current 5.0 line.
- Apache Cassandra downloads — Official release page used to verify Cassandra 5.0.9 as the current GA patch.
- Dynamo architecture: token ring, vnodes, replication — Official explanation of consistent hashing, token ranges, vnodes, natural replicas, and ring membership.
- cassandra.yaml configuration — Official partitioner, num_tokens, token-allocation, and related configuration reference.
- Production token recommendations — Current guidance for vnode counts and token-allocation tradeoffs.
- CQL token() function — Official semantics for mapping partition-key values to partitioner tokens.
- cqlsh SHOW REPLICAS — Official cqlsh command for resolving a token to replicas for a keyspace.
- nodetool ring — Official ring command and keyspace requirement for topology-aware ownership.
- Topology changes and bootstrap streaming — Official bootstrap/token-allocation/streaming and cleanup guidance.
- nodetool netstats — Official command for observing streaming/network activity.
- Java Driver 4.19 TokenMap — Driver-side token ranges, node tokens, and replica lookup API.
- Storage engine — Official evidence that SSTable partitions are ordered by partitioner token.