Summary
The DiaBackendAssetRepository fetches ALL post-captures in a single API request and caches viewed media Blobs in an unbounded in-memory Map. Combined with cascading re-creation of DetailedCapture objects, this causes escalating memory pressure, network latency, and UI jank as user collections grow.
Affected Files
Primary — Unbounded data fetching
src/app/shared/dia-backend/asset/dia-backend-asset-repository.service.ts
- Lines 56-58:
postCapturesImageCache$ — an unbounded Map<string, Blob> with no eviction
- Lines 65-88:
postCaptures$ — fetches total count, then makes a single request with limit: count to download ALL post-captures
- Lines 131-148:
getAndCachePostCaptureMedia$() — stores full Blob objects (multi-MB each) with no max size
Secondary — Cascading object re-creation
src/app/features/home/post-capture-tab/post-capture-tab.component.ts
- Lines 24-33: Subscribes to
postCaptures$, triggering the full unbounded load
src/app/features/home/details/details.page.ts
- Lines 127-144:
fromPostCaptures$ — re-creates ALL DetailedCapture instances (each with 12+ observable properties) on every upstream emission
Impact
- Memory: Unbounded Blob cache grows to hundreds of MB for active users, causing OS-level app kills on memory-constrained devices
- Network: Single large API request causes 5-15+ second delays on mobile connections
- UI:
DetailedCapture object re-creation causes Swiper re-rendering and GC pressure, resulting in janky scrolling
- Backend: Single request for all assets puts unnecessary strain on the API server
Suggested Implementation
Phase 1: Paginated loading
Replace "fetch count then fetch all" with proper pagination:
private readonly PAGE_SIZE = 20;
fetchPostCaptures$(offset: number = 0) {
return this.list$({
orderBy: 'source_transaction',
limit: this.PAGE_SIZE,
offset,
});
}
Follow the existing pattern in CaptureTabComponent which already uses capturedTabPageIndex$ with itemsPerPage = 10.
Phase 2: LRU cache eviction
Replace unbounded Map<string, Blob> with a bounded LRU cache (max ~20 entries):
private readonly MAX_CACHE_SIZE = 20;
private evictOldestIfNeeded(cache: Map<string, Blob>) {
if (cache.size > this.MAX_CACHE_SIZE) {
const oldestKey = cache.keys().next().value;
if (oldestKey) cache.delete(oldestKey);
}
}
Phase 3: Stabilize DetailedCapture object identity
Cache DetailedCapture objects by ID to prevent unnecessary re-creation:
private detailedCaptureCache = new Map<string, DetailedCapture>();
References
Generated by Health Monitor with Omni
Summary
The
DiaBackendAssetRepositoryfetches ALL post-captures in a single API request and caches viewed media Blobs in an unbounded in-memory Map. Combined with cascading re-creation ofDetailedCaptureobjects, this causes escalating memory pressure, network latency, and UI jank as user collections grow.Affected Files
Primary — Unbounded data fetching
src/app/shared/dia-backend/asset/dia-backend-asset-repository.service.tspostCapturesImageCache$— an unboundedMap<string, Blob>with no evictionpostCaptures$— fetches total count, then makes a single request withlimit: countto download ALL post-capturesgetAndCachePostCaptureMedia$()— stores full Blob objects (multi-MB each) with no max sizeSecondary — Cascading object re-creation
src/app/features/home/post-capture-tab/post-capture-tab.component.tspostCaptures$, triggering the full unbounded loadsrc/app/features/home/details/details.page.tsfromPostCaptures$— re-creates ALLDetailedCaptureinstances (each with 12+ observable properties) on every upstream emissionImpact
DetailedCaptureobject re-creation causes Swiper re-rendering and GC pressure, resulting in janky scrollingSuggested Implementation
Phase 1: Paginated loading
Replace "fetch count then fetch all" with proper pagination:
Follow the existing pattern in
CaptureTabComponentwhich already usescapturedTabPageIndex$withitemsPerPage = 10.Phase 2: LRU cache eviction
Replace unbounded
Map<string, Blob>with a bounded LRU cache (max ~20 entries):Phase 3: Stabilize DetailedCapture object identity
Cache
DetailedCaptureobjects by ID to prevent unnecessary re-creation:References
URL.createObjectURLleaks, not Blob caching)Generated by Health Monitor with Omni