Идея решения для дополнительных обновлений с использованием кэша браузера - PullRequest
0 голосов
/ 04 мая 2018

Некоторое время назад я спросил, как сделать Инкрементные обновления с использованием кэша браузера . Здесь я кратко излагаю проблему - для большего контекста, особенно для причины, по которой я хочу это сделать, обратитесь к старому вопросу. Я бы хотел, чтобы вы рассмотрели и улучшили мою идею решения (просто идею, поэтому не отправляйте меня на проверку кода : D).

Проблема

Клиент (одностраничное приложение) получает довольно большие списки с сервера. Это работает нормально и на самом деле экономит ресурсы сервера как

  • один и тот же список может обслуживаться несколькими клиентами
  • и клиенты выполняют фильтрацию и сортировку, не беспокоя сервер снова и снова.

Некоторые из этих списков относятся к конкретному пользователю, другие являются общими для группы пользователей, другие являются глобальными. Все эти списки могут измениться в любое время, и мы никогда не хотим обслуживать устаревшие данные (HTTP-заголовок Cache-Control и Expires здесь не используются).

Мы используем 304 NOT MODIFIED, что помогает в случае, если ничего не изменилось. Когда что-то меняется, изменения обычно небольшие, но HTTP вообще не поддерживает этот случай, поэтому мы должны отправить весь список, включая неизмененные части. Вместо этого мы можем отправить дельту, но нет никакого очевидного способа, как это может быть эффективно кэшировано браузером (кэширование в localStorage или аналогично далеко не так хорошо, как я объяснил в моем связанном вопросе).

Важным свойством наших списков является то, что каждый элемент имеет уникальный id и последний измененный timestamp. timestamp позволяет нам легко вычислять дельту, находя элементы, которые недавно изменились. id позволяет нам применить дельту, просто заменив соответствующие элементы (список внутренне Map<Id, Item>). Это не будет работать для удалений, но давайте пока проигнорируем их.

Идея

Я предлагаю использовать несколько списков (любое число должно работать) разных размеров, с большим списком, который можно долго кэшировать. Давайте предположим, что день - подходящая единица времени, и давайте использовать следующие три списка:

  • WEEK Это базовый список, содержащий всех элементов, которые существовали в произвольный момент времени на текущей неделе .

  • DAY Список, содержащий все элементы, у которых изменился на этой неделе , за исключением сегодняшнего дня, поскольку они существовали в произвольное время в текущий день . Элементы, измененные сегодня, могут включаться или не включаться.

  • CURRENT Список, содержащий все элементы, которые изменились сегодня , как они существуют только сейчас .

Клиент получает все три списка. Он начинается с WEEK, применяется DAY (т.е. вставляет новые элементы и заменяет старые) и, наконец, применяет CURRENT.

Пример

Предположим, в списке есть 1000 элементов, причем 10 элементов меняются в день.

Список WEEK содержит все 1000 элементов, но его можно кэшировать до конца недели. Его точное содержание не указано, и разные клиенты могут иметь разные его версии (при условии, что выполняется условие из вышеупомянутого пункта). Это позволяет серверу кэшировать данные в течение целой недели, но также позволяет ему отбрасывать их, поскольку обслуживание текущего состояния тоже подойдет.

Список DAY содержит до 70 элементов и может кэшироваться до конца дня.

Список CURRENT содержит до 10 элементов и может кэшироваться только до тех пор, пока что-либо не изменится.

Связь

Клиент должен ничего не знать об используемой шкале времени, но ему нужно знать количество списков, которые нужно запросить. «Классический» запрос типа

GET /api/order/123      // get the whole list with up to date content

будет заменено тремя запросами, такими как

GET /api/0,order/123    // get the WEEK list
GET /api/1,order/123    // get the DAY list
GET /api/2,order/123    // get the CURRENT list

Вопросы

