Architecture: hbf-stats
C4 Component Diagram
Key Flows
Stats Refresh Loop
The service runs a while (true) loop in AppService.start():
- Call
HbfCoreService.getTenants()— returns tenants whoselastStatsUpdateis older thanSTATS_LIFESPAN_MINUTES. - If no stale tenants, sleep for
EXECUTION_TIME_INTERVAL_MILLISECONDSand repeat. - Split stale tenants into batches of
BATCH_SIZE. Process each batch withPromise.all. - For each tenant:
a. Resolve organization timezone (cached per org within the run).
b. Build date-range windows: last 60 days (daily) and last 13 months (monthly).
c. Call
AnalyticsService.processExistingStats().- For each daily window, check if existing stats exist and are marked
completed. - Skip complete past days; fetch fresh data only for missing or today's (always incomplete) slot.
- Fetch:
SearchService.getSubscribers+SearchService.getChatSessionsCountin parallel. d. Aggregate 30-day and previous-30-day rollups from the 60 daily records. e. CallHbfCoreService.updateTenantStats()to write the result back to hbf-core.
- For each daily window, check if existing stats exist and are marked
- Sleep for
BATCH_COOLDOWN_SECONDSbetween batches to avoid overloading hbf-core. - Log any tenant failures, then repeat the outer loop.
Stats Data Shape
Stats written to hbf-core follow the Stats interface:
Stats {
lastStatsUpdate: number (timestamp ms)
data: {
last_30_days: CustomPeriodStats
previous_30_days: CustomPeriodStats
daily: DailyStats[] // 60 entries
monthly: MonthlyStats[] // 13 entries
}
}
Each DailyStats entry carries metadata.completed = false for today's slot so it is always refreshed on the next run.