Комментарии 112
Но нам пишут и пишут, пишут и пишут, и пока пользователь читает пятую страницу, список сообщений меняется до неузнаваемости: добавляются новые и удаляются старые.
Т.е. пользователь, листая страницы, новые сообщения не увидит?
Хорошо сделано в некоторых случаях, когда результаты фиксированные, о есть уведомление о новом.
Ещё из подгорающего — когда кнопки работы с выдачей работают с выдачей актуальной на момент нажатия кнопки а не той что отображается.
Ситуации «Посмотрел уведомления, нажал очистить всё — не увидел свежее важное уведомление» встречаются довольно часто к сожалению :(
-2 -1 1 2 3 4…
Здравствуйте, меня зовут Дмитрий Карловский и я… не люблю читать книги, потому что пока перелистываешь страницу, ты вырываешься из увлекательного повествования. И стоит чуть замешкаться, как ты забываешь на чём оборвалось последнее предложение предыдущей страницы, и приходится листать обратно, чтобы перечитать его.
Таким образом мы получаем проблемы двух типов c точки зрения пользователя:
- На следующей станице могут вновь показаться сообщения, что уже были на предыдущей.
- Некоторые сообщения пользователь вообще не увидит, так как они успели переехать с 6 страницы на 5 ровно между переходом пользователя с 5 на 6.
В качестве решения этих проблем можно запомнить дату первоначального запроса (initial_request_date). При переходе от страницы к странице выборки передавать дату первоначального запроса.
SELECT FROM Message WHERE text LICENE "паджинация" AND created <= initial_request_date ORDER BY created DESC SKIP 5 * 10 LIMIT 10
Изначально проблема в том, что читаются большие объемы из таблички, в которую часто что-то пишется. Можно разбить источник данных на две неравномерных части: маленькая "недавняя", большая "история". Писать постоянно в маленькую и периодически переписывать все из маленькой в большую.
В качестве решения этих проблем можно запомнить дату первоначального запроса (initial_request_date).
Так вы защититесь лишь от появления новых сообщений, но не от удаления старых или перемещения существующих в выборке. Кроме того, появляться в выборке могут и старые сообщения вследствии их редактирования.
Изначально проблема в том, что читаются большие объемы из таблички, в которую часто что-то пишется.
Нет, проблема в том, что она вообще меняется. Высокая частота изменений лишь усугубляет ситуацию.
Что значит "перемещения существующих в выборке"? У них поле created меняется?
Удаленные сообщения из таблички можно не удалять, а помечать удаленными. Если настолько важна пажинация без сдвигов, то клиенту можно возвращать плашку "тут было сообщение, но его удалили" вместо текста.
Что значит "перемещения существующих в выборке"? У них поле created меняется?
Ну поменял я на changed
, теперь они будут скакать туда-сюда как угорелые. Попробуйте не к описанному примеру цепляться, а разглядеть мысль, которую он иллюстрирует.
Если настолько важна пажинация без сдвигов, то клиенту можно возвращать плашку "тут было сообщение, но его удалили" вместо текста.
Прилетело НЛО и поудаляло сообщения со словом "антипаттерн". После чего пришёл пользователь и читает 10 страниц сообщений "Здесь было НЛО".
Ну поменял я на changed, теперь они будут скакать туда-сюда как угорелые.
Зачем менять на changed, если можно просто добавить поле changed, а выводить все с той же сортировкой по created?
Нужно ли вообще отслеживать дату последнего изменения?
Прилетело НЛО и поудаляло сообщения со словом "антипаттерн". После чего пришёл пользователь и читает 10 страниц сообщений "Здесь было НЛО".
Зато сообщения не прыгают со страницы на страницу. Тут либо мы показываем следы НЛО, либо пользователь может иногда пропускать сообщения при переключении со страницы на страницу.
Теперь клиент может нарисовать хоть паджинатор, хоть виртуальный скролл,
Как, кстати, будет вести себя пажинатор по списку идентификаторов в вашем решении, если сообщения в середине будут удаляться?
Допустим выводим по 5 элементов на странице. Сценарий такой:
1) Получили список 1-20
2) Для первой страницы запросили 1,2,3,4,5
3) НЛО удалило сообщения 6, 11-15
4) Запросили 6,7,8,9,10 для второй страницы
5) Запросили 11,12,13,14,15 для третьей страницы
6) Запросили 16,17,18,19,20 для четвертой страницы
Есть еще вариант — показывать страницы "внахлест". Т.е. на первой сообщения с 1 по 10. На второй с 8 по 18. На третьей с 16 по 24.
Звучит интересно. Где можно почитать этот рассказ?
Схватки обрушивались на нее, будто горы, скала за скалою, и она словно звала нас из длинного, гулкого туннеля своих мук, и я сидел, скрестив ноги, разрываемый на части ее страданием, и беззвучное «тик-так» звучало в моем мозгу, а в хижине тройняшки поливали водой тело Парвати, чтобы оно не иссохло, ибо воды отходили потоками; разжимали ей зубы и вставляли палочку, чтобы несчастная не откусила себе язык; надавливали на веки, стараясь опустить их, потому что страшно было смотреть, как глаза Парвати вылезают из орбит – девушки боялись, что глазные яблоки выкатятся на пол и выпачкаются в грязи; и настал двенадцатый день, и я уже был ни жив ни мертв от голода, а где-то в городе, в другом месте, верховный суд уведомил госпожу Ганди, что она может не подавать в отставку, пока не будет рассмотрена ее апелляция, но при этом не должна голосовать в Лок Сабха и получать жалованье, и когда премьер-министр Индира, воодушевившись этой частичной победой, принялась честить своих противников в выражениях, каким позавидовали бы и рыбачки коли, роды моей Парвати достигли такой точки, когда, несмотря на крайнее изнеможение, она нашла в себе силы извергнуть из обескровленных уст целую литанию грязных, воняющих клоакой ругательств; смрад непристойной брани шибанул нам в ноздри, вывернул нас наизнанку; три акробатки стремглав вылетели из хижины, крича, что Парвати так высохла, так побледнела: еще немножко, и станет совсем прозрачная; и что она всенепременно умрет, если ребенок не выйдет тотчас же, прямо сейчас; а в ушах у меня звучало «тик-так», громко звучало «тик-так», и я наконец убедился – да, скоро, скоро-скоро-скоро, и когда тройняшки вернулись к ее постели вечером тринадцатого дня, они завопили – да, да, она стала тужиться; ну давай, Парвати, тужься-тужься-тужься, и пока Парвати тужилась в своей лачуге, Дж.П. Нараян и Морарджи Десаи тоже подстрекали Индиру Ганди; пока тройняшки визжали – тужься-тужься-тужься – лидеры «Джанаты Морчи» призывали полицию и армию не подчиняться приказам ограниченного в правах премьер-министра и в каком-то смысле заставляли госпожу Ганди тужиться тоже, и и когда тьма сгустилась к полуночному часу, ибо разве может что-то случиться в какой-то другой час, тройняшки заверещали – он идет-идет-идет – а где-то там, далеко, премьер-министр Индира рожала свое дитя… в трущобах, в хижине, подле которой я сидел, полуживой от голода, мой сын шел-шел-шел – вот уже показалась головка – заверещали тройняшки, а в это время отряды особой резервной полиции арестовывали лидеров «Джанаты Морчи», включая таких невозможно древних, почти мифологических персонажей, как Морарджи Десаи и Дж.П. Нараян; тужься-тужъся-тужься – и в самом сердце этой ужасной полуночи, когда «тик-так» гремело у меня в ушах, родился ребенок, в самом деле первый сорт, настоящий богатырь, выскочил в конце концов так легко, что невозможно было понять, из-за чего разгорелся весь сыр-бор.
Да, это неидеально, да, есть минусы. Но альтернативы, как правило, ещё хуже и непонятнее (кроме частных случаев).
А что насчёт серверных курсоров? Тот же elastic умеет присваивать Id каждому запросу и потом работать в рамках его изначальных результатов.
Уважаемый автор, плохо что ты
Вы либо уважайте, либо тыкайте.
читать книг
читать книги
не в одном
ни в одном
ты не сможешь найти слова: «паджинация», «снепшот»
Однако все меня поняли, вот парадокс.
Реализация постраничной навигации напрямую зависит от структуры базы данных и самого движка БД.
Например?
Не читать книг, не попить воды, не надеть обуви… не включать мозгов.
Или единственно правильно будет: не попить воду, не надеть обувь, не включать мозг?
Идеально было бы:
1) Один раз произвести поиск и где-то запомнить его результаты в виде снепшота на определённый момент времени.
2) Быстро выбирать данные мелкими порциями по мере необходимости.
На сколько я понял, то автор «изобрел» кеширование результатов выборки?
но проблема в том, что пример гугла в очень редких случаях иллюстрирует описанную автором задачу, хабр лучше показывает случай с пагинацией: переходишь на страницу i+1, и первой статьей отображается последняя статья с i-й страницы, в случае, когда появилась 1 новая статься в момент перехода на страницу i+1…
для меня это вообще не проблема, но если пользователей моего ресурса это бы напрягало, более того, было бы ясно о возможности подобного случая, одно из странных решений: фиксировать дату начала постраничной навигации конкретного пользователя, и игнорировать появление новых записей (статей), но решение технически трудоемкого и громоздкое, хоть и возможное… другой вариант — опять привязываясь к конкретному пользователю, при явном переходе на 2-ю страницу — фиксировать первый ИД записи на 2й страницы, и отталкиваться от него при выводе данных на последующих страницах, что по своему объему не меньше первого варианта…
и третий вариант, который приходит на ум, это то что использовал ВК (не знаю как сейчас, полгода не заходил), и вроде как фейсбук в свое время (опять же очень давно там не был): подгружать следующие N-записей относительно последней, так мы точно загрузим заранее фиксированное количество записей, и можем в самом начале добавлять новые, по мере их появления… но такой вариант имеет проблемы: пока не просмотришь все записи, к подвалу сайта не добраться, и когда записей сотни-тысячи — просмотр страницы превращается в кошмар…
Вообще, проблемы, описаны автором, возможны на ресурсе с приличной активностью, где новые материалы появляются по несколько штук в каждую секунду… мне, например, такие ресурсы неизвестны, если у кого есть конкретный пример — пожалуйста напишите, интересно посмотреть как у них интерфейс устроен… а коль и другие таких мест не знают, тем более автор: можно будет считать — проблемы высосана из пальца
единственный скриншот — пример пагинации гугла…
Там не только Гугл, присмотритесь.
фиксировать дату начала постраничной навигации конкретного пользователя, и игнорировать появление новых записей (статей)
Вы таким образом лишь частично решили (отсекаются только новые сообщения, но не изменения старых) одну из проблем паджинации (изменение выборки в процессе листания) в довольно частном случае (сортировка по дате создания).
где новые материалы появляются по несколько штук в каждую секунду…
Появление материала в системе и появление его в конкретной выборке — две большие разницы. Например, на Хабре статья появляется в одно время, в списке всех статей в совершенно другое, а на главной в третее, если её не заминусуют, конечно. И порядок статей в этих трёх списках совершенно никак друг с другом не синхронизирован.
Решение вашей проблемы можно осуществить всего лишь одним SQL запросом:
SELECT * FROM <b>наша_таблица</b> LIMIT <b>x</b>, n
где SQL-запрос вернёт записи, начиная с x-го номера включительно в количестве n штук.
Объясняю.
Допустим пагинация будет передаваться в php и на каждом элементе пагинации вешается ссылка типа "/page=n", где n означает номер следующей страницы и обрабатываете в php скрипте так:
$number_of_showing_items = 10;
$page = 0;
if(isset($get['page']))
$page = $get['page'] * number_of_showing_items;
$rows = $db -> select('SELECT * FROM `our_table` LIMIT '.$page.', '.$number_of_showing_items);
for($i = 0; $i <= sizeof($rows) - 1; $i++)
{
echo $rows[$i]['post_author'];
echo $rows[$i]['post_header'];
echo $rows[$i]['post_content'];
}
Если надо добавить какие либо условия, то передаем в запросе эти условия и в итоге SQL запрос может выглядеть вот так:
$rows = $db -> select('SELECT * FROM `our_table` WHERE наш_столбец="наш_текст" LIMIT '.$page.', '.$number_of_showing_items);
Это самый простой и эффективный на мой взгляд метод решения проблемы. А то что вы написали в статье, извините меня, полный бред.
— Обратный порядок странцы. Пользователь смотрит не с первой, а с наибольшей по номеру страницы. На первой странице самые «старые» данные. Проблема: на наибольшая по номеру страница может быть заполнена не полностью
— Относительная паджинация. Если у данных есть уникальный монотонно возрастающий ключ (а id, как правило, такой и есть), то можно передавать не номер страницы, а последний просмотренный id, от которого «листаем» дальше.
Проблема: на наибольшая по номеру страница может быть заполнена не полностьюИли переполненная, если страницу с недостаточным количеством записей объеденить с предыдущей.
Есть два других решения проблемы того что данные перескакивают со страницы на страницу
Я описал несколько проблем паджинации, почему вы пытаетесь решать только одну?
Пользователь смотрит не с первой, а с наибольшей по номеру страницы.
Это не решение. Это костыль для одного очень частного случая: когда поле, по которому идёт сортировка никогда не меняется, записи никогда не удаляются, а новые элементы в выборке появляются исключительно с одного конца.
Если у данных есть уникальный монотонно возрастающий ключ (а id, как правило, такой и есть), то можно передавать не номер страницы, а последний просмотренный id
Тот же костыль, но ещё и сортировка исключительно по id.
Я описал несколько проблем паджинации, почему вы пытаетесь решать только одну?
Потому что мне так хочется. По-вашему я должен решать все?
Оба, предложенных мной решения, не костыли, а именно решения со своими плюсами и минусами, о чем я, собственно, и написал.
Если не рассматривать «сферическую паджинацию в ваккуме», то создание снепшотов умеет далеко не каждая БД, а для тех, кто умеет, это довольно дорогая операция.
Даже постраничная паджинация имеет проблемы с производительностью по сравнению с относительной паджинацией. Потому что на больших данных OFFSET та еще жесть для БД. Зпрос для относительной паджинации почти вчегда будет быстрее запроса постраничной.
Так что, еще раз скажу, надо выбирать меньшее из зол исходя из конкретной задачи.
По-вашему я должен решать все?
Реализация без паджинации, но с разделением поиска и выборки решает их все. Было бы не плохо предлагать альтернативы, которые как минимум не хуже.
Оба, предложенных мной решения, не костыли, а именно решения
Если решаемая проблема остаётся, то это не решение.
Если не рассматривать «сферическую паджинацию в ваккуме», то создание снепшотов умеет далеко не каждая БД, а для тех, кто умеет, это довольно дорогая операция.
Снепшот — это просто список идентификаторов. В статье предлагается его сразу выгружать клиенту. Никакая поддержка со стороны СУБД тут не нужна.
Даже постраничная паджинация имеет проблемы с производительностью по сравнению с относительной паджинацией.
Об этом написано в статье.
Зпрос для относительной паджинации почти вчегда будет быстрее запроса постраничной.
Она применима только при сортировке по уникальному иммутабельному полю. Обычно это только id строки.
Реализация без паджинации, но с разделением поиска и выборки решает их все. Было бы не плохо предлагать альтернативы, которые как минимум не хуже.
Serious? Ваше решение решает только проблему дублей на странице, но оно не решает остальные поставленные вами проблемы, о чем вам уже не однократно написали в комментариях.
Относительная паджинация, предложенная maximw, решает как проблему дублей, так и проблему удаленных записей.
оно не решает остальные поставленные вами проблемы
Какие такие проблемы оно не решает?
Это такой тонкий троллинг?
Вам же уже написали:
- Метод не будет работать если пользователю важны последние записи.
- При удалении записей которые есть в снепшоте вы получите НЛО или дырки. Если удаленных материалов много, то вы можете получить вообще
дырку от бубликапустую страницу. - Снепшот, это по сути кэширование, со всеми вытекающими последствиями.
- Вы ограничиваете результат до 1000 записей, но это не всегда корректно. Это корректно для поисковой выдаче, но не корректно для каталогов и архивов. Вспоминаем Бездну.
А вы принципиально не читаете мои ответы на те комментарии, ссылки на которые приводите? Что ж, я повторю:
Метод не будет работать если пользователю важны последние записи.
Когда пользователь перемещается между страницами его явно интересуют не последние записи, а "дай мне следующие 20 элементов". Самому пользователю ваша паджинация даром не нужна. Она — лишь техническая деталь, позволяющая показать пользователю большой список за разумное время. И пользователя интересует именно этот список, а не какая-то страница из него.
При удалении записей которые есть в снепшоте вы получите НЛО или дырки.
Я не вижу проблемы в том, что мы честно пишем, что "на момент запроса тут было сообщение, но к тому моменту как вы до него добрались оно уже было удалено, поэтому мы не можем показать вам его содержимое". И это гораздо лучше, чем "мы можем случайно не показать вам какие-то сообщение, а некоторые показать по нескольку раз, но мы вам об этом не сообщим" и неприятных скачков виртуального скролла из-за изменения состава выдачи.
Снепшот, это по сути кэширование, со всеми вытекающими последствиями.
Ошибка того же порядка, что и "человек произошёл от шимпанзе". У человека и шимпанзе общий предок, но частным случаем друг друга никто из них не является. Так вот, "снепшот" и "кеш" — это частные случаи "производных данных". Снепшот создаётся клиентом целенаправленно, чтобы с ним потом работать независимо от изменений в исходных данных. Вы можете создать сколько угодно снепшотов для одного и того же поискового запроса. Кеш же работает прозрачно между клиентом и исходными данными, не создавая отдельной сущности с которой можно оперировать. В то время как снепшот является иммутабельным со всеми вытекающими. С кешом у вас есть вечная проблема по его инвалидации. Пока он не инвалидировался вы получаете устаревшие данные. А как только инвалидировался — вы получаете изменение выборки со всеми описанными в статье проблемами. Так что нет, по сути это два совершенно различных механизма.
Однако, справедливости ради, стоит отметить, что механизм кеширования можно приспособить, чтобы он создавал снепшоты. Для этого клиенту при первом запросе нужно сформировать query_id
и передавать его вместе с запросом к каждой странице. А серверу формировать ключ для кеша на основе: query_id, id сессии и параметров запроса. При этом нужно задать заведомо большое время жизни кеша, например, в неделю.
Вы ограничиваете результат до 1000 записей, но это не всегда корректно.
Не корректно выдавать ленту новостей в виде отсортированного списка. Я не стал это описывать в данной статье, так как это ортогональная проблема никак не связанная с паджинацией.
А ограничение на число элементов есть всегда. Я уже приводил в пример Гугл с его паджинатором. Да что там далеко ходить: https://habr.com/all/page101/
Когда пользователь перемещается между страницами его явно интересуют не последние записи
И вернувшись на первую страницу он не увидит новых записей.
на момент запроса тут было сообщение, но к тому моменту как вы до него добрались оно уже было удалено, поэтому мы не можем показать вам его содержимое
Вот вы не видите проблемы в НЛО, но видите проблему в дублях. Другие же разработчики и пользователи не видят проблемы в дублях, а вот НЛО раздражает.
Снепшот создаётся клиентом целенаправленно, чтобы с ним потом работать независимо от изменений в исходных данных.
Из вашей статьи я делаю вывод, что вы запрашиваете с сервера список id записей соответствующих поисковому запросу и кешируете результат на клиенте. Далее, реализуете пагинацию на клиенте по списку закешированных записей.
Вы список закешированных записей называете снепшотом, но механика явно кеширующая.
Так что, нет. В вашем случае кеш и снепшот это синонимы.
А ограничение на число элементов есть всегда
И снова нет. Ограничений почти никогда нет. Вам уже привели 2 примера и можно привести ещё миллион.
Да, некоторые искусственно ограничивают число элементом в силу своих каких-то технических ограничений и Google Search тому яркий пример. Выдавать в поисковой выдаче результаты до которых пользователей не долистает для них скорей всего экономически не выгодно. А вот для каталогов (1, 2, 3) это не корректно. Тоже с архивом новостей. А обрезание комментариев на форуме или сообщений в личке или списка конкурсантов в онлайн конкурсе или ленты в соцсетях это вообще нарушение целостности данных и пользователи вас за это по головке не погладят.
Подитожу. Как я, так и другие комментаторы я полагаю, критикуем не сам ваш метод, давно известный, но не слишком популярный. Мы критикуем ваше отношение к нему. Вы преподносить классическую пагинацию как антипаттерн, а ваш метод как серебряную пулю и универсальное решение хотя это не так.
Но это конечно ваше право иметь собственное мнение и не принимать истину.
И вернувшись на первую страницу он не увидит новых записей.
А с чего бы ему видеть там не те записи, что были там буквально пару минут назад? "перейти на первую страницу выдачи" и "повторить поиск" — это две различные операции. Чтобы лучше понять разницу — представьте себе виртуальный скролл. Должен ли поиск повторяться только лишь потому, что пользователь мотнул на начало?
Другие же разработчики и пользователи не видят проблемы в дублях,
Ну конечно, пользователь перешёл с 5 на 6 страницу, а мы ему показываем ровно те же самые данные, только лишь потому, что между переходами данных в начало выборки добавилось аж на целую страницу. Совсем никакой проблемы, да.
а вот НЛО раздражает.
Ну конечно, недетерминированное дублирование и пропадание записей не раздражает, а детерминированное и понятное отображение состояния записи — раздражает. Вы либо крестик снимите, либо трусы наденьте.
Из вашей статьи я делаю вывод, что вы запрашиваете с сервера список id записей соответствующих поисковому запросу и кешируете результат на клиенте.
Не надо "делать выводы", если там чётко написано, что выгружается список идентификаторов, который сохраняется на клиенте. Вы вот зачем со мной спорите? Я вам вполне конкретно описал чем снепшот отличается от кеша и почему некорректно называть кешом любые производные данные. Можете сколько угодно делать вид, что разницы нет. Но вы правда верите, что и я перестану её видеть только лишь от того, что вы 10 раз назовёте снепшот кешом?
Ограничений почти никогда нет.
Приведите ссылку на исследование.
Выдавать в поисковой выдаче результаты до которых пользователей не долистает для них скорей всего экономически не выгодно.
А для кого выгодно выдавать результаты, которых пользователь не увидит?
А вот для каталогов (1, 2, 3) это не корректно.
Давайте пройдёмся по ссылкам:
Амазон. Максимальная страница — 400, что даёт не более 5000 элементов в выдаче. Список идентификаторов весил бы 64КБ максимум. Вроде бы много, пока не узнаешь, что один только html каждой страницы Амазона весит более 500КБ.
Кинопоиск. Тут похоже действительно нет ограничения. Однако, вы можете представить себе пользователя, который бы прощёлкал 1148 страниц? На всякий случай — возможность перейти на последнюю страницу не является обоснованием, так как гораздо лучше это решается инвертированием сортировки. Ну и посмотрите сколько стоит переход между страницами. Формирование каждой страницы — полторы секунды. Навигация по снепшоту была бы почти мгновенной. Какой мог бы получиться размер снепшота? Ну, не более 360КБ. Много. Но не так много как текущий html каждой страницы в 400КБ.
Тоже с архивом новостей.
Там просто фильтрация по дате и всё.
А обрезание комментариев на форуме или сообщений в личке или списка конкурсантов в онлайн конкурсе или ленты в соцсетях это вообще нарушение целостности данных и пользователи вас за это по головке не погладят.
Во всех этих случаях пользователю нужен поиск, а не паджинатор на сотни страниц. К целостности данных это отношения не имеет.
Но это конечно ваше право иметь собственное мнение и не принимать истину.
Не путайте, пожалуйста, истину с мнением большинства.
Сомнительно, чтобы пользователю хватило терпения домотать хотя бы до 100 страницы, не воспользовавшись фильтрацией и сортировкой.Зависит от темы и пользователя. Не все пользователи ведут себя так, как вы хотите. Некоторые, к примеру, сразу берут скролл и приблизительно проматывают длинное полотно, если вы понимаете о чем я.
Ну вот к примеру я пришел на какой-либо абстрактный ДжойРеактор впервые в жизни, мне он очень понравился и я начинаю поглощать контент быстрее, чем он появляется. Лег вечерком, поржал с 20 страничек старых мемчиков, выключил телефон, лег спать, а на следующую продолжил с той же страницы.
Вполне разумным будет задать жёсткий лимит на размер выдачи в, допустим, 1000 элементов.Нет, совсем не разумным)
Конечно, иногда такое решение вполне подходит. Но мне кажется, что вы решаете какие-то технические проблемы ограничивая пользователя так, как оно лучше ложится на ваш фреймворк.
Есть предложение, что вы не можете решить те проблемы с тормозами в mol
Я смотрю высокая скорость работы $mol вам спать спокойно не даёт?
Некоторые, к примеру, сразу берут скролл и приблизительно проматывают длинное полотно, если вы понимаете о чем я.
Ага, а потом наигранно удивляются, чего это браузер не может отрендерить всё это полотно мгновенно.
Лег вечерком, поржал с 20 страничек старых мемчиков, выключил телефон, лег спать, а на следующую продолжил с той же страницы.
Только за ночь напостили ещё контента, поэтому с 20 по 30 страницу идут одни бояны. Поэтому ленты новостей — это совершенно другая опера. Правильно реализуется двумя ручками:
- Дай топ свежих новостей.
- Пометь такие-то новости как прочитанные.
Но об этом уже в другой статье.
Нет, совсем не разумным)
Обоснуйте. Даже если на один элемент пользователь потратит секунду, то на 1000 элементов ему потребуется пол часа, что запредельно много.
Но мне кажется, что вы решаете какие-то технические проблемы ограничивая пользователя так, как оно лучше ложится на ваш фреймворк.
Попробуйте в Гугле дойти до 100 страницы.
Попробуйте в Гугле дойти до 100 страницы.Ну, может, в гугле и нету в этом смысла, но это не значит, что нигде нет.
Только за ночь напостили ещё контента, поэтому с 20 по 30 страницу идут одни бояны.Нет, оно работает совершенно не так. Тот сценарий, что я описал вполне реален.
Я правильно понимаю, что вы этот прекрасный антипаттерн рассматриваете только в контексте потребителей-людей, и чисто программные клиенты вас не интересуют?
Ну так тогда ваше предположение "можно отдать всего не больше 1000 элементов" некорректно (чисто программные клиенты легко забирают тысячу на страницу), а общие объемы измеряются сотнями тысяч и миллионами записей. Что делать будем?
ваше предположение "можно отдать всего не больше 1000 элементов" некорректно
1000 — разумное ограничение для пользователя. Для разных запросов и клиентов вы вольны задавать любые ограничения.
Что делать будем?
Формировать снепшот и стримить данные из него любым удобным способом, ибо снепшот иммутабелен.
Формировать снепшот
На сервере? Ресурсов-то хватит, удерживать снепшоты для всех клиентов?
… и, кстати, как для клиента это будет отличаться от обычного пейджинга?
Скажем, на Хабре в пагинации списка постов я бы предпочёл снепшоты или ещё какие курсоры, запоминающие список постов по id.
На сервере? Ресурсов-то хватит, удерживать снепшоты для всех клиентов?
Придётся найти.
как для клиента это будет отличаться от обычного пейджинга?
Выдачу снепшота вы можете делать как пейджингом, так и стримингом. Даже если делаете пейджингом, то у вас будет два типа запросов:
- Сформируй снепшот по параметрам
- Дай такую-то страницу такого-то снепшота.
Придётся найти.
… или не придется, если не делать снепшоты.
Выдачу снепшота вы можете делать как пейджингом, так и стримингом.
Если стримингом, то это ничем не отличается от обычного "отдай мне все данные" (разумные БД все равно там делают consistent-read-транзакцию), со всеми вытекающими недостатками.
Даже если делаете пейджингом, то у вас будет два типа запросов:
Очевидно, нет. Можно просто отдавать в каждой странице уникальный continuation id (потому что обратно программным клиентам ходить не надо). Другое дело, что это все равно требует хранения на сервере, что возвращает нас к вопросу "ресурсы-то где брать".
Зависит от количества клиентов и сценариев их работы.
Ну так некоторые клиенты и обычный пейджинг устраивает, так что антипаттерн как-то не складывается.
Суть моего комментария прямо ниже — нет в этом вопросе бест-практайс и антипаттернов, которые были бы определенно хороши или определнно плохи.
Суть моего комментария прямо ниже — нет в этом вопросе бест-практайс и антипаттернов, которые были бы определенно хороши или определнно плохи.
Ну вот да.
Понятно, что у "традиционного" пейджинга есть проблемы, и про них надо помнить (и, кстати, есть прекрасная статья про это, но и она слишком категорична), но их недостаточно, чтобы отказаться от пейджинга вовсе.
А вы не забывайте, что эта проблема бывает только для неустойчивого порядка, а он, в свою очередь, бывает при сочетании конкурентности и сортировки по мутабельному значению. Если конкретный клиент забирает неудаляемые данные, отсортированные по иммутабельному значению, у него эта проблема не случится. Или если клиент работает в условиях исключенной конкурентности.
Разумеется, в этом редком кейсе, когда нам повезло и выборка никогда не меняется мы можем позволить себе не делать снепшоты. Это исключение из правила, а не аргумент в пользу отказа от него.
У кого исключение, а у кого и нет.
Вы так говорите, как будто мы всегда можем себе позволить делать снепшоты, что далеко не так.
Вы мне напоминаете сурового такого каскадёра, который приходит в автошколу и авторитетно заявляет, что преподаватель не прав, утверждая, что нужно водить автомобиль аккуратно, не создавая аварийной обстановки, ведь есть профессии, где аварии наоборот надо целенаправлено создавать.
Я даже не буду говорить, кого вы мне напоминаете. Аналогии, как известно, больше говорят о том, кто их приводит, чем о сути вопроса.
Так вот, по сути вопроса. Пейджинг — это прием, который (как и многие другие) применим только в определенной ситуации. Предлагаемые вами снепшоты тоже применимы только в определенных сиутациях.
Для многих кейсов свести задачу к такой ситуации, где применим пейджинг, проще, чем свести ее к такой ситуации, где применимы снепшоты.
Пейджинг — это прием, который (как и многие другие) применим только в определенной ситуации.
И эта ситуация — иммутабельная коллекция. По снепшоту тоже можно безопасно применять этот приём. Собственно создание снепшота — это и есть способ свести любую ситуацию к той, где можно применить пейджинг. Или не применять, если снепшот получается небольшой.
Есть разные бизнес-правила и свойства, к сочетанию которых применимы разные технические решения пагинации или близких к ней техник типа бесконечного скролла. Должны быть аргументы не в пользу отказа от какого-то «дефолтного» механизма, а в пользу выбора одного из известных или выделения ресурсов на попытку разработать новый.
Главный аргумент в пользу выбора классической пагинации — простота технической реализации и простота объяснению бизнесу её особенностей, которые для бизнеса могут являться недостатками, а могут не являться.
А вы пробовали объяснить бизнесу эти особенности? Я вот пробовал. Ответ был коротким:
Меня не волнует где и почему вы накосячили. Исправляйте за свой счёт.
Если хотите использовать свои снэпшоты, то даже вызвать заказчика на диалог можно фразой типа «Самым быстрым и дешевым будет реализация примитивной пагинации, которая будет постоянно выводить непредсказуемые результаты, когда кто-то изменяет, удаляет или добавлять данные. Если вам нужно что-то другое — есть варианты. Как поступим?» Объективно — та же информация :)
В идеальном-то мире все думают наперёд, договариваются заранее, не косячат и не перекладывают друг на друга ответственность :-)
К сожалению, часто разработчики просто бездумно лепят паджинацию, мол все так делают, стандартное решение, чего тут ещё думать и обсуждать.
Собственно, комментарии к этой статье весьма показательны. Думаю никто не пошёл к бизнесу уточнить "а устраивают ли вас вот эти вот особенности, предложенного нами ранее решения? Может нам всё переделать пока не поздно?". Это ж надо признать свою возможную неправоту, что сильно бьёт по самолюбию.
Вместо этого, понеслось:
- Слили рейтинг статьи до рекордных значений.
- Слили автору карму, чтоб не мутил воду.
- Обозвали автора троллем, шизофреником и вообще дилетантом.
- Обвинили автора в NIH синдроме за то, что он рассказал про давно известное, но не столь популярное решение.
- Предложили несколько костылей, которые лишь частично решают некоторые из поставленных проблем в некоторых частных случаях.
- Усмотрели в предложенном решении кучу несуществующих недостатков.
- Отделались общими фразами про "для любого решения можно подобрать такие условия, где оно будет единственно возможным или совершенно не возможным".
Представьте, что сейчас 1968 год, все пользуются goto
, а тут Дейкстра пишет свою небезызвестную статью. Так и вижу комментарии:
- Просто не прыгай через
goto
в циклы и никаких проблем. - Структурное программирование — это просто синтаксический сахар для
goto
. - Я каждый день с утра до вечера пилю конечные автоматы, ваше структурное программирование сделает мой код ещё менее читаемым.
- Надо спрашивать у бизнеса до разработки должны ли мы использовать
goto
.
Однако, спустя 50 лет, использование goto
без крайней необходимости будет считается плохой практикой. Хотя он всё так же будет присутствовать в языках наших потомков:
Часто вообще необходимости в паджинации при детальном анализе «а зачем» не оказывается. Оказывается, что в одних кейсах юзеру нужна печать списка из 500-1000 пунктов и ему лишь достаточно первой страницы на экране, чтобу убедиться что грубых ошибок не сделал при поиске/фильтрации, а в других для не очевидно как искать/фильтровать записи и только поэтому листал по страницам. Упростили использование поиска, стали отдавать первые 50 и общее количество, а в UI кнопка «показать все N» с предупреждением «может занять очень много времени» если N > 1000.
Сперва давайте обратим внимание, что при работе с базой есть 2 разные по своей сути операции:
- Поиск. Относительно тяжёлая операция поиска указателей на данные по некоторому запросу.
- Выборка. Относительно простая операция собственно получения данных.
Я отдельно, кстати, хочу заметить, что далеко не всегда сценарий "сначала выбери список идентификаторов, а потом забирай записи по этим идентификаторам" будет быстрее (или хотя бы не медленнее), чем просто получение всех записей, подходящих под критерий; особенно это сильно проявляется, когда мы не напрямую с БД говорим (а какой клиент это нынче делает?), а с прикладным сервером, у которого своих мозгов [не] хватает.
А можно на хабре рейтинг статьи установить в самом вверху, чтобы не приходилось на мобильном устройстве сперва заходить в статью, затем всю ее прочитать, а потом увидеть рейтинг. Был бы благодарен за ответ
Моя реализация чего? Паджинации? Так статья как раз о том, что её не надо делать.
Почитав статью и комментарии, мне кажется, что автор просто толсто троллит или это шизофрения?
Идеально было бы:
Один раз произвести поиск и где-то запомнить его результаты в виде снепшота на определённый момент времени.
Быстро выбирать данные мелкими порциями по мере необходимости.
Ну так ты же сам понимаешь в чём проблема, так зачем штамповать Снапшоты, если есть Кэширование? Дак еще потом ты выбрку делать. Кто мешает закэшировать данные на стороне сервера и спокойно через skip / take (или как традиционно offset / limit) брать порционно данные?
Пример:
public Info getInfo(
@RequestParam(value = "sort", required = false) final String sort,
@RequestParam(value = "skip") final Integer skip,
@RequestParam(value = "take") final Integer take) {
Criteria criteria = Info.builder()
.range(Range.of(skip, take, Sort.by(SortUtils.getOrder(sort, Sort.Direction.ASC))))
.build();
return InfoService.find(criteria);
}
Просто никак не могу дойти до той истины, которую проповедует автор статьи. В большинстве случаев, заказчику надо чтобы просто данные были, а как там страницы съезжают или данные восполняются — уходит на голову разработчика. Тот способ, что описывал выше, легко подходит для реализации Lazy скроллов без пагинации.
Где хранить снепшоты? тут есть 2 варианта:
На сервере. Но тогда мы забиваем его кучей мусора с результатами поисков, которые со временем надо вычищать.
На клиенте. Но тогда надо сразу же передавать весь снепшот клиенту.
Тот кто читает этот пост, если ты заметил, что у тебя тормозит сайт, знай — это автор этого поста постарался.

