Skip to main content

Architecture: hbf-stats

C4 Component Diagram

Key Flows

Stats Refresh Loop

The service runs a while (true) loop in AppService.start():

  1. Call HbfCoreService.getTenants() — returns tenants whose lastStatsUpdate is older than STATS_LIFESPAN_MINUTES.
  2. If no stale tenants, sleep for EXECUTION_TIME_INTERVAL_MILLISECONDS and repeat.
  3. Split stale tenants into batches of BATCH_SIZE. Process each batch with Promise.all.
  4. 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.getChatSessionsCount in parallel. d. Aggregate 30-day and previous-30-day rollups from the 60 daily records. e. Call HbfCoreService.updateTenantStats() to write the result back to hbf-core.
  5. Sleep for BATCH_COOLDOWN_SECONDS between batches to avoid overloading hbf-core.
  6. 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.