feat: добавил инструкции к SDD подходу
All checks were successful
CI / ci (push) Successful in 3m13s
All checks were successful
CI / ci (push) Successful in 3m13s
This commit is contained in:
parent
d8b3886130
commit
1dc27a6e9b
@ -1,88 +0,0 @@
|
||||
# Broker API Performance Optimization
|
||||
|
||||
## Проблема
|
||||
|
||||
- `GET /api/v1/broker/accounts/:id/portfolio` — **~5s**
|
||||
- `GET /api/v1/broker/accounts/:id/positions` — **1-3s**
|
||||
|
||||
## Диагностика
|
||||
|
||||
### 1. Мёртвый код в `getPortfolio`
|
||||
|
||||
`buildInstrumentMap` делает N gRPC вызовов `GetInstrumentBy` (по одному на каждый `instrumentUid` в портфеле), но результат **не используется** в `mapBrokerPortfolio`. Это чистое WASTE.
|
||||
|
||||
### 2. Блокирующий instrument enrichment в `getPositions`
|
||||
|
||||
`buildInstrumentMap` вызывает `findByInstrumentUid` для каждого инструмента через gRPC. Даже при `Promise.allSettled`, все вызовы проходят через единый `p-queue` с 5 req/s. Для 10-20 позиций = 2-4 секунды ожидания в очереди.
|
||||
|
||||
Из всех полей `GetInstrumentBy` `mapBrokerPosition` использует только `name` — `ticker`, `classCode`, `instrumentType` уже есть в ответе `PortfolioPosition` (proto fields 32, 33, 2).
|
||||
|
||||
### 3. Единый rate limiter
|
||||
|
||||
Один `p-queue` на 5 req/s для всех gRPC вызовов. Instrument lookups конкурируют за очередь с portfolio/operations запросами.
|
||||
|
||||
## Изменения
|
||||
|
||||
### Change 1: Убрать `buildInstrumentMap` из `getPortfolio`
|
||||
|
||||
**Файлы:** `broker-portfolio.service.ts`
|
||||
|
||||
Удалить вызов `buildInstrumentMap` и передачу `instruments` в `mapBrokerPortfolio`. Исключить `BrokerInstrumentsService` из зависимостей (если не используется больше нигде в сервисе).
|
||||
|
||||
### Change 2: Instrument enrichment из кэша без блокировки
|
||||
|
||||
**Файлы:** `broker-portfolio.service.ts`, `cache.service.ts`
|
||||
|
||||
- `buildInstrumentMap` пытается достать данные из кэша без триггера gRPC
|
||||
- Если данных нет — возвращаем `null` для имени (не блокируем ответ)
|
||||
- Новый метод `CacheService.getIfPresent(key)` — проверяет кэш без вызова fetchFn
|
||||
|
||||
### Change 3: Разделить rate limiter на 3 очереди
|
||||
|
||||
**Файлы:** `tbank-client.service.ts`
|
||||
|
||||
Заменить единый `p-queue` на:
|
||||
|
||||
| Очередь | Rate | Сервисы |
|
||||
|---|---|---|
|
||||
| `operationsQueue` | 5 req/s | OperationsService |
|
||||
| `instrumentsQueue` | 20 req/s | InstrumentsService |
|
||||
| `usersQueue` | 5 req/s | UsersService |
|
||||
|
||||
Метод `callUnary` принимает параметр `queueName`. Клиентские методы выбирают очередь по типу сервиса.
|
||||
|
||||
### Change 4: Увеличить rate limit по умолчанию
|
||||
|
||||
**Файлы:** `configuration.ts`
|
||||
|
||||
`rateLimitPerSecond` по умолчанию: 5 → 20.
|
||||
|
||||
### Change 5: Shared cache сырого `GetPortfolio`
|
||||
|
||||
**Файлы:** `broker-portfolio.service.ts`
|
||||
|
||||
Оба эндпоинта вызывают `GetPortfolio` с одинаковым `accountId`. Кэшировать сырой ответ отдельно (TTL 60s, ключ `tbank:raw-portfolio:{accountId}`), чтобы второй запрос в том же окне не дублировал вызов.
|
||||
|
||||
## Ожидаемый эффект
|
||||
|
||||
| Endpoint | До | После |
|
||||
|---|---|---|
|
||||
| Portfolio | ~5s | ~0.3-0.5s (2 параллельных gRPC, без instrument enrichment) |
|
||||
| Positions | 1-3s | ~0.2-0.3s (1 gRPC GetPortfolio, name из кэша / null) |
|
||||
|
||||
## Этапы реализации (по порядку)
|
||||
|
||||
1. Убрать `buildInstrumentMap` из `getPortfolio`
|
||||
2. `CacheService.getIfPresent()` для instrument enrichment в positions
|
||||
3. Разделить rate limiter на очереди
|
||||
4. Увеличить rate limit по умолчанию
|
||||
5. Shared cache сырого GetPortfolio
|
||||
|
||||
Каждый этап отдельным коммитом.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. Portfolio endpoint < 1s при тёплом кэше account/instrument, < 1.5s при холодном
|
||||
2. Positions endpoint < 0.5s при тёплом кэше, < 1s при холодном
|
||||
3. Все существующие тесты проходят
|
||||
4. Instrument name показывается если есть в кэше, иначе `null`
|
||||
Loading…
x
Reference in New Issue
Block a user