Time-Series at Mealtime: Why Our Sensor Data Lives in Cassandra
A bowl that never stops talking
Every EverBowl in a customer's kitchen is a small sensor station: a load cell weighing food and water, a temperature probe, a microphone and a camera, all reporting to the cloud around the clock. That stream lands in two tables in Amazon Keyspaces, AWS's managed Apache Cassandra service: devicedatatable at 528.3 GB and device_events at 25.3 GB. Our app data (users, pets, settings, alerts) lives in DynamoDB, so we run both databases side by side. This post explains why the device firehose went to Cassandra, and when we would pick each one.
What the data actually looks like
devicedatatable holds raw sensor readings: weight samples, temperature, thermal frames and audio metadata. Each row is (user_id, device, sensor, data, timestamp), partitioned by user_id and ordered by timestamp. device_events holds what we derive from those readings (consumption, refill, addition, bowl_added, bowl_removed, audio_class), partitioned by device_id and ordered by event_ts. Both are classic time-series data: rows are written once, belong to exactly one bowl, and nearly every read asks for one bowl's rows between time A and time B.
Why Cassandra fits this shape
Cassandra was built for exactly this access pattern. The partition key (device_id) sends all of one bowl's data to the same place, and the clustering key (event_ts) keeps it sorted on disk, so WHERE device_id = ? AND event_ts >= ? AND event_ts <= ? is a single sequential read rather than a search. Its write path is append-only: a write goes to a commit log and an in-memory table and returns, with sorting and compaction done later in the background, which makes sustained high-volume inserts cheap. CQL also looks like SQL, so analysts can query a bowl's day with SELECT ... ORDER BY timestamp, and Keyspaces gives us all of this without running Cassandra nodes ourselves.
Why not DynamoDB, and where DynamoDB wins
DynamoDB could store the same rows with device_id as the partition key and the timestamp as the sort key, so the choice is about fit, not possibility. For raw telemetry, Cassandra gives us a query language our engineers already read, typed columns, time-ordered clustering with ORDER BY and LIMIT, and an open-source engine we could run outside AWS if half a terabyte ever needs to move. DynamoDB also caps each item at 400 KB, while a Keyspaces row can reach 1 MB, which matters for thermal frames stored inline. DynamoDB wins everywhere else in our stack, though: single-item lookups by key, transactions across items, flexible secondary indexes, and DynamoDB Streams that trigger a Lambda on every change. That is why users, pets, alerts and baselines all live there.
When to use which
Our rule of thumb: if the data is append-only, grows without end and is read as "this thing, over this time window", reach for Cassandra; if it is a record you look up, update and react to, use DynamoDB. Cassandra is not free of sharp edges. Keyspaces throttles a partition under sustained load, so our readers retry on ReadTimeout, and any query that filters on a non-key column needs a materialized view or gets slow. Design the partition key around your one dominant query, and bucket by day if a single device's history could grow unbounded. Pick each database for the shape of its data, and running two of them stops being a burden and starts being the design.