Назад

Семь раз получи, один раз закэшируй!

Процессы
18 июн., 2024
article cover

В этой статье лид отдела аналитики Алексей Аксянов на реальном кейсе рассмотрит, как организовать кэширование данных в приложениях. Дальше его авторская колонка.


Я работаю аналитиком уже более 10 лет, и так уж сложилось, что зачастую разработчики ожидают от меня более «бизнесовые» постановки, предпочитая самостоятельно прорабатывать техническую составляющую решения. В этой статье мне хочется показать, что аналитикам можно и нужно копаться в технике. И в качестве примера мы будем разбирать такую технологию, как кэширование данных. По ходу повествования я постараюсь показать зоны ответственности бизнес-, системного аналитика и разработчика.

Немного занудной теории

Кэширование — механизм временного оперативного хранилища данных между клиентом и сервером. Он позволяет временно сохранять требуемые нам данные в буфере для использования в определённых ситуациях. Техническая реализация может быть различной, об этом поговорим чуть дальше.

Преимущества применения кэширования:

  • Ускорение получения данных. Нам не нужно каждый раз обращаться к серверу или базе данных за однотипными или редко изменяющимися данными.
  • Снижение нагрузки на мастер-систему. Это полезное следствие первого пункта. Меньше запросов на сервер — меньше обращений к базе данных, меньше трафика в целом. Это особенно полезно, если у нас не самое производительное железо или имеются строгие нефункциональные требования к производительности системы.
  • Возможность дообогащения и конвертации данных. Это очень полезное свойство, которое зачастую помогает обойти некоторые технические ограничения текущей реализации. Об этом подробнее расскажу далее в ходе разбора кейса.

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

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

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

И наконец, можно реализовать мгновенное получение данных в самых частых сценариях использования. Храним данные на устройстве, поэтому не ходим на сервер и радуем пользователей скоростью отклика.

Недостатки у технологии тоже есть:

  • Устаревание данных в кэше. Это фундаментальный недостаток. Неактуальные данные могут натворить много бед, поэтому всё подряд кэшировать не получится, а аналитику будет особенно важно, исходя из бизнес-требований, чётко определить, что кэшируем, а что — нет.
  • Усложнение логики обработки запросов. Аналитику придётся описать более подробные сценарии использования, разработчику придётся написать больше строчек кода, а потом всё это надо тестировать и внедрять. Однако, если мы хотим сделать действительно классный продукт, без этого никак.
  • Использование дополнительной памяти на устройстве или сервере. Закэшированные данные нужно где-то хранить, однако сейчас, при наличии различных технологий быстрой и недорогой памяти, этот недостаток не очень критичен.

Для простоты понимания я бы выделил несколько вариантов классификации кэша.

Во-первых, по месту хранения, чтобы понять, :

  • на клиенте (локальная БД, оперативная память, встроенные механизмы)
  • на сервере (Redis)
  • комбинированный (сервер+клиент)

По продолжительности хранения. Для упрощения восприятия назову более «человеческие» термины:

  • “Короткий” актуален в рамках экрана или сессии пользователя.
  • “Длинный” - сохраняется вне сессии пользователя или даже после удаления приложения

Кэширование и аналитики

Мы разобрались с теоретической частью, однако остался главный вопрос: а что требуется от аналитика для описания требований и постановки задач по этой технологии? Ответ прост: ничего особенного, просто хорошо сделать свою работу :)

Бизнес-аналитик собирает и формализует требования к фиче:

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

Системный аналитик подсвечивает технические аспекты:

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

Разработчик:

  • выбрать оптимальный технический способ реализации кэша
  • реализовать кэширование согласно требованиям

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

Разбираем кейс

В этой части статьи я поделюсь кейсом применения кэширования из своего практического проектного опыта.

Начнём со стартовых условий.

Разрабатываем мобильное приложение для клиентов банка. Мы пришли на проект в активной фазе разработки и получили следующие вводные:

  • приложение должно работать в условиях медленного интернета
  • middleware банка уже реализован, мы можем ограниченно на него влиятьинтерфейс уже отрисован, мы можем ограниченно на него влиять
  • первичная реализация кэширования уже есть, аналитика по ней не проводилась, её сделали разработчики на своё усмотрение

Теперь подробнее про текущую реализацию. Интерфейс выглядит примерно так:

block image

