Skip to content

Allow an explicit now in the ETS fix_window backend#187

Open
neilberkman wants to merge 1 commit into
ExHammer:masterfrom
neilberkman:feat/injectable-clock
Open

Allow an explicit now in the ETS fix_window backend#187
neilberkman wants to merge 1 commit into
ExHammer:masterfrom
neilberkman:feat/injectable-clock

Conversation

@neilberkman

Copy link
Copy Markdown

Motivation

The :fix_window ETS backend computes its window from Hammer.ETS.now/0 (System.system_time(:millisecond)), so there is no way for a caller to pin the current time. Two consequences:

  • Tests that exercise window boundaries have to :timer.sleep/1 across a real wall-clock boundary (see test/hammer/ets/fix_window_test.exs "returns expected tuples after waiting for the next window"), which is slow and can flake.
  • Downstreams that want a custom/monotonic clock have no seam.

What this does

Adds an optional trailing now argument (a timestamp in the backend's time unit) to Hammer.ETS.FixWindow.hit/inc/get, defaulting to ETS.now/0, and generates the matching arity in the use Hammer wrappers when the algorithm exports it:

# unchanged - uses the system clock
MyApp.RateLimit.hit("k", 1_000, 10)

# pinned - deterministic windows, no sleeping
now = 1_000_000
MyApp.RateLimit.hit("k", 1_000, 10, 1, now)
MyApp.RateLimit.hit("k", 1_000, 10, 1, now + 1_000)  # next window, fresh count
  • Backward compatible: now defaults to ETS.now/0, so every existing call site and the existing generated arities are untouched.
  • The generated wrapper arity is gated on function_exported?(@algorithm, :hit, 6) etc., so only algorithms that opt in get it.
  • New test demonstrates deterministic windows (and get/4) without sleeping.

mix test and mix format --check-formatted are green.

Open question

I scoped this to :fix_window (the default and most common) to keep the change small. Happy to extend the same optional-now seam to :fix_window_per_key / :sliding_window (and the Atomic backends) in this PR or a follow-up if you'd prefer it applied consistently across algorithms - let me know which you'd rather review.

The fix_window algorithm reads the current time from Hammer.ETS.now/0, so
a caller cannot pin the window. Tests that exercise window boundaries have
to sleep across a real wall-clock boundary, and downstreams that want a
custom clock have no seam.

Add an optional trailing now argument (timestamp in the backend's time
unit) to FixWindow.hit/inc/get, defaulting to ETS.now/0 so existing callers
are unaffected, and generate the matching arity in the use Hammer wrappers
when the algorithm supports it. Windows become deterministic without
sleeping.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant