iot
Sensor Readings
Append-only device telemetry, read by time window and expired by TTL.
Time-series in its simplest DynamoDB shape: one partition per device, a timestamp as the sort key, and readings only ever appended. Old readings expire rather than being deleted, so the table stays the size of the window you care about.
The model
Reading
One measurement from one device at one moment.
- pk
- DEVICE#<deviceId>
- sk
- TS#<takenAtIso>
Attributes: celsius (N), humidity (N), takenAt (N), expiresAt (N)
Access patterns
- QueryRead a device's recent readings
Every reading held for one device, oldest to newest.
- ScanScan every device (for contrast)
The same readings without a partition key: every row in the table is read.
Design notes
One partition per device, not per fleetwhy
Keying by device rather than by, say, the day spreads writes across as many partitions as there are devices. A day-keyed partition would take every device's writes at once, which is the textbook hot partition: correct, and unusably slow at volume. The usual answer for one very chatty device is a composite bucket - DEVICE#<id>#<yyyy-mm> - which keeps the device split without collecting the whole fleet.
The window is a key condition, not a filtertrade-off
A chart wants a range - between two timestamps - and that belongs in the key condition so the readings outside it are never read. The preview resolves only the partition half, so the console returns everything held for the device. On a real device with a year of readings that difference is the whole cost of the page.
Old readings expire rather than being deletedtrade-off
Each reading carries an expiresAt, and TTL removes it once that passes. One seeded reading has already gone past its expiry and is still present, because the preview engine runs no sweep and real DynamoDB deletes on its own schedule - typically within a few days, with no guarantee. Deleting explicitly would mean paying a write to remove every reading you already paid a write to store.
Run the Scan and watch what it costswhy
The Query reads one device's partition and nothing else. The Scan beside it reads every row in the table to find the same readings, and the instrument marks the ones it read and threw away. Both return the readings you asked for; only one of them charges you for the other device's.
The sort key needs the time, not the datewhy
A device taking a reading every few hours collides on a date-only sort key four times over, and each write silently replaces the last - a primary key is unique, so the second reading is not a second row. Anything keyed by time needs enough resolution to separate two events, or an id after the timestamp to break the tie.
Append-only means no read before writewhy
A reading is a new item at a new sort key, so nothing is read, merged and put back. That is what makes the write path cheap and free of contention: two readings arriving at once cannot conflict, because at a resolution fine enough to separate them they are not the same item.
Taught in the course
- Read the latest first - A sorted timestamp makes 'the most recent' a direction, not a sort in your code.
Every reading held for one device, oldest to newest.
Run an operation to see the raw engine response.
The in-browser engine is a preview build. Transactions, vector search, streams, tags and TTL are among the operations it doesn't implement yet.