Как ограничить кол-во запросов в секунду в Telegram боте?

Добрый день.

У меня есть Telegram бот, который выдает бонусы людям. Бонус выдается один раз в сутки. Для того, чтобы получить бонус, нужно нажать на кнопку (неважно, inline или обычную). И вот ушлые люди нажимают на кнопку десятки раз за несколько секунд и получают не один, а несколько бонусов в итоге.

Функция, которая обрабатывает нажатие, выглядит примерно так:
1. Проверить, когда начислялся бонус последний раз
2. Если больше дня назад, то начислить бонус и обновить дату получения


То есть всего 2-3 команды, ошибки в них быть не может, они банальны и я сто раз их проверил.

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

Бот написан на PHP. Я думал над записью в базу даты последнего обращения к боту пользователя, но я боюсь, что оно тоже не сработает, как и функция начисления бонуса.
А так как Telegram посылает все запросы со своего API, то ограничить их через настройку сервера не получится.
  • Вопрос задан
  • 2167 просмотров
Пригласить эксперта
Ответы на вопрос 7
@BorisKorobkov Куратор тега PHP
Web developer
Вам надо не "ограничить кол-во запросов", а изучить, что такое "race condition".
Ответ написан
Комментировать
@VadosL
Тоже замечал такую особенность поведения на ботах, написанных на Питоне. Но там, скорее всего, дело в том, что updater раскидывает обработку входящих апдейтов по потокам, и вот тут то и происходит race condition. Может Вам поискать в этом направлении?
Ответ написан
Комментировать
@artinnok
бекенд-программист
Ложите ID юзера в Redis и ставьте EX на сутки :)
При получении запроса на бонус проверяйте наличие ID юзера в Redis через EXISTS - если нету, значит не получал в течение суток. Если есть - значит получал в течение суток.
Ответ написан
Комментировать
@Mysterion
Банально можно в сессию добавлять параметр и проверять его наличие.
Перед обработкой нажатия его устанавливать и перед обращением к базе данных проверять.
Проверять, например, текущую дату, дату последнего обращения и вычислять разницу в нужном количестве времени.
Ответ написан
inoise
@inoise Куратор тега PHP
Solution Architect, AWS Certified, Serverless
Возьмите очередь, допустим, RabbitMQ или Kafka. Повесьте на другой конец обработчик отправки и пусть он перед отправкой проверяет сколько сообщений отправил за последнюю секунду. Если лимит исчерпал то кулдаун. Это, конечно, узкое горлышко получится, но для большинства задач должно хватить. Параллелить эту историю не очень надо ибо лимиты там не высокие, но если очень хочется то могу подсказать совсем дикие варианты
Ответ написан
@OlegPyatakov
pyatakov.com
Варианты:
  • Реализовать что-то вроде mutex для функции начисления бонуса
  • Сделать однопоточную очередь или воспользоваться внешней
  • Сделать задержку в начислениях: по нажатию кнопки делается заявка на начисление, а бонус начисляется потом всем скопом, кто нажимал кнопку
Ответ написан
Комментировать
Adamos
@Adamos
1. Проверить, когда начислялся бонус последний раз
2. Если больше дня назад, то начислить бонус и обновить дату получения

Если бонусы хранятся в базе, то нужно просто собрать эти два действия в один атомарный оператор обновления.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы