From 3b919ecdc6af42f54d2b7541533c51f5e35a99b4 Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Thu, 25 Jun 2026 20:25:53 +0300 Subject: [PATCH] perf(backend): add dedicated screenerTtl config for screener caching - Add CACHE_SCREENER_TTL env var (default 900s) to configuration - Move screener from marketDataTtl to dedicated screenerTtl - Add test verifying cache key prefix, parts, and TTL config key - 117 tests pass, build succeeds --- apps/backend/src/config/configuration.ts | 1 + .../securities/screener.service.spec.ts | 41 +++++++++++++------ .../modules/securities/screener.service.ts | 2 +- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/apps/backend/src/config/configuration.ts b/apps/backend/src/config/configuration.ts index 9eaf969..6544673 100644 --- a/apps/backend/src/config/configuration.ts +++ b/apps/backend/src/config/configuration.ts @@ -29,6 +29,7 @@ export default registerAs('app', () => ({ candlesTtl: parseInt(process.env.CACHE_CANDLES_TTL || '3600', 10), securityTtl: parseInt(process.env.CACHE_SECURITY_TTL || '86400', 10), searchTtl: parseInt(process.env.CACHE_SEARCH_TTL || '3600', 10), + screenerTtl: parseInt(process.env.CACHE_SCREENER_TTL || '900', 10), dividendsTtl: parseInt(process.env.CACHE_DIVIDENDS_TTL || '86400', 10), tbankAccountsTtl: parseInt(process.env.CACHE_TBANK_ACCOUNTS_TTL || '3600', 10), tbankPortfolioTtl: parseInt(process.env.CACHE_TBANK_PORTFOLIO_TTL || '60', 10), diff --git a/apps/backend/src/modules/securities/screener.service.spec.ts b/apps/backend/src/modules/securities/screener.service.spec.ts index dc627d5..db70320 100644 --- a/apps/backend/src/modules/securities/screener.service.spec.ts +++ b/apps/backend/src/modules/securities/screener.service.spec.ts @@ -7,24 +7,15 @@ import { ScreenerType } from './dto/screener-query.dto'; describe('ScreenerService', () => { let service: ScreenerService; let cache: CacheService; + const moexClient = { getShareMarketDataBatch: vi.fn(), getBondPositionDataBatch: vi.fn() }; beforeEach(async () => { + vi.clearAllMocks(); const module: TestingModule = await Test.createTestingModule({ providers: [ ScreenerService, - { - provide: MoexClientService, - useValue: { - getShareMarketDataBatch: vi.fn(), - getBondPositionDataBatch: vi.fn(), - }, - }, - { - provide: CacheService, - useValue: { - getOrFetch: vi.fn(), - }, - }, + { provide: MoexClientService, useValue: moexClient }, + { provide: CacheService, useValue: { getOrFetch: vi.fn() } }, ], }).compile(); @@ -37,6 +28,30 @@ describe('ScreenerService', () => { }); describe('screen', () => { + it('should cache full dataset with screenerTtl config', async () => { + const mockShares = [{ + secid: 'SBER', shortName: 'Sberbank', last: 250, volume: 1000000, + lastChange: 5, lastChangePrcnt: 2, issueCapitalization: 1e9, + }]; + + moexClient.getShareMarketDataBatch.mockResolvedValue(mockShares); + + vi.mocked(cache.getOrFetch).mockImplementation(async (_prefix, _keys, fetchFn) => ({ + data: await fetchFn(), + fromCache: false, + cachedAt: null, + })); + + await service.screen({ type: ScreenerType.SHARE }); + + expect(cache.getOrFetch).toHaveBeenCalledWith( + 'screener', + [ScreenerType.SHARE], + expect.any(Function), + 'screenerTtl', + ); + }); + it('should filter and sort shares', async () => { const mockShares = [ { diff --git a/apps/backend/src/modules/securities/screener.service.ts b/apps/backend/src/modules/securities/screener.service.ts index 9edb140..1138ac2 100644 --- a/apps/backend/src/modules/securities/screener.service.ts +++ b/apps/backend/src/modules/securities/screener.service.ts @@ -85,7 +85,7 @@ export class ScreenerService { ); } }, - 'marketDataTtl', + 'screenerTtl', ); return data;