Когда пришел в новую команду и меня попросили оптимизировать работу сайта, я просто в шоке был с того что он на фронт тянет. Наркоманы…
Поэтому прошу вас, не делайте никаких антипаттернов и всяких криворульных универсальных велосипедов. Реализация простыми и понятными способами — самый лаконичный вариант в любом деле.
P.S. Полистав статьи автора, особенно эту сделал вывод:
Здравствуйте, меня зовут Дмитрий Карловский и я… антиконформист
Человек просто не хочет соглашаться ни с чьим мнением и продвигает своё сомнительное решение, которое вам либо сервер превратит в помойку, либо клиент, как на скрине выше.
Краткий итог: Да, есть пагинация. Да, всем нужно знать какие есть проблемы. Прекратите придумывать велосипеды, уже до вас всё придумали (и это не только пагинации касается), берите, исходя от ваших потребностей, и пользуйтесь.
зачем штамповать Снапшоты, если есть Кэширование?
Отличительная особенность "кэша" в том, что он может быть в любой момент очищен, не ломая приложение. Кеш увеличивает производительность, но не несёт никакой бизнес-ценности. Если кеш у вас потухнет между 5 и 6 страницей, вы получите все описанные в статье проблемы. Если же вы сделаете большое время жизни кеша, то получите другую проблему — пользователь обновляет страницу в надежде получить актуальный список, а вы ему показываете устаревшие данные.
Если кеш у вас потухнет между 5 и 6 страницей, вы получите все описанные в статье проблемы
Если бы, да кабы.
Расскажу по секрету — можно не обновляй страницу целиком, а только блок с гридом.
Реализуй механизм обновления кэша, это не сложно. Опять же, зависит от задачи. Критично иметь тру данные в Realtime или некритично иметь расхождение в какой-то промежуток времени — это не то на чем стоит заострять внимание.
Если же вы сделаете большое время жизни кеша, то получите другую проблему
Извините, а снапшот раз в 10 секунд будет обновляться или как? Откуда я буду знать, что на данный момент смотрю на актуальные данные? Точно такая же ситуация.
Повторюсь, что уже писал раз пять. Тут всё решается просто — поговорил с бизнесом, согласовал как им нужно.Не надо париться.
Извините, а снапшот раз в 10 секунд будет обновляться или как? Откуда я буду знать, что на данный момент смотрю на актуальные данные?
Он создаётся каждый раз, когда вы инициируете новый поиск. В частности, при обновлении страницы.
Если же мы говорим про многостраничный сайт, то, очевидно, в результате поиска будет редирект на урл с идентификатором снепшота и номером страницы. Тут сколько ни перезагружай страницу — ничего не поменяется.
В данном кейсе это одно и то же.
> В большинстве случаев, заказчику надо чтобы просто данные были, а как там страницы съезжают или данные восполняются — уходит на голову разработчика.
И претензии тоже потом уходят на голову, в лучшем случае, разработчика. Как только слышу «данные нужно частями показывать» (не важно, страницы или подгрузка), то всегда спрашиваю чёткий алгоритм показа. Могу предложить варианты, их плюсы и минусы, но ни в коем случае не выбирать за заказчика.
Популярные антипаттерны: паджинация