MySQL · 2798 bytes Raw Blame History
1 -- SPDX-License-Identifier: AGPL-3.0-or-later
2 --
3 -- Generic rate-limit counter queries (S35). Two write paths:
4 -- * BumpRateLimit — atomic UPSERT that rolls the window forward
5 -- when stale, increments hits otherwise. Returns
6 -- the post-update hits + window_started_at so
7 -- the caller can compute Retry-After without a
8 -- second round trip.
9 -- * BumpSignupIPThrottle — same shape against signup_ip_throttle
10 -- keyed by inet/CIDR.
11 --
12 -- Reads (PeekRateLimit, PeekSignupIPThrottle) are kept around for
13 -- the admin observability surface; the hot path uses Bump-and-decide.
14
15 -- name: BumpRateLimit :one
16 -- Roll-or-increment in one statement. The CASE in the UPDATE branch
17 -- handles the window roll: when the existing window started before
18 -- (now - $3 interval), we treat it as a new window and reset hits
19 -- to 1; otherwise we increment in place.
20 INSERT INTO rate_limits (scope, key, hits, window_started_at)
21 VALUES (sqlc.arg(scope), sqlc.arg(key), 1, now())
22 ON CONFLICT (scope, key)
23 DO UPDATE SET
24 hits = CASE
25 WHEN rate_limits.window_started_at < now() - sqlc.arg(ttl)::interval
26 THEN 1
27 ELSE rate_limits.hits + 1
28 END,
29 window_started_at = CASE
30 WHEN rate_limits.window_started_at < now() - sqlc.arg(ttl)::interval
31 THEN now()
32 ELSE rate_limits.window_started_at
33 END
34 RETURNING hits, window_started_at;
35
36 -- name: PeekRateLimit :one
37 SELECT scope, key, hits, window_started_at
38 FROM rate_limits
39 WHERE scope = $1 AND key = $2;
40
41 -- name: PruneRateLimits :execrows
42 DELETE FROM rate_limits
43 WHERE window_started_at < now() - sqlc.arg(retention)::interval;
44
45 -- name: BumpSignupIPThrottle :one
46 -- Same UPSERT shape against the inet-keyed signup throttle.
47 INSERT INTO signup_ip_throttle (cidr, hits, window_started_at)
48 VALUES (sqlc.arg(cidr), 1, now())
49 ON CONFLICT (cidr)
50 DO UPDATE SET
51 hits = CASE
52 WHEN signup_ip_throttle.window_started_at < now() - sqlc.arg(ttl)::interval
53 THEN 1
54 ELSE signup_ip_throttle.hits + 1
55 END,
56 window_started_at = CASE
57 WHEN signup_ip_throttle.window_started_at < now() - sqlc.arg(ttl)::interval
58 THEN now()
59 ELSE signup_ip_throttle.window_started_at
60 END
61 RETURNING hits, window_started_at;
62
63 -- name: PruneSignupIPThrottle :execrows
64 DELETE FROM signup_ip_throttle
65 WHERE window_started_at < now() - sqlc.arg(retention)::interval;
66