Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Pagination Demystified

Pagination Demystified

В множестве клиентских приложений реализована постраничная загрузка данных. Эта, достаточно простая в теории тема, может во много раз усложниться в зависимости от специфики проекта и функциональности серверной части.

Egor Tolstoy

March 30, 2016
Tweet

More Decks by Egor Tolstoy

Other Decks in Technology

Transcript

  1. PAGINATION
    DEMYSTIFIED
    Egor Tolstoy @igrekde

    View Slide

  2. DISCLAIMER
    ВЫСТУПЛЕНИЕ ОСНОВАНО НА ЛИЧНОМ
    СТРАДАНИЯХ ОПЫТЕ.
    ДОКЛАД СОДЕРЖИТ ДЕТАЛЬНОЕ ОПИСАНИЕ
    ОТВРАТИТЕЛЬНЫХ КОСТЫЛЕЙ
    И ПОЭТОМУ НЕ РЕКОМЕНДУЕТСЯ К ПРОСМОТРУ
    НИКОМУ.

    View Slide

  3. View Slide

  4. View Slide

  5. Локальная фильтрация
    Динамический размер страницы
    Пересобирается раз в час
    Выдача лежит на CDN
    Все еще limit/offset
    authorId = postId >> 32

    View Slide

  6. Техники пагинации
    Как подступиться
    Загрузка вниз
    Обновление ленты

    View Slide

  7. limit/offset
    httр://api.соm/entries?offset=100&limit=20

    View Slide

  8. 0
    1
    2
    cache server
    0
    1
    2
    3
    4
    5
    6
    7

    View Slide

  9. 0
    1
    2
    cache server
    0
    1
    2
    3
    4
    5
    6
    7
    offset: 3
    limit: 5
    3
    4
    5
    6
    7

    View Slide

  10. 0
    1
    2
    cache server
    0
    1
    2
    3
    4
    5
    6
    7
    offset: 3
    limit: 5
    3
    4
    5
    6
    7
    3
    4
    5
    6
    7
    3
    4
    5
    6
    7

    View Slide

  11. Номера страниц
    httр://api.соm/entries?page_number=3

    View Slide

  12. cache server
    0
    1
    2
    0

    View Slide

  13. cache server
    0
    1
    2
    0
    page: 1
    1

    View Slide

  14. cache server
    0
    1
    2
    0
    page: 1
    1
    1
    2
    1

    View Slide

  15. Позиции элементов не меняются
    Условия

    View Slide

  16. Курсоры
    httр://api.соm/entries?since=752284800&limit=10
    httр://api.соm/entries? max_id=228&limit=10
    httр://api.соm/entries? after=MTAxNTE&limit=10

    View Slide

  17. cache server
    0
    1
    2
    21:04-
    21:02-
    19:35-
    19:18-
    17:44-
    16:02-
    15:58-
    15:49-
    15:40-
    3
    4
    5
    6
    7
    8
    0
    1
    2
    19:35-
    19:18-
    17:44-
    15:21- 9

    View Slide

  18. cache server
    0
    1
    2
    21:04-
    21:02-
    19:35-
    19:18-
    17:44-
    16:02-
    15:58-
    15:49-
    15:40-
    3
    4
    5
    6
    7
    8
    0
    1
    2
    19:35-
    19:18-
    17:44-
    since: 17:44
    limit: 5
    15:21- 9
    3
    4
    5
    6
    7
    16:02-
    15:58-
    15:49-
    15:40-
    15:21-

    View Slide

  19. cache server
    0
    1
    2
    21:04-
    21:02-
    19:35-
    19:18-
    17:44-
    16:02-
    15:58-
    15:49-
    15:40-
    3
    4
    5
    6
    7
    8
    0
    1
    2
    19:35-
    19:18-
    17:44-
    since: 17:44
    limit: 5
    15:21- 9
    3
    4
    5
    6
    7
    16:02-
    15:58-
    15:49-
    15:40-
    15:21-
    16:02-
    15:58-
    15:49-
    15:40-
    5
    6
    7
    8
    15:21- 9

    View Slide

  20. Уникальный параметр каждого элемента
    Сортировка выдачи не меняется
    Условия

    View Slide

  21. Изменение количества
    элементов
    Актуальность
    выдачи
    Обновление
    контента
    • Лента статична
    • Новые элементы
    добавляются
    сверху
    • Любая часть
    выдачи может быть
    изменена
    • Выдача всегда
    актуальна
    • Выдача может быть
    переформирована
    • Отображаемые
    данные не
    обновляются
    • Отображаемые
    данные могут быть
    изменены

    View Slide

  22. 1. Количество элементов не
    меняется
    2. Выдача всегда актуальна
    3. Данные могут быть
    изменены
    Афиша.Рестораны

    View Slide

  23. 1. Новые элементы
    добавляются только сверху
    2. Выдача всегда актуальна
    3. Данные могут быть
    изменены
    Афиша

    View Slide

  24. 1. Количество элементов не
    меняется
    2. Выдача может быть
    переформирована
    3. Данные не могут быть
    изменены
    Рамблер.Новости

    View Slide

  25. 1. Любая часть выдачи может
    быть изменена
    2. Выдача всегда актуальна
    3. Данные могут быть
    изменены
    Рамблер.Почта

    View Slide

  26. тем временем в офисе бэкенда

    View Slide

  27. Решения проблем
    пагинации на
    клиенте

    View Slide

  28. item 0
    item 1
    item 2
    item 3
    item 4
    item 5
    item 6
    item 7
    item 8
    item 9
    Загрузка вниз
    Случай 1:
    Лента статична
    1/5

    View Slide

  29. item 0
    item 1
    item 2
    item 3
    item 4
    item 5
    item 6
    item 7
    item 8
    item 9
    offset: 0
    limit: 5
    Загрузка вниз
    Случай 1:
    Лента статична
    2/5

    View Slide

  30. item 0 (cached)
    item 1 (cached)
    item 2 (cached)
    item 3 (cached)
    item 4 (cached)
    item 5
    item 6
    item 7
    item 8
    item 9
    offset: 0
    limit: 5
    Загрузка вниз
    Случай 1:
    Лента статична
    3/5

    View Slide

  31. item 0 (cached)
    item 1 (cached)
    item 2 (cached)
    item 3 (cached)
    item 4 (cached)
    item 5
    item 6
    item 7
    item 8
    item 9
    offset: 0
    limit: 5
    offset: 5
    limit: 5
    Загрузка вниз
    Случай 1:
    Лента статична
    4/5

    View Slide

  32. item 0 (cached)
    item 1 (cached)
    item 2 (cached)
    item 3 (cached)
    item 4 (cached)
    item 5 (cached)
    item 6 (cached)
    item 7 (cached)
    item 8 (cached)
    item 9 (cached)
    offset: 0
    limit: 5
    offset: 5
    limit: 5
    Загрузка вниз
    Случай 1:
    Лента статична
    5/5

    View Slide

  33. paging.startIndex = cachedPosts.count;
    paging.count = 5;

    View Slide

  34. item 0 (cached)
    item 1 (cached)
    item 2 (cached)
    item 3 (cached)
    item 4 (cached)
    item 5
    item 6
    item 7
    item 8
    item 9
    offset: 0
    limit: 5
    Загрузка вниз
    Случай 2:
    Добавились новые
    элементы
    1/5

    View Slide

  35. item 0 (cached)
    item 1 (cached)
    item 2 (cached)
    item 3 (cached)
    item 4 (cached)
    item 5
    item 6
    item 7
    item 8
    item 9
    offset: 0
    limit: 5
    item 0' (new)
    item 2' (new)
    Загрузка вниз
    Случай 2:
    Добавились новые
    элементы
    2/5

    View Slide

  36. item 0 (cached)
    item 1 (cached)
    item 2 (cached)
    item 3 (cached)
    item 4 (cached)
    item 5
    item 6
    item 7
    item 8
    item 9
    offset: 0
    limit: 5
    offset: 5
    limit: 5
    item 0' (new)
    item 2' (new)
    Загрузка вниз
    Случай 2:
    Добавились новые
    элементы
    3/5

    View Slide

  37. item 0 (cached)
    item 1 (cached)
    item 2 (cached)
    item 3 (cached)
    item 4 (cached)
    item 5 (cached)
    item 6 (cached)
    item 7 (cached)
    item 8
    item 9
    offset: 0
    limit: 5
    offset: 5
    limit: 5
    item 0' (new)
    item 2' (new)
    Загрузка вниз
    Случай 2:
    Добавились новые
    элементы
    4/5

    View Slide

  38. item 0 (cached)
    item 1 (cached)
    item 2 (cached)
    item 3 (cached)
    item 4 (cached)
    item 5 (cached)
    item 6 (cached)
    item 7 (cached)
    item 8
    item 9
    offset: 0
    limit: 5
    offset: 5
    limit: 5
    offset: 8 + 2
    item 0' (new)
    item 2' (new)
    Загрузка вниз
    Случай 2:
    Добавились новые
    элементы
    5/5

    View Slide

  39. paging.startIndex = cachedPosts.count
    + intersections;
    paging.count = 5;

    View Slide

  40. item 0 (cached)
    item 1 (cached)
    item 2 (cached)
    item 3 (cached)
    item 4 (cached)
    item 5
    item 6
    item 7
    item 8
    item 9
    offset: 0
    limit: 5
    Загрузка вниз
    Случай 3:
    Удалены старые
    элементы
    1/7

    View Slide

  41. item 0 (cached)
    item 1 (cached)
    item 2 (cached)
    item 3 (cached)
    item 4 (cached)
    item 5
    item 6
    item 7
    item 8
    item 9
    offset: 0
    limit: 5
    item 10
    item 11
    Загрузка вниз
    Случай 3:
    Удалены старые
    элементы
    2/7

    View Slide

  42. item 0 (cached)
    item 1 (cached)
    item 2 (cached)
    item 3 (cached)
    item 4 (cached)
    item 5
    item 6
    item 7
    item 8
    item 9
    offset: 0
    limit: 5
    item 10
    item 11
    offset: 5
    limit: 5
    Загрузка вниз
    Случай 3:
    Удалены старые
    элементы
    3/7

    View Slide

  43. item 0 (cached)
    item 1 (cached)
    item 2 (cached)
    item 3 (cached)
    item 4 (cached)
    item 5
    item 6
    item 7 (cached)
    item 8 (cached)
    item 9 (cached)
    offset: 0
    limit: 5
    item 10 (cached)
    item 11 (cached)
    offset: 5
    limit: 5
    Загрузка вниз
    Случай 3:
    Удалены старые
    элементы
    4/7

    View Slide

  44. item 0 (cached)
    item 1 (cached)
    item 2 (cached)
    item 3 (cached)
    item 4 (cached)
    item 5
    item 6
    item 7
    item 8
    item 9
    offset: 0
    limit: 5
    ...
    Загрузка вниз
    Случай 3:
    Удалены старые
    элементы
    5/7

    View Slide

  45. item 0 (cached)
    item 1 (cached)
    item 2 (cached)
    item 3 (cached)
    item 4 (cached)
    item 5
    item 6
    item 7
    item 8
    item 9
    offset: 0
    limit: 5
    ...
    offset: 4
    limit: 5
    Загрузка вниз
    Случай 3:
    Удалены старые
    элементы
    6/7

    View Slide

  46. item 0 (cached)
    item 1 (cached)
    item 2 (cached)
    item 3 (cached)
    item 4 (cached)
    item 5 (cached)
    item 6 (cached)
    item 7 (cached)
    item 8 (cached)
    item 9
    offset: 0
    limit: 5
    ...
    offset: 4
    limit: 5
    Загрузка вниз
    Случай 3:
    Удалены старые
    элементы
    7/7

    View Slide

  47. paging.startIndex = startIndex - 1;
    paging.count = 5;

    View Slide

  48. еще одна шутка про Facebook

    View Slide

  49. item 0
    item 1
    item 2
    item 3
    item 4
    item 5
    item 6
    item 7
    item 8
    item 9
    Загрузка вниз
    Случай 4:
    Порядок выдачи
    пересобирается
    1/5

    View Slide

  50. item 0
    item 1
    item 2
    item 3
    item 4
    item 5
    item 6
    item 7
    item 8
    item 9
    items:
    0,1,2,3,4
    Загрузка вниз
    Случай 4:
    Порядок выдачи
    пересобирается
    2/5

    View Slide

  51. item 0 (cached)
    item 1 (cached)
    item 2 (cached)
    item 3 (cached)
    item 4 (cached)
    item 5
    item 6
    item 7
    item 8
    item 9
    items:
    0,1,2,3,4
    Загрузка вниз
    Случай 4:
    Порядок выдачи
    пересобирается
    3/5

    View Slide

  52. item 6
    item 1 (cached)
    item 4 (cached)
    item 5
    item 8
    item 9
    item 2 (cached)
    item 0 (cached)
    item 7
    item 3 (cached)
    Загрузка вниз
    Случай 4:
    Порядок выдачи
    пересобирается
    4/5

    View Slide

  53. item 6 (cached)
    item 1 (cached)
    item 4 (cached)
    item 5 (cached)
    item 8 (cached)
    item 9 (cached)
    item 2 (cached)
    item 0 (cached)
    item 7 (cached)
    item 3 (cached)
    items:
    5,6,7,8,9
    Загрузка вниз
    Случай 4:
    Порядок выдачи
    пересобирается
    5/5

    View Slide

  54. NSRange pageRange =
    NSMakeRange(startIndex, 5);
    NSArray *postIds = [snapshot
    subarrayWithRange:pageRange];

    View Slide

  55. item 0 (new)
    item 1 (new)
    item 2 (new)
    item 3 (new)
    item 4 (new)
    item 5 (new)
    item 7 (cached)
    item 8 (cached)
    item 9 (cached)
    item 6 (new)
    Обновление
    Случай 1:
    Появилось много
    новых элементов
    1/3

    View Slide

  56. item 7 (cached)
    item 8 (cached)
    item 9 (cached)
    offset: 0
    limit: 5
    item 0 (new)
    item 1 (new)
    item 2 (new)
    item 3 (new)
    item 4 (new)
    item 5 (new)
    item 6 (new)
    Обновление
    Случай 1:
    Появилось много
    новых элементов
    2/3

    View Slide

  57. item 0 (cached)
    item 1 (cached)
    item 2 (cached)
    item 3 (cached)
    item 4 (cached)
    item 5 (new)
    item 7 (cached)
    item 8 (cached)
    item 9 (cached)
    offset: 0
    limit: 5
    item 6 (new)
    Обновление
    Случай 1:
    Появилось много
    новых элементов
    3/3

    View Slide

  58. if (intersections == 0) {
    [self dropCache];
    }

    View Slide

  59. item 0 (cached)
    item 1 (cached)
    item 3 (cached)
    item 4 (cached)
    item 6 (cached)
    item 7 (cached)
    item 8 (deleted)
    item 9 (cached)
    item 5 (new)
    item 2 (new)
    Обновление
    Случай 2:
    Свободно
    изменяемая лента
    1/1

    View Slide

  60. for (ShortPost *post in diff) {
    [self updateCacheWith:post];
    }

    View Slide

  61. for (ShortPost *post in snapshot) {
    if (![cachedPosts containsObject:post]) {
    [self downloadPost:post];
    }
    }
    for (Post *post in cachedPosts) {
    if (![snapshot containsObject:post]) {
    [self deletePost:post];
    }
    }

    View Slide

  62. item 6
    item 1 (cached)
    item 4 (cached)
    item 5
    item 8
    item 9
    item 2 (cached)
    item 0 (cached)
    item 7
    item 3 (cached)
    Обновление
    Случай 3:
    Порядок выдачи
    пересобирается
    1/1

    View Slide

  63. NSString *lastModified = [self makeFeedHeadRequest];
    if (![lastModified isEqual:cachedLastModified]) {
    [self dropCache];
    [self obtainPostSnapshot];
    [self obtainFirstPage];
    }

    View Slide

  64. item 0 (cached)
    item 1 (updated)
    item 2 (cached)
    item 3 (updated)
    item 4 (updated)
    item 5
    item 6
    item 7
    item 8
    item 9
    Обновление
    Случай 4:
    Данные элементов
    меняются
    1/1

    View Slide

  65. for (NSUInteger i = 0; i < cachedPosts.count; i++) {
    Post *cachedPost = cachedPosts[i];
    ShortPost *post = snapshot[i];
    if (![cachedPost isEqual:post]) {
    [cachedPost updatePostWithShortPost:post];
    }
    }

    View Slide

  66. posts: NSArray
    snapshot: NSArray
    offset: NSUInteger
    maxCount: NSUInteger
    lastModified: NSDate
    Feed

    View Slide

  67. PostService
    loadPosts
    didLoad
    Cache

    View Slide

  68. PostService
    loadPosts
    didLoad
    Cache

    View Slide

  69. CacheTracker
    PostService
    loadPosts
    didLoad
    Cache

    View Slide

  70. PagingFacade
    PostService
    ViewController
    FeedService CacheUpdater
    SnapshotProcessor
    PagingCalculator

    View Slide

  71. @protocol PostPagingFacade
    - (void)loadNextPageWithBlock:(ErrorBlock)block;
    - (void)refreshCategoryWithBlock:(ErrorBlock)block;
    - (void)reloadCategoryWithBlock:(ErrorBlock)block;
    @end

    View Slide

  72. Клиент должен быть
    простым

    View Slide

  73. Техники пагинации
    Как подступиться
    Загрузка вниз
    Обновление ленты

    View Slide

  74. Egor Tolstoy @igrekde
    не смог придумать
    заключительную фразу

    View Slide