Пагинация пропускает записи, когда набор данных меняется между запросами или сортировка не имеет уникального порядка. При 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, сохранить совместимость клиентов и добавить тесты без пропусков.