33 lines
2.1 KiB
SQL
33 lines
2.1 KiB
SQL
-- 0009_rate_limits — счётчики брутфорс-лимитов в БД вместо памяти.
|
||
--
|
||
-- Раньше лимитер был in-memory (internal/ratelimit): перезапуск API
|
||
-- обнулял все счётчики, а при нескольких инстансах каждый считал своё.
|
||
-- Таблица решает оба: строка переживает рестарт и общая для всех
|
||
-- инстансов, подключённых к базе.
|
||
--
|
||
-- Семантика — то же фиксированное окно, что и раньше: строка с ключом
|
||
-- живёт window_start..window_start+window, внутри — счётчик попыток;
|
||
-- инкремент и сброс окна делаются одним upsert'ом (см. ratelimit.DBLimiter).
|
||
--
|
||
-- Ключ — «имя лимита|IP» (login|1.2.3.4): четыре эндпоинта с разными
|
||
-- потолками живут в одной таблице, IP не обязан быть глобальным ключом.
|
||
--
|
||
-- Чистка протухших строк — фоновым тикером в cmd/api (RunCleanup):
|
||
-- все окна — минутные, поэтому строки старше 10 минут бесполезны.
|
||
-- Без чистки таблица росла бы со скоростью уникальных IP.
|
||
--
|
||
-- Применённый файл не редактировать (checksum в schema_migrations).
|
||
|
||
CREATE TABLE rate_limits (
|
||
-- «имя лимита|IP»; primary key — и есть точка апсерта
|
||
key text PRIMARY KEY,
|
||
-- начало текущего окна; старше окна — счётчик начинается заново
|
||
window_start timestamptz NOT NULL DEFAULT now(),
|
||
-- попыток в текущем окне
|
||
count integer NOT NULL DEFAULT 0,
|
||
CHECK (count >= 0)
|
||
);
|
||
|
||
-- Чистка по времени: DELETE по window_start идёт по индексу.
|
||
CREATE INDEX rate_limits_window_start_idx ON rate_limits (window_start);
|