perf: group catalog source reads by identity
Backend verify / commits (push) Skipped
Backend verify / security (push) Successful in 9s
Backend verify / verify (push) Failing after 11m54s

This commit was merged in pull request #40.
This commit is contained in:
2026-09-28 16:37:01 +03:00
committed by ksv741
parent b24433092b
commit 6cd9c2dd28
2 changed files with 58 additions and 2 deletions
@@ -123,11 +123,23 @@ export class CatalogDiscoveryRepository {
}
readSources(inputs: CatalogSource[]) {
// Share identity predicates across UIDs instead of preparing one OR per row.
const groups = new Map<string, CatalogSource[]>();
for (const input of inputs) {
const key = JSON.stringify([
input.kind,
sourceProvider(input),
input.kind === 'detail' ? input.instrumentType : (input.rangeKey ?? ''),
]);
const group = groups.get(key);
if (group) group.push(input);
else groups.set(key, [input]);
}
return this.prisma.tbankInstrumentSource.findMany({
where: {
provider,
OR: inputs.map((input) => ({
instrumentUid: input.uid,
OR: [...groups.values()].map(([input, ...rest]) => ({
instrumentUid: { in: [input, ...rest].map((entry) => entry.uid) },
kind: input.kind,
source: sourceProvider(input),
rangeKey:
+44
View File
@@ -178,6 +178,50 @@ describe('local stock/bond search (SQLite HTTP)', () => {
});
afterAll(async () => app?.close());
it('reads only requested source identities when batching UIDs, types and periods', async () => {
const repository = app.get(CatalogDiscoveryRepository);
const fetchedAt = new Date();
for (const uid of ['a', 'b']) {
await prisma.tbankInstrument.create({
data: { provider: 'tbank', uid, instrumentType: 'share', metadata: {}, fetchedAt },
});
}
const expected = [
{ instrumentUid: 'a', source: 'tbank', kind: 'detail', rangeKey: 'share' },
{ instrumentUid: 'b', source: 'tbank', kind: 'detail', rangeKey: 'share' },
{ instrumentUid: 'a', source: 'tbank', kind: 'dividends', rangeKey: '2026' },
{ instrumentUid: 'b', source: 'tbank', kind: 'dividends', rangeKey: '2027' },
{ instrumentUid: 'b', source: 'moex', kind: 'bond-events', rangeKey: '' },
{ instrumentUid: 'a', source: 'ncr', kind: 'rating', rangeKey: '' },
];
const distractors = [
{ instrumentUid: 'a', source: 'tbank', kind: 'detail', rangeKey: 'bond' },
{ instrumentUid: 'b', source: 'tbank', kind: 'dividends', rangeKey: '2026' },
{ instrumentUid: 'a', source: 'tbank', kind: 'dividends', rangeKey: '2027' },
{ instrumentUid: 'a', source: 'moex', kind: 'bond-events', rangeKey: '' },
{ instrumentUid: 'b', source: 'tbank', kind: 'bond-events', rangeKey: '' },
{ instrumentUid: 'a', source: 'tbank', kind: 'rating', rangeKey: '' },
];
await prisma.tbankInstrumentSource.createMany({
data: [...expected, ...distractors].map((identity) => ({
...identity, provider: 'tbank', fetchedAt, payload: identity,
})),
});
const rows = await repository.readSources([
{ uid: 'a', instrumentType: 'share', kind: 'detail' },
{ uid: 'b', instrumentType: 'share', kind: 'detail' },
{ uid: 'a', instrumentType: 'share', kind: 'detail' },
{ uid: 'a', instrumentType: 'share', kind: 'dividends', rangeKey: '2026' },
{ uid: 'b', instrumentType: 'share', kind: 'dividends', rangeKey: '2027' },
{ uid: 'b', instrumentType: 'bond', kind: 'bond-events' },
{ uid: 'a', instrumentType: 'bond', kind: 'rating' },
{ uid: 'missing', instrumentType: 'share', kind: 'detail' },
]);
expect(rows.map((row) => row.payload)).toEqual(expect.arrayContaining(expected));
expect(rows).toHaveLength(expected.length);
expect(await repository.readSources([])).toEqual([]);
});
it('returns missing without waiting for discovery, then partial while enrichment is blocked; observe never launches work', async () => {
const found = deferred<(typeof share)[]>();
const detail = deferred<typeof share>();