accesspatterns.dev
← All models

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

sensor-readings
Try an example

Every reading held for one device, oldest to newest.

PK(pk)
SK(sk)
celsius
expiresAt
humidity
takenAt
DEVICE#greenhouse-1
TS#2026-07-05T00:00:00ZS
19.6N
1785801600N
72N
1783209600N
DEVICE#greenhouse-1
TS#2026-08-13T04:48:00ZS
23.1N
1789171200N
58N
1786596480N
DEVICE#greenhouse-1
TS#2026-08-13T10:48:00ZS
20.9N
1789171200N
67N
1786618080N
DEVICE#greenhouse-1
TS#2026-08-13T16:48:00ZS
21.8N
1789257600N
64N
1786639680N
DEVICE#greenhouse-1
TS#2026-08-13T22:48:00ZS
22.4N
1789257600N
61N
1786661280N
DEVICE#coldstore-2
TS#2026-08-13T15:36:00ZS
3.9N
1789257600N
46N
1786635360N
DEVICE#coldstore-2
TS#2026-08-13T21:36:00ZS
3.2N
1789257600N
44N
1786656960N

Run an operation to see the raw engine response.

table sensor-readingskeys PK / SKitems 7

The in-browser engine is a preview build. Transactions, vector search, streams, tags and TTL are among the operations it doesn't implement yet.