Cassandra Asks for ALLOW FILTERING on a Clustering Key? Here's Why
Why Cassandra demands ALLOW FILTERING (or says a clustering column cannot be restricted) even though the column is a clustering key: clustering columns must.
20+ years shipping high-throughput database systems. Lessons pulled from things that broke in production.
- ✓Basic Cassandra CQL
- ✓Partition and clustering keys
- Being a clustering key doesn't make a column independently searchable.
- Clustering columns are sorted in order; Cassandra can only seek using them left to right, after the full partition key.
- Skipping an earlier clustering column (or using a range on it) means filtering: Cassandra 3.6+ asks for ALLOW FILTERING; older versions say the column cannot be restricted as preceding column is not restricted.
- Fix: restrict the preceding columns, reorder the clustering key to match the query, or create a table designed for this query.
- ALLOW FILTERING inside one small partition is acceptable; without the partition key it scans the whole cluster.
A clustering key is like the sort order of a phone book: by surname, then first name. You can jump straight to "Smith, John". But asking for everyone called "John" without a surname means reading every page, even though first name is part of the sort order. Cassandra makes you say ALLOW FILTERING to admit you're reading every page.
You defined PRIMARY KEY ((tenant_id), created_at, event_id). event_id is a clustering key, so querying WHERE tenant_id = ? AND event_id = ? should be fast, right? Instead Cassandra replies: Cannot execute this query as it might involve data filtering and thus may have unpredictable performance. If you want to execute this query despite the performance unpredictability, use ALLOW FILTERING. On older clusters the message is different: PRIMARY KEY column "event_id" cannot be restricted as preceding column "created_at" is not restricted. Both mean the same thing, and the reason is how Cassandra stores rows on disk.
How Clustering Columns Are Stored
Inside a partition, Cassandra stores rows sorted by the clustering columns in the order you declared them. With PRIMARY KEY ((tenant_id), created_at, event_id), rows are sorted by created_at first, then by event_id within each created_at.
That sort order is what makes reads fast: Cassandra can seek to an exact created_at, then to an exact event_id within it. It can't seek to an event_id on its own, because event_ids are scattered across every created_at.
The Query That Triggers It
Restricting event_id without created_at skips the first clustering column. Cassandra would have to read the whole partition and filter rows, so it refuses unless you opt in.
The Rules for Clustering Columns
For a query Cassandra can serve without filtering:
- Restrict every partition key column with = (or IN).
- Restrict clustering columns from the left, without gaps.
- Use = on all of them except the last one you restrict, which may use a range (<, >, <=, >=).
A range on created_at followed by a restriction on event_id breaks rule 3, and so does any gap.
When ALLOW FILTERING Is Acceptable
ALLOW FILTERING doesn't make the query faster; it just lets it run. Its cost depends on how much data must be read:
- With the full partition key: Cassandra reads only that partition and filters it. Acceptable when partitions are small and bounded.
- Without the partition key: Cassandra scans data across the whole cluster. Avoid in application code; it degrades as data grows and can time out.
Fixes That Avoid Filtering
Pick based on how often the query runs:
- Supply the missing column, if the caller knows it.
- Reorder the clustering key so the column you query by comes first, if that's the main access pattern.
- Add a query table: Cassandra data modelling is query-first, so a second table keyed for this lookup is the standard answer. Write to both, usually in a logged batch.
- Use an index: Storage-Attached Indexing (SAI, in Cassandra 5.0 and DataStax Astra) or a secondary index can serve some lookups, best within a partition. Materialized views exist but are experimental and disabled by default since Cassandra 4.0.
The Lookup That Turned Into a Partition Scan
- Clustering columns only help left to right.
- ALLOW FILTERING cost grows with partition size: fine in tests, slow in production.
- Model one table per query pattern.
| File | Command / Code | Purpose |
|---|---|---|
| schema.cql | CREATE TABLE tenant_events ( | How Clustering Columns Are Stored |
| fails.cql | SELECT * FROM tenant_events | The Query That Triggers It |
| works.cql | SELECT * FROM tenant_events | The Rules for Clustering Columns |
| query_table.cql | CREATE TABLE tenant_events_by_id ( | Fixes That Avoid Filtering |
Key takeaways
Common mistakes to avoid
3 patternsAdding ALLOW FILTERING to silence the error
Assuming every primary key column is searchable on its own
Ranging on an early clustering column, then filtering a later one
Interview Questions on This Topic
Why can't Cassandra query a second clustering column directly?
Frequently Asked Questions
20+ years shipping high-throughput database systems. Lessons pulled from things that broke in production.
That's Cassandra. Mark it forged?
3 min read · try the examples if you haven't