From 1dc27a6e9b6af5faa97169abd54538bfeb8ba550 Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Fri, 19 Jun 2026 07:38:33 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B8=D0=BD=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=BA=20SDD=20=D0=BF=D0=BE=D0=B4=D1=85=D0=BE=D0=B4?= =?UTF-8?q?=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plan.md} | 0 .../plan.md} | 0 ...6-06-18-broker-performance-optimization.md | 88 ------------------- 3 files changed, 88 deletions(-) rename docs/{superpowers/plans/2026-06-17-broker-operations-ui-improvements.md => features/broker-operations-ui-improvements/plan.md} (100%) rename docs/{superpowers/plans/2026-06-17-broker-positions-pagination-and-loading.md => features/broker-positions-pagination-and-loading/plan.md} (100%) delete mode 100644 docs/superpowers/specs/2026-06-18-broker-performance-optimization.md diff --git a/docs/superpowers/plans/2026-06-17-broker-operations-ui-improvements.md b/docs/features/broker-operations-ui-improvements/plan.md similarity index 100% rename from docs/superpowers/plans/2026-06-17-broker-operations-ui-improvements.md rename to docs/features/broker-operations-ui-improvements/plan.md diff --git a/docs/superpowers/plans/2026-06-17-broker-positions-pagination-and-loading.md b/docs/features/broker-positions-pagination-and-loading/plan.md similarity index 100% rename from docs/superpowers/plans/2026-06-17-broker-positions-pagination-and-loading.md rename to docs/features/broker-positions-pagination-and-loading/plan.md diff --git a/docs/superpowers/specs/2026-06-18-broker-performance-optimization.md b/docs/superpowers/specs/2026-06-18-broker-performance-optimization.md deleted file mode 100644 index a024049..0000000 --- a/docs/superpowers/specs/2026-06-18-broker-performance-optimization.md +++ /dev/null @@ -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`