Implementation of a hits in last second/minute/hour data structure
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
A “hits in the last second, minute, or hour” structure is a sliding-window counting problem. You record timestamped events and then answer questions like “how many hits happened in the last 1 second, 60 seconds, or 3600 seconds?” The right design depends on whether your time windows are fixed and bounded or arbitrary and unbounded.
A Practical Fixed-Window Design
If the largest window is one hour, a ring buffer with one bucket per second is usually the cleanest solution. You keep 3600 slots, each storing:
- the timestamp that currently owns the slot
- the number of hits in that second
When a new hit arrives, you map its timestamp to timestamp % 3600. If the slot already belongs to the same second, increment the count. If it belongs to an older second, reset the slot and replace its timestamp.
This design gives:
- '
O(1)insert time' - '
O(window)query time, where the window is at most 3600' - fixed
O(3600)memory
Because 3600 is a small constant, the query cost is often acceptable in practice.
Python Implementation
Here is a straightforward implementation:
This is a good interview and production baseline when timestamps are integer seconds.
Why the Ring Buffer Works
The modulo operation turns time into a circular array index. Old data eventually collides with new data, but that is safe because any data older than 3600 seconds is no longer relevant to the one-hour window.
The timestamp check prevents stale counts from being mistaken for current counts. That check is what makes overwriting safe.
Without storing the timestamp alongside the count, a slot reused an hour later would mix old and new events incorrectly.
When a Queue Is Better
If you need arbitrary windows rather than a known maximum, a deque of timestamps is often simpler. Insert every hit at the end, and on query, evict timestamps older than the relevant cutoff.
That version is elegant, but it only supports one active sliding window cleanly at a time unless you maintain more structure. It also stores one entry per hit, which can grow large under heavy traffic.
Optimizing for Frequent Queries
If queries are very frequent and must be strictly O(1), you can maintain pre-aggregated rolling totals for 1 second, 60 seconds, and 3600 seconds. That adds implementation complexity because each new second forces totals to roll forward correctly.
In many real systems, the ring-buffer design is a better tradeoff:
- simple to reason about
- bounded memory
- fast enough for fixed windows up to one hour
Engineering time matters too, not just asymptotic notation.
Handling Clock Semantics
These designs assume timestamps are monotonically increasing integer seconds. If events can arrive late or out of order, you need to define the semantics clearly.
Options include:
- reject out-of-order hits
- accept them only if still inside the buffer horizon
- use event time instead of wall-clock time with more complex bookkeeping
For interview-style problems, monotonic timestamps are usually assumed. For production analytics pipelines, you must make that assumption explicit.
Common Pitfalls
The biggest pitfall is forgetting to store the timestamp per slot in a ring buffer. Without that, reused indices corrupt the counts.
Another issue is using one queue for multiple independently queried windows without thinking about eviction behavior.
People also often say their query is O(1) because the window size is fixed. That is fair in practice for 3600 slots, but it is still conceptually a bounded linear scan.
Finally, decide whether “last minute” includes the current second and document that boundary clearly.
Summary
- A ring buffer with one bucket per second is a strong solution for fixed windows up to one hour.
- Store both timestamp and count in each slot so stale data can be detected safely.
- Inserts are
O(1)and queries are bounded by the largest configured window. - A deque works well for simpler or more flexible sliding-window problems.
- Make timestamp ordering and window-boundary semantics explicit before coding the data structure.