На главном экране отображаются все банковские продукты клиента, разбитые по категориям («Карты», «Счета», «Вклады», «Кредиты»). В общем, довольно стандартная картина для любого банковского приложения. При тапе на продукт открывается деталка по нему с расширенной информацией. Главная «фишка» от дизайнеров на этом экране — карусель, которая позволяет перелистывать все продукты клиента горизонтальными свайпами.

API для получения продуктов клиента (на примере карт) :

GET /cards - получаем список всех карт с минимальной информацией, достаточной для отображения на главном экране:

block image

GET /cards/{cardId}/details - подробная информация по карте с расширенным атрибутивным составом (лимиты, реквизиты и т.д.):

block image

Знакомимся с текущей реализацией кэширования

Реализован раздельный кэш для запросов /cards и /details. Настроены индивидуальные триггеры для деактуализации каждого кэша.

Для начала согласуем с командой и зафиксируем в аналитике эти самые триггеры, когда кэш запроса становится неактуальным:

  • завершение сессии пользователя (в т. ч. при неактивности пользователя, по таймауту)
  • logout или принудительное завершение работы приложения. Возвращаясь к классификации в начале статьи, кэш этих запросов можно считать “коротким”, так как данные завязаны на конкретного пользователя и могут обновляться часто
  • успешное совершение операции по карте (оплата, перевод)
  • успешное изменение настроек карты (переименование, изменение лимитов)

Далее проанализируем плюсы и минусы такой реализации.

Преимущества:

  • быстрая загрузка данных на главном экране (лёгкий запрос + кэш)
  • самая простая логика. Задача решена фактически “в лоб”.

Недостатки:

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

Пробуем другой вариант

Ускоряем загрузку деталки - для обоих экранов берём данные из дообогащённого запроса /cards. Запрос /details используется только для последующих экранов (настройки, реквизиты). Принудительно обновляем оба кэша при обновлении хотя бы одного из них.

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

Однако не всё оказалось так идеально, ведь объём данных в запросе /cards увеличился, и это стало плохо влиять на скорость загрузки главного экрана. И если у подавляющего большинства клиентов (у которых максимум 2-3 банковских продукта) эта разница не ощущалась бы, то для VIP-пользователей (у которых могут быть десятки самых разных продуктов) это точно стало бы критичной проблемой.

Поэтому предложенное решение не ушло дальше тестового контура, а я ушёл придумывать следующий вариант реализации.

Финальное решение - объединённый кэш

На новое решение меня натолкнула беседа с разработчиком про техническую реализацию кэширование на мобильных устройствах. И от него я услышал реплику про то, что “фактически кэш - это обычный json-файл, который хранится на устройстве и обновляется по определённым триггерам”.

А что если сделать структуру этого кэша нужного нам формата и научиться заполнять её из 2 разных запросов по заданной логике? На устройстве список (кэш) карт будет иметь общую структуру запроса /cards, однако атрибутивный состав внутри каждого элемента массива будет соответствовать структуре запроса /details. Получается вот такая структура:

block image

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

Логика получилась такая:

  • Данные могут обновляться из любого запроса в любом порядке. При обновлении стараемся перезатереть старые данные в модели, однако для некоторых её элементов можно указать особые условия
  • На главном экране вызываем запрос /cards, на деталке - в фоне вызываем /details. Для быстрой работы карусели в фоне превентивно вызываем запрос на получение детальной информации по предыдущему и следующему продуктам.
  • Добавляем скелетоны на некоторые блоки в деталке, данные для которых подгружаются в фоновом режиме. Здесь пришлось попросить дизайнеров немного обновить макеты, чтобы аналитика и реализация соответствовали дизайну. 
  • Триггеры для деактуализации кэша не изменяются

Реализация и тестирование показали отличную работу такой модели. Пользователи получили быструю загрузку главного экрана, мгновенное отображение базовой информации о продукте в деталке, а также быструю работу карусели. А нам не потребовалось дорабатывать сервисный слой, а само решение получилось более стабильным, так как почти всегда имелась информация для отображения главного экрана и деталки.

Итоги

В качестве итога хотелось бы сделать единственный простой вывод - не стесняйтесь выходить за рамки текущей задачи и старайтесь не думать шаблонно! Именно творческий подход и насмотренность отличают специалистов высокого уровня от всех остальных.