Why architecture matters here
Cassandra scales by pushing work to owner replicas. BATCH breaks that model by asking one coordinator to accept a bundle of mutations and either atomically log them or fan them out. When misused, one node bottlenecks on behalf of many, latency inflates, and the batchlog table grows into a hot spot.
The architecture matters because the right BATCH is a scalpel: single-partition BATCH gives you atomicity and isolation within a partition; cross-partition logged BATCH gives you all-or-nothing guarantees at a real cost; unlogged BATCH is only a driver-side network optimization for keys co-located on the same coordinator.
Get the taxonomy right and you use BATCH where it fits, and use async concurrent writes where you actually need throughput.
The architecture: every piece explained
The top strip is the request path. Client BATCH stmt declares logged or unlogged. Coordinator receives the batch and, for logged, picks two batchlog nodes (avoiding itself) to receive a copy of the batch. Batchlog is a system table with the batch bundle and a TTL for cleanup. Replay on failure means if the coordinator crashes mid-batch, the batchlog nodes take over and apply the remaining mutations.
The middle row is the actual mutations. Row mutations are extracted from the batch and sent to the correct owner replicas. Owner replicas apply them to the commit log and memtable. Unlogged BATCH skips the batchlog entirely — no atomicity guarantee, just a bundle sent to the coordinator. Single-partition BATCH is a special case: all statements target the same partition; the coordinator applies them atomically and in isolation on the partition owner, with no batchlog needed.
The bottom rows are guidance. Anti-patterns: sending a large cross-partition unlogged BATCH thinking it is faster — it is slower and overloads the coordinator. Right pattern: async writes with a bounded concurrency semaphore in the driver — this is how you get bulk throughput. Ops metrics track coordinator CPU, batchlog table size, batch timeouts, and hints handed off.
End-to-end flow
End-to-end: an application needs to update user profile fields across three tables that share a partition key. It sends a single-partition logged BATCH; the coordinator routes to the partition owner; the owner applies the three mutations atomically and returns acknowledgment. Good use, expected cost. Now consider the same application inserting 5,000 rows across many partitions. If it wraps them in an unlogged BATCH, one coordinator receives the whole payload and fans out; latency spikes because that one node is doing 5,000 mutations of network coordination. The correct pattern is 5,000 async individual inserts with a semaphore of, say, 128 in flight; each goes to its owner directly; total time drops 10x. Metrics prove it — coordinator CPU flat, batchlog table stable, driver-side concurrency healthy.