Stateless-пагинация

Большинство систем пагинации хранят текущую страницу где-то на сервере. В Raito это не так: всё состояние живёт в кнопках. Эта страница объясняет, как именно и в чём компромиссы.

Состояние живёт в callback_data

Каждая кнопка навигации несёт callback_data, кодирующую всё состояние страницы:

rt_p:<mode>:<name>:<page>:<total>:<limit>

На сервере не хранится записи о том, «на какой странице пользователь X». Когда пользователь жмёт кнопку, Telegram присылает эту строку обратно, и Raito восстанавливает из неё всё нужное.

Круговорот

  1. Вы вызываете paginate() с именем, страницей и лимитом. Raito рисует страницу и кнопки навигации, чьи callback_data кодируют целевую страницу каждой.

  2. Пользователь жмёт «вперёд». Telegram доставляет колбэк-запрос с этим закодированным состоянием.

  3. Middleware распаковывает его, пересобирает нужный пагинатор для этого mode и передаёт в ваш хендлер paginator, page, limit и вычисленный offset = (page - 1) * limit.

  4. Ваш хендлер режет свои данные по offset/limit и вызывает paginator.answer(...), который редактирует сообщение и перерисовывает навигацию, замыкая круг.

Ваши данные Raito не хранит; вы каждый раз заново режете их оттуда, где они лежат (список, запрос к БД, API).

Чем это хорошо

  • Ни хранилища, ни срока жизни, ни очистки. Кнопки, отправленные на прошлой неделе, всё ещё работают; нет сессии, которую можно потерять.

  • Тривиально масштабируется. Любой процесс за ботом может обработать любой колбэк, ведь состояние путешествует вместе с запросом.

  • Простая модель в голове. Страница это чистая функция от (offset, limit) над вашими данными.

Компромисс: 64 байта

Telegram ограничивает callback_data 64 байтами, и туда должно уложиться всё: mode, name, page, total, limit. На практике это бьёт только при длинном name пагинации вместе с большими номерами страниц. Держите имена короткими. Raito проверяет входные данные, но кодировка это жёсткий потолок, поэтому очень длинное имя плюс крупные счётчики могут превысить лимит.

Режимы

Поле mode в колбэке выбирает пагинатор: inline (кнопки с контентом), text, list, photo или rich. Так как режим закодирован, middleware может пересобрать нужный тип пагинатора на каждое нажатие без всякого сохранённого контекста. API смотрите в руководстве по пагинации.