Управление загрузкой с помощью PostgreSQL и pg_headerkit

ca0e73b2e7b34228b592404836523c50.png

Привет, Хабр!

Supabase — это крутой open-source аналог Firebase, с его помощью можно организовать крутые штуки вроде ограничения скорости запросов.

Supabase — это инструмент, который дает возможность создавать масштабируемые серверные решения, используя PostgreSQL. С его помощью можно легко управлять базами данных, аутентификацией, хранением данных и реальным временем, но без всяких vendor lock-ins.

Rate Limiting контролирует поток запросов, чтобы ваш сервер не ушел в нокаут от перегрузки. Это спасает сервера от DDOS-атакти помогает обеспечить более равномерное распределение ресурсов среди пользователей.

pg_headerkit

pg_headerkit — это расширение PostgreSQL, предназначенное для управления HTTP-заголовками прямо из вашей базы данных. Это расширение позволяет вам манипулировать HTTP-заголовками на уровне бдшки, что открывает широкие возможности для контроля и управления вашими API-запросами.

С pg_headerkit можно установить различные правила и логику для обработки входящих и исходящих HTTP-запросов. Например, вы можете настроить ограничение скорости на основе IP-адреса или API-ключа, а также управлять кэшированием или даже блокировать запросы, не соответствующие определенным критериям.

Для установки pg_headerkit потребуется иметь учетку supbase и установить dbdev на гитхабе.

Устанавливаем:

SELECT dbdev.install('burggraf-pg_headerkit');
CREATE EXTENSION "burggraf-pg_headerkit" VERSION '1.0.0';

Далее создадмим проект на официальном сайте Supabase.

Войдите в Supabase Dashboard. Откройте раздел SQL.

Запустим SQL-запрос для создания таблицы rate_limit:

CREATE TABLE rate_limit (
    api_key VARCHAR(255) NOT NULL,
    last_request_time TIMESTAMP NOT NULL DEFAULT now(),
    request_count INTEGER NOT NULL DEFAULT 1
);

Реализация ограничения скорости запросов

Входим в нашу БД постгреса и создаем расширение pg_headerkit:

psql -U username -d database

CREATE EXTENSION pg_headerkit;

Создадим таблицу, которая будет использоваться для контроля скорости запросов:

CREATE TABLE rate_limit (
    api_key VARCHAR(255) NOT NULL,
    last_request_time TIMESTAMP NOT NULL DEFAULT now(),
    request_count INTEGER NOT NULL DEFAULT 1
);

Напишем функцию, которая будет проверять и обновлять счетчик запросов:

CREATE OR REPLACE FUNCTION check_rate_limit() RETURNS TRIGGER AS $$
DECLARE
    time_difference INTERVAL;
BEGIN
    -- Проверяем время с последнего запроса
    SELECT now() - last_request_time INTO time_difference FROM rate_limit WHERE api_key = NEW.api_key;
    IF time_difference > INTERVAL '1 minute' THEN
        -- Сброс счетчика запросов после 1 минуты
        UPDATE rate_limit SET request_count = 1, last_request_time = now() WHERE api_key = NEW.api_key;
    ELSE
        -- Увеличиваем счетчик запросов
        UPDATE rate_limit SET request_count = request_count + 1 WHERE api_key = NEW.api_key;
        IF (SELECT request_count FROM rate_limit WHERE api_key = NEW.api_key) > 10 THEN
            -- Превышен лимит запросов
            RAISE EXCEPTION 'Превышен лимит запросов';
        END IF;
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Cоздадим триггер, который будет вызывать нашу функцию при каждом запросе:

CREATE TRIGGER rate_limit_trigger
BEFORE INSERT ON your_table
FOR EACH ROW EXECUTE FUNCTION check_rate_limit();

Теперь каждый раз, когда кто-то пытается сделать запрос, наш триггер проверяет, не превышен ли лимит запросов, и если да, то выкидывает исключение.

Другие возможности

Можно настроить более гибкие ограничения скорости, которые будут меняться в зависимости от времени суток. Например, установить более жесткие ограничения в часы пик и более мягкие в нерабочее время:

CREATE OR REPLACE FUNCTION dynamic_rate_limit() RETURNS TRIGGER AS $$
DECLARE
    current_hour INT;
BEGIN
    current_hour := EXTRACT(HOUR FROM now());

    IF current_hour BETWEEN 8 AND 18 THEN
        -- Жесткие ограничения в рабочее время
    ELSE
        -- Мягкие ограничения в нерабочее времни]
    END IF;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER dynamic_rate_limit_trigger
BEFORE INSERT ON your_table
FOR EACH ROW EXECUTE FUNCTION dynamic_rate_limit();

Можно создать более сложную систему лимитов, которая будет учитывать, к примеру, разные уровни доступа пользователей или их группы:

CREATE OR REPLACE FUNCTION user_specific_rate_limit() RETURNS TRIGGER AS $$
DECLARE
    user_role VARCHAR;
BEGIN
    SELECT role INTO user_role FROM users WHERE api_key = NEW.api_key;
    IF user_role = 'premium' THEN
        -- Высокие лимиты для премиум пользователей
    ELSE
        -- Стандартные лимиты для обычных пользователей
    END IF;
    -- [Остальная логика функции]
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Можно использовать pg_headerkit для добавления HTTP-заголовков в ответ, информирующих пользователя о текущем состоянии его лимитов:

CREATE OR REPLACE FUNCTION add_rate_limit_headers() RETURNS TRIGGER AS $$
DECLARE
    remaining_requests INTEGER;
BEGIN
    SELECT (10 - request_count) INTO remaining_requests FROM rate_limit WHERE api_key = NEW.api_key;

    PERFORM headerkit.add('X-RateLimit-Remaining', remaining_requests::TEXT);

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Если у вас есть список IP-адресов, с которых не должно быть доступа к вашему API, вы можете легко заблокировать их:

CREATE TABLE blocked_ips (
    ip_address VARCHAR(15)
);

INSERT INTO blocked_ips (ip_address) VALUES ('192.168.1.1');

CREATE OR REPLACE FUNCTION check_blocked_ips() RETURNS TRIGGER AS $$
BEGIN
    IF EXISTS (SELECT 1 FROM blocked_ips WHERE ip_address = NEW.ip_address) THEN
        RAISE EXCEPTION 'Доступ запрещен с вашего IP-адреса';
    END IF;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER ip_block_trigger
BEFORE INSERT ON your_requests_table
FOR EACH ROW EXECUTE FUNCTION check_blocked_ips();

pg_headerkit только улучшит производительность баз данных за счет более гибкого и точного управления загрузкой, и обеспечивает возможность более тонкой настройки процессов в соответствии с потребностями конкретной системы.

Больше практических инструментов и навыков эксперты отрасли показывают в рамках онлайн-курса PostgreSQL для администраторов баз данных и разработчиков. Присоединяйтесь.

© Habrahabr.ru