Как отслеживать изменение стоимости активов в портфеле - NestJS, Temporal

По выходным я стараюсь не тратить время впустую. Пробую различные активности вне дома или работаю над своими pet-проектами. В данный момент я разрабатываю FuncFund — инструмент для отслеживания средств в широком портфеле в реальном времени с использованием ИИ-аналитики. Появилась сложная задача: нужно отображать разницу за разные периоды (1 день, 1 месяц, 1 час и другие).

Как отслеживать изменение стоимости активов в портфеле - NestJS, Temporal

Проблематика

Чтобы отображать разницу за период, нам нужно несколько значений: текущая дата, смещение времени и стоимость активов в прошлом. Проблему вызывает только последнее.

Есть два глобальных подхода к хранению исторических данных:

  • Сохранять все активы в первичном виде и применять к ним исторические данные из внешних API (московская биржа, Binance)
  • Регулярно сохранять исторические значения сумм по активам для разных курсов

Выделим требования и оценим что больше подходит:

  • Пользователь в любое время может менять активы (оба варианта выполняют)
  • Валюта также изменяется со временем (оба варианта выполняют)
  • Сравнение должно быть максимально точным (второй вариант - дискретный, редкие сохранения создадут неточности)
  • Сервис сравнения должен быть доступен почти всегда (первый вариант зависит от множества API, не все обладают историческими данными)

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

Расчеты

Вводные: 1 миллион регистраций, 10 валют, 8 групп активов у каждого пользователя, точность сравнения p95, одна историческая запись - 42 байта, сохранение данных одного пользователя - 60ms. Доступные пользователю интервалы от 1 часа до одного года.

Сколько места будут занимать данные за максимальный период - 1 год? Как часто нужно сохранять данные, где золотая середина? Сколько Pod`ов должно сохранять данные?

  • Вес всех записей одного пользователя в моменте: 10 валют ⋅ 8 групп ⋅ 42 байта = 3`360 байт
  • Минимальный интервал сохранения по теореме Котельникова: 1 час / 2 = 30 минут
  • Сколько времени занимает сохранения всех данных пользователей? 60ms ⋅ 1`000`000 = 16.6 часов
  • Получается, один Pod не успеет сохранить столько данных, нужно больше: 16.6 часов / 30 минут ⋅ 1.25 фактор масштабирования= 42 пода
  • Посчитаем, объем всех записей за год: 1 год ⋅ 365 дней ⋅ 24 часа ⋅ 60 минут / 30 минут ⋅ 3`360 байт ⋅ 1`000`000 пользователей = 54`825 гигабайт = 55 терабайт максимум
  • В принципе, частоту можно и увеличить, но в данном варианте, Selectel на 8 февраля 2025 будет съедать 409`634 рублей в месяц.

Отказоустойчивость

Можно заметить, записи на диск намного больше, чем чтений, из-за этого сразу хочется применить паттерн CQRS (Command and Query Responsibility Segregation). Например, сохранять данные будут 42 пода, а читать 10, это намного выгоднее.

Решено использовать Temporal.io — это платформа для создания устойчивых и масштабируемых процессов в распределённых системах на разных платформах Java, NodeJs и прочих. Она позволяет создавать надёжные и согласованные оркестрации, которые продолжают работать даже в случае сбоев или ошибок. Удобный UI позволяет отслеживать ошибки, время выполнения, это очень важно в данном проекте.

Как отслеживать изменение стоимости активов в портфеле - NestJS, Temporal

Аналитические данные было решено хранить в ClickHouse. База данных обладает большим русским сообществом, хорошо себя показывала в прошлых проектах, берет AP из CAP теоремы.

Как отслеживать изменение стоимости активов в портфеле - NestJS, Temporal

Заключение

Получилось создать новую feature! Теперь пользователи моего pet-проекта могут получать временную аналитику, а я научился работать с Temporal.

2
Начать дискуссию