Implemente| Retention Window | Strategy | | ----------------- | ---------------------- | | < 7 days (recent) | All snapshots (1/hour) | | 7-90 days | Last snapshot per day | | > 90 days | Deleted |
Example (100 days of 1 snapshot/hour):
- Before: 2,376 snapshots (7 days × 24 + 83 days × 24 + 10 days × 24)
- After: ~251 snapshots (~89.4% storage reduction)
- Recent 7 days: 168 snapshots retained (full resolution)
- 7-90 days: 83 daily snapshots retained (1 per day)
- 90+ days: Deleted (240 snapshots removed) analytics snapshot compaction system that automatically reduces database storage costs by preserving recent high-frequency data while rolling up older snapshots into daily aggregates.
New Function: dbCompactAnalyticsSnapshots()
- Purpose: Performs the SQL compaction logic with two-phase deletion
- Parameters:
portfolioId: Target portfoliocutoffDays(default: 90): Delete all snapshots older than thisrecentDays(default: 7): Keep high-frequency data for this period
- Returns:
CompactionStatswith metrics (deletedCount, retainedCount, cutoffTimestamp) - Strategy:
- Phase 1: Delete all snapshots older than
cutoffDays - Phase 2: For snapshots between
cutoffDaysandrecentDays, keep only the last per day usingDISTINCT ON (DATE(timestamp))
- Phase 1: Delete all snapshots older than
New Methods:
compactAnalyticsForPortfolio(): Compact snapshots for a single portfolio- Validates
cutoffDays >= recentDays - Delegates to database function
- Logs success/failure with metrics
- Validates
compactAllPortfolios(): Compact all portfolios- Called by the BullMQ worker
- Iterates through all portfolios
- Aggregates results and logs summary
New Worker: analyticsCompactionWorker.ts
-
Processor:
processAnalyticsCompactionJob()- Extracts cutoffDays/recentDays from job data
- Calls
analyticsService.compactAllPortfolios() - Logs completion with portfolio counts and storage metrics
-
Functions:
startAnalyticsCompactionWorker(): Initialize worker singletonstopAnalyticsCompactionWorker(): Graceful shutdowngetAnalyticsCompactionWorkerStatus(): Runtime statussetAnalyticsCompactionSchedulerRegistered(): Scheduler flag
-
Configuration:
- Worker name:
analytics-compaction - Concurrency: 1 (sequential per portfolio)
- Queue: Created on-demand with default job options
- Worker name:
New Queue: analytics-compaction
- Added
QUEUE_NAMES.ANALYTICS_COMPACTION - Added
AnalyticsCompactionJobDatainterface - Added
getAnalyticsCompactionQueue()getter - Updated
closeAllQueues()to close compaction queue
New Schedule:
- Cron:
0 2 * * 0(Every Sunday at 02:00 UTC) - Job name:
scheduled-analytics-compaction - Repeatable job ID:
repeatable-analytics-compaction - Triggers automatically on schedule or can be called manually
Integration:
- Imports
getAnalyticsCompactionQueueand setter function - Registers repeatable job in
startQueueScheduler() - Cleans up on
stopQueueScheduler()
Updates:
- Added analytics compaction queue health check
- Added analytics compaction worker status monitoring
- Updated readiness report to include all queues and workers
- Readiness endpoint (
/ready,/readiness) reflects compaction subsystem status
Updates:
- Added
analytics-compactionto startup worker list - Updated logs to include new worker in active workers
Analytics Service Tests:
- Calls database function with correct parameters
- Uses default parameters when not provided
- Rejects invalid parameter combinations (cutoffDays < recentDays)
- Handles database errors gracefully
- Processes multiple portfolios
- Aggregates statistics correctly
- Handles empty portfolio lists
- Fails fast on first portfolio error (with proper error propagation)
Worker Tests:
- Calls compactAllPortfolios with default parameters
- Passes custom parameters through
- Handles empty results
- Propagates errors correctly
- Handles missing correlation IDs
No new API endpoints were added (scope limited as requested). The compaction runs automatically via scheduler.
Existing Endpoints Updated:
GET /readiness- Now includes analytics-compaction queue and worker statusGET /ready- Now includes analytics-compaction queue and worker statusGET /api/v1/system/status- Includes analytics-compaction worker runtime status
Log Levels:
-
INFO:
- Compaction cycle start (portfolio count, cutoffDays, recentDays)
- Per-portfolio compaction (deletedCount, retainedCount)
- Cycle completion with aggregated stats
-
ERROR:
- Compaction failures with portfolio ID and error details
- Parameter validation failures
Correlation Tracking:
- All jobs include
correlationIdfor request tracing - Logs propagate correlation ID for end-to-end tracing
Failure Cases:
- Database connection failure → BullMQ retries with exponential backoff (5s → 80s)
- Parameter validation → Rejected immediately with error
- Portfolio processing error → Entire batch fails (stops at first error)
- Redis unavailable → Queue/worker disabled gracefully
Failure Logs:
- Include portfolio ID, error message, and correlation ID
- Severity level appropriate to issue type
Three-Tier Retention:
| Age Range | Retention Policy |
|---|---|
| < 7 days (recent) | All snapshots (1/hour) |
| 7-90 days | Last snapshot per day |
| > 90 days | Deleted |
Example:
- Before: 2,568 snapshots (100 days of hourly data)
- After: ~180 snapshots (93% storage reduction)
- Recent 7 days: 168 snapshots retained
- 7-90 days: ~12 daily snapshots retained
- 90+ days: Deleted
backend/src/db/analyticsDb.ts- Added compaction DB functionbackend/src/services/analyticsService.ts- Added service methodsbackend/src/queue/queues.ts- Added queue and job data typebackend/src/queue/workers/analyticsCompactionWorker.ts- NEWbackend/src/queue/scheduler.ts- Added schedule registrationbackend/src/monitoring/readiness.ts- Added health checksbackend/src/config/startupConfig.ts- Updated startup logs
backend/src/queue/workers/analyticsCompactionWorker.ts- Worker implementationbackend/src/test/analyticsCompaction.test.ts- Service testsbackend/src/test/analyticsCompactionWorker.test.ts- Worker testsbackend/src/db/ANALYTICS_COMPACTION.md- Detailed documentation
Automatic:
- Runs every Sunday at 02:00 UTC via BullMQ scheduler
Manual (development):
// Direct service call
const stats = await analyticsService.compactAnalyticsForPortfolio(
"portfolio-id",
90, // cutoffDays
7, // recentDays
);
// All portfolios
const allStats = await analyticsService.compactAllPortfolios(90, 7);API/service behavior reachable through intended code path:
-
Compaction triggered via BullMQ worker processing scheduled jobs
-
Database functions executed correctly with SQL logic
-
Service aggregates results across portfolios
Failure cases return actionable responses/logs:
-
Parameter validation with specific error messages
-
Database failures logged with portfolio ID
-
Correlation ID tracking for tracing
Automated coverage for touched backend paths:
-
Unit tests for analytics service methods
-
Unit tests for worker processor function
-
Tests cover success paths, error cases, and edge cases
Changes within scope of issue:
-
Only backend changes (no API/frontend modifications)
-
Focused on analytics snapshot compaction
-
Preserves existing analytics functionality
- Admin API endpoint for manual compaction trigger
- Configurable retention policies per portfolio
- Archive to cold storage instead of delete
- Compaction metrics dashboard
- Rollup statistics (min/max/avg) instead of sampling