Сразу введу в курс дела, это был легаси проект и задача была доработка одного эндпоинта, который должен возвращать огромную Json-нину. По итогу работы среднее количество строк в респонсе было 800.000-2.000.000 строк и весил он в районе 30 мб.

На этом проекте я выяснил что Postman уже ломается от 1.000.000 строк, перестаёт работать форматирование и начинает хромать поиск. А в целом весь json напоминал мне один огромный клубок снега который пустили горы и он всё разрастался и разрастался, т.к. когда я пришёл на проект он был всего лишь 40.000-80.000 строк.

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

Схема json респонса
Схема json респонса

И в целом весь этот JSON обрабатывался в районе 4-5 минут, после того как на выборку из БД прикрутили кэш, то последующий запросы уже обрабатывались гораздо быстрее 3-10 секунд. Но первый запрос всё равно обрабатывался очень долго.

Решение было принято моментально нужно разделить один большой эндпоинт на два поменьше. В итоге получилось так что первый 4 уровня ушли на один эндпоинт(назовём его summary), а последующие уже выбирались по id на втором эндпоинте(назовём details).

Реализация первого эндпоинта не составила большого труда, он сократил наш великий Json до 8.000-20.000 строк и размером 100-400Kb. И время запроса также уменьшилось до 1-3 секунд.

Среднее время запроса с включеным кэшом
Среднее время запроса с включеным кэшом

Но между двумя частями всё равно оставалась сильная связь и информацию на details эдпоинте о последующих уровнях просто так взять не удавалось, нам нужны были предыдущие данные из summary. Мы решили узнать у заказчика какое решение ему предпочтительнее и как оказалось он имел своё видение на проблему. Его идея заключалось в том, чтобы сохранять весь респонс на 30мб в blob-storage, а потом в details скачивать этот json и забирать нужную нам информацию. Мы были мягко говоря в замешательстве слегка не понимаю для чего вообще тогда нам нужно разделение одного большого эндпоинта. Мы обсудили этот момент и окончательно предложили реализовать две модели поведения:

  1. Забирать весь json и сохранять его в blob, после в details забирать весь json из blob-а и возвращать нужную нам информацию.

  2. В blob сохранять информацию только из summary(8-20к строк), а потом в details забирать этот json и при помощи него забирать дополнительную информацию.

  3. Забирать весь json и сохранять его в blob, после в details забирать весь json из blob-а и возвращать нужную нам информацию.

По итогу, как и ожидалось первый случай с треском провалился. Только взятие json-а из blob-storage занимало по разному от 50 секунд до 4 минут, но тут надо оговориться что это сильно зависит от самого blob-storage, скорости интернета и azure.

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

Заказчика естественно всё устроило, но у нас всё ещё было неприятное чувство использование ресурса не по назначению. Мы хотели использовать кэш. На проекте, как я уже до этого упоминал, был настроен кэш и мы решили заюзать его. 

По итогу результат нас порадовал, среднее время запроса на details эндпоинт составляло 90-300 мс.

В результате работало это дело следующим образом.

  • Пользователь обращается к первому эндпоинту summary и получает общую информацию, в это время blob записывает к себе этот эндпоинт.

  • Если пользователь хочет получить более подробную информацию он обращается к details эндпоинту.

  • Тот в свою очередь для начала проверяет кэш на существования summary респонса, если он его не находит, то тогда он полезет в blob-storage и заберёт его оттуда (используем blob как долгосрочное хранилище), а потом добавит в кэш со сроком жизни 15 минут.

Таким образом нам удалось уменьшить время ожидание с 5 минут до 1.5 секунд

От blob-storage по итогу мы всё равно не могли отказаться так как нам нужно было хранить информацию о запросах и респонсах, для последующего бизнес анализа.

Для кэша использовалась сторонняя библиотека CacheManager которая работала по средством in-memory cache.

Комментарии (5)


  1. anonymous
    00.00.0000 00:00


    1. Exvise Автор
      27.09.2021 23:41
      +1

      Добрый вечер, никакого мутного контента, просто решил попробовать себя в чём-то новом, захотел поделится историей вот и всё
      Видимо первый блин комом, поэтому да, придётся удалить, надеюсь следующий раз будет лучше !)


  1. LeshaRB
    27.09.2021 22:08
    +3

    Прочитал, честно каша... Blob, summary, details...

    Вспомнилось стихотворение

    ...

    Смешались в кучу кони, люди,

    И залпы тысячи орудий

    Слились в протяжный вой…"

    ...


  1. dabrahabra
    27.09.2021 22:50

    Спасибо, но возникает резонный вопрос, зачем?


  1. MyraJKee
    29.09.2021 11:00

    Очень трудно понять. Начиная с того что это за клиент, которому нужен такой огромный объем данных за раз. Два эндпоинта суммарно отдающие 30 мб json с вложенностью в 80 уровней??? Такое ощущение что там изначально какие-то фундаментальные архитектурные проблемы.

    И потом, почему этот клубок разбили всего на 2 части? Вообще не понятно.