1+ import { getMasterDbClient , getReadReplicaDbClient } from '../database/client'
2+ import { IKPICollector , SnapshotService } from './services/snapshot-service'
13import { createDashboardRouter } from './api/dashboard-router'
24import { createLogger } from '../factories/logger-factory'
35import { DashboardServiceConfig } from './config'
46import { DashboardWebSocketHub } from './ws/dashboard-ws-hub'
57import express from 'express'
68import { getHealthRequestHandler } from './handlers/request-handlers/get-health-request-handler'
79import http from 'http'
10+ import { IncrementalKPICollectorService } from './services/incremental-kpi-collector-service'
11+ import { KPICollectorService } from './services/kpi-collector-service'
812import { PollingScheduler } from './polling/polling-scheduler'
9- import { SnapshotService } from './services/snapshot -service'
13+ import { StatefulIncrementalKPICollectorService } from './services/stateful-incremental -service'
1014import { WebSocketServer } from 'ws'
11-
1215const debug = createLogger ( 'dashboard-service:app' )
1316
1417export interface DashboardService {
@@ -22,14 +25,36 @@ export interface DashboardService {
2225
2326export const createDashboardService = ( config : DashboardServiceConfig ) : DashboardService => {
2427 console . info (
25- 'dashboard-service: creating service (host=%s, port=%d, wsPath=%s, pollIntervalMs=%d)' ,
28+ 'dashboard-service: creating service (host=%s, port=%d, wsPath=%s, pollIntervalMs=%d, useDummyData=%s, collectorMode=%s )' ,
2629 config . host ,
2730 config . port ,
2831 config . wsPath ,
2932 config . pollIntervalMs ,
33+ config . useDummyData ,
34+ config . collectorMode ,
3035 )
3136
32- const snapshotService = new SnapshotService ( )
37+ const collector : IKPICollector = config . useDummyData
38+ ? {
39+ collectMetrics : async ( ) => ( {
40+ eventsByKind : [ ] ,
41+ admittedUsers : 0 ,
42+ satsPaid : 0 ,
43+ topTalkers : { allTime : [ ] , recent : [ ] } ,
44+ } ) ,
45+ }
46+ : ( ( ) => {
47+ if ( config . collectorMode === 'stateful-incremental' ) {
48+ return new StatefulIncrementalKPICollectorService ( getMasterDbClient ( ) )
49+ }
50+
51+ const dbClient = getReadReplicaDbClient ( )
52+ return config . collectorMode === 'incremental'
53+ ? new IncrementalKPICollectorService ( dbClient )
54+ : new KPICollectorService ( dbClient )
55+ } ) ( )
56+
57+ const snapshotService = new SnapshotService ( collector )
3358
3459 const app = express ( )
3560 . disable ( 'x-powered-by' )
@@ -44,11 +69,17 @@ export const createDashboardService = (config: DashboardServiceConfig): Dashboar
4469
4570 const webSocketHub = new DashboardWebSocketHub ( webSocketServer , ( ) => snapshotService . getSnapshot ( ) )
4671
47- const pollingScheduler = new PollingScheduler ( config . pollIntervalMs , ( ) => {
48- const nextSnapshot = snapshotService . refreshPlaceholder ( )
49- debug ( 'poll tick produced snapshot sequence=%d' , nextSnapshot . sequence )
50- webSocketHub . broadcastTick ( nextSnapshot . sequence )
51- webSocketHub . broadcastSnapshot ( nextSnapshot )
72+ const pollingScheduler = new PollingScheduler ( config . pollIntervalMs , async ( ) => {
73+ const { snapshot, changed } = await snapshotService . refresh ( )
74+
75+ if ( ! changed ) {
76+ debug ( 'poll tick detected no KPI changes' )
77+ return
78+ }
79+
80+ debug ( 'poll tick produced snapshot sequence=%d status=%s' , snapshot . sequence , snapshot . status )
81+ webSocketHub . broadcastTick ( snapshot . sequence )
82+ webSocketHub . broadcastSnapshot ( snapshot )
5283 } )
5384
5485 const start = async ( ) => {
@@ -72,13 +103,31 @@ export const createDashboardService = (config: DashboardServiceConfig): Dashboar
72103 } )
73104 } )
74105
106+ try {
107+ const initialSnapshotRefresh = await snapshotService . refresh ( )
108+ if ( initialSnapshotRefresh . changed ) {
109+ debug ( 'initial snapshot prepared with sequence=%d status=%s' , initialSnapshotRefresh . snapshot . sequence , initialSnapshotRefresh . snapshot . status )
110+ }
111+ } catch ( error ) {
112+ console . error ( 'dashboard-service: initial snapshot refresh failed (will retry on next poll)' , error )
113+ }
114+
75115 pollingScheduler . start ( )
76116 console . info ( 'dashboard-service: polling scheduler started' )
77117 }
78118
79119 const stop = async ( ) => {
80120 console . info ( 'dashboard-service: stopping service' )
81121 pollingScheduler . stop ( )
122+
123+ if ( collector ?. close ) {
124+ try {
125+ await collector . close ( )
126+ } catch ( error ) {
127+ console . error ( 'dashboard-service: failed to close collector resources' , error )
128+ }
129+ }
130+
82131 webSocketHub . close ( )
83132 await new Promise < void > ( ( resolve , reject ) => {
84133 if ( ! webServer . listening ) {
0 commit comments