Обычно изменения действительно такие, как описано, но иногда все элементы изменяются одновременно. Когда это происходит, тогда все три списка содержат все элементы, что означает, что мы должны обслуживать в три раза больше данных.К счастью, такие события очень редки (например, когда мы добавляем атрибут), но я хотел бы найти способ, позволяющий нам избежать таких всплесков?

Видите ли вы другие проблемы с этой идеей?

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

Какие-нибудь улучшения?

Ответы [ 2 ]

0 голосов
/ 11 мая 2018

Я предполагаю, что вы понимаете следующие общие проблемы с вашим подходом:

  • По сравнению с подходом «один большой список + 304» это уменьшает сетевой трафик, но увеличивает время обработки клиента: ваш код клиента по-прежнему видит те же ответы в «горячем» кэше, что и в «холодном», но теперь их три с перекрывающимися данными.
  • По сравнению с подходом localStorage, это немного уступает «умной» стороне, что имеет значение для долгосрочной ремонтопригодности. Чистые документы и набор тестов являются обязательными.

Предполагая это, мне нравится ваш подход.

Есть одна вещь, которую я мог бы изменить. Это добавляет немного гибкости, но и немного сложности. Это может быть или не быть хорошей идеей.

Вместо жесткого кодирования трех URL-адресов на клиенте можно отправлять явные гиперссылки в заголовках ответов. Вот как это может работать:

Клиент запрашивает жестко закодированную «точку входа»:

> GET /api/order/123?delta=all

< 200 OK
< Cache-Control: max-age=604800
< Delta-Location: /api/order/123?delta=604800
<
< [...your WEEK list...]

Видя заголовок Delta-Location, клиент запрашивает его и применяет полученную дельту:

> GET /api/order/123?delta=604800

< 200 OK
< Cache-Control: max-age=86400
< Delta-Location: /api/order/123?delta=86400
<
< [...your DAY list...]

И так до тех пор, пока ответ не будет иметь Delta-Location.

Это позволяет серверу в любое время в одностороннем порядке изменять структуру дельты. (Конечно, он все еще должен поддерживать старую структуру до тех пор, пока она может кэшироваться на клиентах.)

В частности, это позволяет решить проблему очередей. После изменения массы вы можете начать обслуживать гораздо меньшие дельты (с соответственно меньшим max-age), чтобы они исключали изменение массы. Тогда вы будете постепенно увеличивать размеры дельты с течением времени. Это потребовало бы дополнительной логики / конфигурации на стороне сервера, но я уверен, что вы сможете выяснить это, если всплески вызывают у вас серьезную озабоченность.

В идеале вы должны разрешить Delta-Location для URL запроса, чтобы он вел себя как стандартные Location и Content-Location заголовки, для единообразия и гибкости. Одним из способов сделать это в JavaScript является объект URL.

Дальнейшие вещи, которые вы можете настроить в этом подходе гиперссылок:

  • Вероятно, вы должны сделать max-age чуть меньше delta, чтобы учесть сетевые задержки.
  • Вам может понадобиться дополнительная логика на клиенте, чтобы избежать бесконечного цикла, если сервер (ошибочно) связывается с предыдущей дельтой.
  • Вы можете использовать стандартный Link заголовок вместо нестандартного Delta-Location. Но вам все еще нужен нестандартный тип отношений, поэтому неясно, что это может вас купить.
0 голосов
/ 11 мая 2018

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

Все эти списки могут измениться в любое время, и мы никогда не хотим обслуживать устаревшие данные

Таким образом, вы должны использовать длительное время кеширования и URL для очистки кеша .

Мы используем 304 НЕ ИЗМЕНЕНО

Это наихудший из возможных способов решения проблемы. Большая часть стоимости поиска находится в латентном состоянии. Если вы отвечаете с ответом 304, то у вас уже была большая часть расходов - это будет особенно заметно, когда вы имеете дело с небольшими порциями данных. HTTP / 2 помогает (по сравнению с 1.0 и 1.1), но не устраняет стоимость.

Я бы также поставил под сомнение многие предположения, сделанные в вашем первоначальном вопросе.

...