Пагинация пропускает записи, когда набор данных меняется между запросами или сортировка не имеет уникального порядка. При offset новые строки сдвигают границы страниц, а одинаковые даты позволяют базе возвращать элементы в разной последовательности.

Сначала зафиксируйте точный sort и фильтры двух соседних запросов. Добавьте уникальный вторичный ключ к сортировке и воспроизведите вставку или удаление между страницами.

Что сделать в первую очередь

  • Сохраните URL, параметры и id элементов двух соседних страниц.
  • Проверьте ORDER BY и наличие уникального tie-breaker.
  • Уточните, менялись ли записи во время обхода списка.
  • Сравните общее количество, если API его возвращает.

Почему возникает проблема

Симптом обычно появляется не из-за одной настройки. Сначала разделите путь данных на этапы и найдите место, где фактическое поведение расходится с ожидаемым.

  • Offset сдвигается после вставки или удаления строк.
  • Сортировка только по created_at не уникальна.
  • Клиент меняет фильтр, но продолжает старую страницу.
  • Кеш смешивает ответы для разных пользователей или параметров.

Пошаговая диагностика

  • Воспроизведите обход на неизменяемом снимке данных.
  • Добавьте ORDER BY created_at, id и сравните результат.
  • Вставьте тестовую запись между первым и вторым запросом.
  • Проверьте кодирование cursor и связь с текущими фильтрами.
  • Проверьте cache key на все параметры и область пользователя.

Как исправить

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

  • Используйте стабильную сортировку с уникальным последним ключом.
  • Для меняющихся больших списков перейдите на keyset или cursor pagination.
  • Включайте в cursor значения сортировки и проверяйте его подпись.
  • Сбрасывайте cursor при изменении фильтра или направления сортировки.
  • Для отчетов используйте snapshot или верхнюю границу выборки.

Как проверить результат

  • Полный обход возвращает каждый id один раз.
  • Вставка новой строки не скрывает ранее существующие элементы.
  • Невалидный или чужой cursor отклоняется понятной ошибкой.

Как не допустить повторения

  • Тестируйте пагинацию при параллельных insert, update и delete.
  • Документируйте гарантии порядка и структуру cursor.
  • Мониторьте дубли и пропуски в критических выгрузках.

Частые вопросы

Offset pagination всегда плохая?

Нет. Она подходит небольшим или стабильным наборам, но хуже переносит изменения между страницами.

Можно ли показывать номер страницы с cursor?

Можно отдельно считать навигацию, но точный случайный переход сложнее и зависит от требований интерфейса.

Когда стоит обратиться за помощью

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

Итог

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