From 7d96c06bbf6e17b9caaececa93f93e3210df8f36 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 4 Aug 2026 12:30:29 -0400 Subject: [PATCH] Avoid retaining accounting summary promises --- .../share-accounting.service.ts | 43 ++++++++++++++----- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/src/ORM/share-accounting/share-accounting.service.ts b/src/ORM/share-accounting/share-accounting.service.ts index 0cb6723..b1401ce 100644 --- a/src/ORM/share-accounting/share-accounting.service.ts +++ b/src/ORM/share-accounting/share-accounting.service.ts @@ -102,6 +102,7 @@ export class ShareAccountingService implements OnModuleInit, OnModuleDestroy { private rollupTimer: NodeJS.Timeout | null = null; private activeRollup: Promise | null = null; private summaryCache = new Map(); + private summaryInFlight = new Map>(); private readonly poolSummaryCacheKey = 'accounting:pool-summary'; private readonly batchSize = this.readPositiveInt('SHARE_ACCOUNTING_BATCH_SIZE', DEFAULT_BATCH_SIZE); private readonly flushIntervalMs = this.readPositiveInt('SHARE_ACCOUNTING_FLUSH_INTERVAL_MS', DEFAULT_FLUSH_INTERVAL_MS); @@ -565,19 +566,32 @@ export class ShareAccountingService implements OnModuleInit, OnModuleDestroy { return cached.value; } - const value = this.loadCachedSummary(filter, cacheKey).catch(error => { + if (cached != null) { this.summaryCache.delete(cacheKey); - throw error; - }); - - if (this.summaryCacheMs > 0) { - this.summaryCache.set(cacheKey, { - expiresAt: now + this.summaryCacheMs, - value, - }); - this.trimSummaryCache(); } + const inFlight = this.summaryInFlight.get(cacheKey); + if (inFlight != null) { + return inFlight; + } + + const value = this.loadCachedSummary(filter, cacheKey) + .then(summary => { + if (this.summaryCacheMs > 0) { + this.summaryCache.set(cacheKey, { + expiresAt: Date.now() + this.summaryCacheMs, + value: summary, + }); + this.trimSummaryCache(); + } + return summary; + }) + .finally(() => { + this.summaryInFlight.delete(cacheKey); + }); + + this.summaryInFlight.set(cacheKey, value); + return value; } @@ -865,6 +879,13 @@ export class ShareAccountingService implements OnModuleInit, OnModuleDestroy { } private trimSummaryCache(): void { + const now = Date.now(); + for (const [key, entry] of this.summaryCache) { + if (entry.expiresAt <= now) { + this.summaryCache.delete(key); + } + } + while (this.summaryCache.size > this.summaryCacheMax) { const firstKey = this.summaryCache.keys().next().value; if (firstKey == null) { @@ -901,5 +922,5 @@ interface PendingShare { interface SummaryCacheEntry { expiresAt: number; - value: Promise; + value: ShareAccountingSummary; }