Обработка стратегии и аннулирование кэшированных данных по подпискам в умеренно сложном сценарии использования
Возьмем, к примеру, приложение для чата. Пользователь имеет доступ к нескольким цепочкам чата, каждая из которых содержит несколько сообщений. Пользовательский интерфейс состоит из списка потоков в левой части (listThreads
), который содержит имя другой стороны, последнее сообщение, количество непрочитанных сообщений, дату и время последнего сообщения, а также фактические сообщения (viewThread
) и поле ответа справа (например, мессенджер facebook).
Когда пользователь выбирает цепочку сообщений, viewThread
Компонент подписывается на запрос примерно следующего содержания:
query thread {
threads(id: 'xxxx') {
id
other_party { id name }
unread_messages
messages {
sent_by { id }
timestamp
text
}
}
Чтобы обновления были доступны, он устанавливает q.subscriptToMore
с подпиской по линиям:
subcription newMessages {
newMessage(thread_id: 'xxx') {
sent_by { id }
timestamp
text
}
}
Это работает отлично, новые сообщения отображаются так, как должны.
Чтобы вывести список доступных цепочек сообщений, запрашивается менее подробное представление всех цепочек:
query listThreads {
threads {
id
other_party { id name }
unread_messages
last_updated_at
}
}
Для синхронизации списка используется одна и та же подписка без фильтрации по thread_id
, а данные списка потоков обновляются вручную
Это тоже отлично работает.
Однако если поток A
выбрано, сообщения ветки A
кешируются. Если потокB
после этого выбирается подписка на запрос, получающий подробную информацию о ветке A
уничтожается, поскольку наблюдаемое уничтожается, когда маршрутизатор заменяет viewThread
составная часть.
Если после этого в ветку приходит сообщение A
пока пользователь просматривает ветку B
, то threadList
обновляется (поскольку эта подписка активна), но если пользователь снова переключается на поток A
, сообщения загружаются из кеша, который теперь устарел, поскольку не было подписки на эту конкретную цепочку сообщений, которая могла бы обновить или сделать кеш недействительным.
В других обстоятельствах, когда пользователь переходит на совершенно другую страницу, где список потоков не будет отображаться, проблема еще более очевидна, поскольку нет ничего, связанного с сообщениями чата, на которые активно подписаны, поэтому нет ничего, что могло бы аннулировать кешированные данные когда приходит новое сообщение, хотя сервер теоретически предоставляет способ сделать это, предлагая новые события подписки на сообщения.
У меня следующий вопрос:
Каковы наилучшие методы сохранения данных в синхронизации / признании недействительными, которые были кэшированы Apollo и которые не активно "используются"? Каковы наилучшие практики для синхронизации вложенных данных (сообщения потоков события [см. Ниже]). Мне не хочется реализовывать логику того, как подписываться и обновлять данные сообщения в запросе события, - это чистое решение.
С помощью .subscribeToMore
работает для сохранения данных, которые активно используются в синхронизации, но как только этот запрос больше не используется, данные остаются в кеше, который со временем может или не может устареть. Есть ли способ удалить кэшированные данные, когда наблюдаемое выходит за пределы области видимости? Как и сохранить эти данные в кеше, пока есть хотя бы один запрос, использующий их, потому что я верю, что он также реализует логику, которая будет синхронизировать их на основе событий push-уведомлений сервера.
Следует ли использовать службу, которая подписывается (на протяжении всего жизненного цикла SPA) на все события подписки и содержит сведения о том, как обновить каждый тип кэшированных данных, если они присутствуют в кеше? (эта служба может получать уведомления о том, какие данные необходимо синхронизировать, чтобы избежать использования большего количества ресурсов, чем необходимо) (как в службе, которая подписывается на всеnewMessage
события, и тыкает кеш исходя из этого)? Будет ли это автоматически выдавать новые значения для запросов, которые вернули объекты, имеющие ссылки на такие данные? (обновил быmessage:1
сделать запрос потока, который вернул то же самое message:1
в поле сообщений автоматически выдает новое значение) Или эти запросы тоже нужно обновлять вручную?
Это становится очень обременительным при расширении этой модели, скажем, Events
у которых также есть собственная ветка чата, поэтому запросы event { thread { messages { ... } }
теперь необходимо подписаться на newMessage
подписка, которая нарушает инкапсуляцию и принцип единой ответственности. Также проблематично подписаться наnewMessage
данные, которые потребуются для предоставления идентификатора потока сообщений, связанного с событием, но это неизвестно до возврата запроса. В связи с этим.subscribeToMore
нельзя использовать, потому что в этот момент у меня нет thread_id
доступно еще.
1 ответ
Если предполагаемое поведение - "каждый раз, когда я открываю ветку, показывать последние сообщения, а не только то, что кэшировано", то вам просто нужно установить fetchPolicy
для тебя thread
запрос к network-only
, что гарантирует, что запрос всегда отправляется на сервер, а не выполняется из кеша. Документы дляapollo-angular
отсутствует информация об этой опции, но вот описание из документации React:
Допустимые значения fetchPolicy:
cache-first
: Это значение по умолчанию, при котором мы всегда пытаемся сначала прочитать данные из вашего кеша. Если все данные, необходимые для выполнения вашего запроса, находятся в кеше, эти данные будут возвращены. Apollo будет получать данные из сети только в том случае, если кэшированный результат недоступен. Эта политика выборки направлена на минимизацию количества сетевых запросов, отправляемых при рендеринге вашего компонента.cache-and-network
: Эта политика выборки заставит Apollo сначала попытаться прочитать данные из вашего кеша. Если все данные, необходимые для выполнения вашего запроса, находятся в кеше, эти данные будут возвращены. Однако, независимо от того, находятся ли полные данные в вашем кеше, этот fetchPolicy всегда будет выполнять запрос через сетевой интерфейс, в отличие от метода cache-first, который будет выполнять ваш запрос только в том случае, если данные запроса не находятся в вашем кеше. Эта политика выборки оптимизирует для пользователей, получающих быстрый ответ, а также пытается поддерживать кэшированные данные в соответствии с данными вашего сервера за счет дополнительных сетевых запросов.network-only
: Эта политика выборки никогда не вернет вам исходные данные из кеша. Вместо этого он всегда будет делать запрос к серверу через ваш сетевой интерфейс. Эта политика выборки оптимизирует согласованность данных с сервером, но за счет мгновенного ответа пользователю, когда он доступен.cache-only
: Эта политика выборки никогда не будет выполнять запросы через ваш сетевой интерфейс. Вместо этого он всегда будет пытаться читать из кеша. Если данных для вашего запроса нет в кеше, будет выдана ошибка. Эта политика выборки позволяет вам взаимодействовать только с данными в вашем локальном клиентском кэше, не делая никаких сетевых запросов, что обеспечивает быстродействие вашего компонента, но означает, что ваши локальные данные могут не согласовываться с данными на сервере. Если вас интересует взаимодействие только с данными в вашем кэше Apollo Client, также обязательно посмотрите методы readQuery() и readFragment(), доступные вам в вашем экземпляре ApolloClient.no-cache
: Эта политика выборки никогда не вернет ваши исходные данные из кеша. Вместо этого он всегда будет делать запрос к серверу через ваш сетевой интерфейс. В отличие от сетевой политики, он также не будет записывать данные в кэш после завершения запроса.