Skip to content

QA-3: Keyset (cursor) пагинация #103

Description

@ii-reviewer

Цель: для больших списков заменить offset-пагинацию на keyset (cursor) — стабильный порядок и отсутствие деградации на глубоких страницах.
Зависимость: #4, #8.

Что сделать

  • Добавь keyset-вариант для GET /api/items и истории движений (GET /api/movements/{itemId}/history). Offset (page/size) для совместимости можно оставить — keyset включается параметром cursor.
  • Запрос вместо OFFSET: WHERE (sort_field, id) < (:lastSort, :lastId) ORDER BY sort_field, id LIMIT :size. Обязателен устойчивый tie-break по id — иначе строки с равным ключом сортировки «прыгают».
  • Курсор в ответе — непрозрачная строка (Base64 от lastSort+lastId), не сырой offset:
{ "content": [...], "nextCursor": "eyJpZCI6NDIs..." , "hasNext": true }
  • Трудные места: кодирование/декодирование и валидация курсора (битый курсор → 400, а не 500); согласованность поля сортировки в курсоре и в ORDER BY; что делать при смене направления/поля сортировки между запросами.

Acceptance criteria

  • GET /api/items?cursor=... отдаёт страницу + nextCursor
  • Проход по всем страницам через курсор не теряет и не дублирует записи
  • Порядок стабилен при равных значениях поля сортировки (tie-break по id)
  • Битый/чужой курсор → 400
  • Глубокая страница не делает большой OFFSET (проверить по SQL-логу)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestperformanceПроизводительность

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions