1
- import { startIndexingChainToDataStores } from './chainTracker.js'
1
+ import { startIndexingMultipleCoordinators } from './chainTracker.js'
2
2
3
3
import Fastify , { FastifyRequest } from 'fastify'
4
4
import cors from '@fastify/cors'
@@ -62,8 +62,8 @@ async function main() {
62
62
fetch : makeRateLimitedFetch ( ) ,
63
63
} )
64
64
65
- const { coordinator , miningPool, cancel } =
66
- await startIndexingChainToDataStores (
65
+ const { coordinators , miningPool, cancel } =
66
+ await startIndexingMultipleCoordinators (
67
67
{
68
68
connection : coordinatorRpc ,
69
69
addressOverride : process . env . COORDINATOR_PROGRAM_ID ,
@@ -83,28 +83,31 @@ async function main() {
83
83
Set < ( runData : RunData ) => void >
84
84
> = new Map ( )
85
85
86
- coordinator . dataStore . eventEmitter . addListener ( 'update' , ( key ) => {
87
- const listeners = liveRunListeners . get ( key )
88
- if ( listeners ) {
89
- const [ programId , runId , index ] = getRunFromKey ( key )
90
- const runData = coordinator . dataStore . getRunDataById ( runId , index )
91
- if ( ! runData ) {
92
- console . warn (
93
- `Tried to emit updates for run ${ runId } but it has no data!`
94
- )
95
- return
96
- }
97
- for ( const listener of listeners ) {
98
- try {
99
- listener ( runData )
100
- } catch ( err ) {
101
- console . error (
102
- `Failed to send run data for run ${ runId } to subscribed client...`
86
+ // Set up event listeners for all coordinators
87
+ for ( const [ programId , coordinator ] of coordinators ) {
88
+ coordinator . dataStore . eventEmitter . addListener ( 'update' , ( key ) => {
89
+ const listeners = liveRunListeners . get ( key )
90
+ if ( listeners ) {
91
+ const [ programId , runId , index ] = getRunFromKey ( key )
92
+ const runData = coordinator . dataStore . getRunDataById ( runId , index )
93
+ if ( ! runData ) {
94
+ console . warn (
95
+ `Tried to emit updates for run ${ runId } from coordinator ${ programId } but it has no data!`
103
96
)
97
+ return
98
+ }
99
+ for ( const listener of listeners ) {
100
+ try {
101
+ listener ( runData )
102
+ } catch ( err ) {
103
+ console . error (
104
+ `Failed to send run data for run ${ runId } from coordinator ${ programId } to subscribed client...`
105
+ )
106
+ }
104
107
}
105
108
}
106
- }
107
- } )
109
+ } )
110
+ }
108
111
109
112
const liveMiningPoolListeners : Set < ( ) => void > = new Set ( )
110
113
miningPool . dataStore . eventEmitter . addListener ( 'update' , ( ) => {
@@ -127,15 +130,18 @@ async function main() {
127
130
console . log ( 'got shutdown signal, shutting down!' )
128
131
cancel ( )
129
132
await fastify . close ( )
130
- await Promise . all ( [ coordinator . stopped , miningPool . stopped ] )
133
+ const allCoordinatorPromises = Array . from ( coordinators . values ( ) ) . map ( c => c . stopped )
134
+ await Promise . all ( [ ...allCoordinatorPromises , miningPool . stopped ] )
131
135
process . exit ( 0 )
132
136
}
133
137
134
138
let coordinatorCrashed : Error | null = null
135
- coordinator . stopped . catch ( ( err ) => {
136
- console . error ( `[${ Date . now ( ) } ] coordinator broken: ` , err )
137
- coordinatorCrashed = new Error ( err )
138
- } )
139
+ for ( const [ programId , coordinator ] of coordinators ) {
140
+ coordinator . stopped . catch ( ( err ) => {
141
+ console . error ( `[${ Date . now ( ) } ] coordinator ${ programId } broken: ` , err )
142
+ coordinatorCrashed = new Error ( err )
143
+ } )
144
+ }
139
145
140
146
let miningPoolCrashed : Error | null = null
141
147
miningPool . stopped . catch ( ( err ) => {
@@ -215,8 +221,26 @@ async function main() {
215
221
)
216
222
217
223
fastify . get ( '/runs' , ( _req , res ) => {
224
+ // Aggregate runs from all coordinators
225
+ let allRuns : any [ ] = [ ]
226
+ let totalTokens = 0n
227
+ let totalTokensPerSecondActive = 0n
228
+
229
+ for ( const [ programId , coordinator ] of coordinators ) {
230
+ try {
231
+ const coordinatorSummary = coordinator . dataStore . getRunSummaries ( )
232
+ allRuns = allRuns . concat ( coordinatorSummary . runs )
233
+ totalTokens += coordinatorSummary . totalTokens
234
+ totalTokensPerSecondActive += coordinatorSummary . totalTokensPerSecondActive
235
+ } catch ( error ) {
236
+ console . error ( `Failed to get run summaries from coordinator ${ programId } :` , error )
237
+ }
238
+ }
239
+
218
240
const runs : ApiGetRuns = {
219
- ...coordinator . dataStore . getRunSummaries ( ) ,
241
+ runs : allRuns ,
242
+ totalTokens,
243
+ totalTokensPerSecondActive,
220
244
error : coordinatorCrashed ,
221
245
}
222
246
@@ -241,14 +265,29 @@ async function main() {
241
265
throw new Error ( `Invalid index ${ indexStr } ` )
242
266
}
243
267
244
- const matchingRun = runId
245
- ? coordinator . dataStore . getRunDataById ( runId , index )
246
- : null
268
+ // Search for the run across all coordinators
269
+ let matchingRun : any = null
270
+ let totalRuns = 0
271
+
272
+ if ( runId ) {
273
+ for ( const [ programId , coordinator ] of coordinators ) {
274
+ try {
275
+ const run = coordinator . dataStore . getRunDataById ( runId , index )
276
+ if ( run ) {
277
+ matchingRun = run
278
+ break // Found the run, stop searching
279
+ }
280
+ totalRuns += coordinator . dataStore . getNumRuns ( )
281
+ } catch ( error ) {
282
+ console . error ( `Failed to search for run in coordinator ${ programId } :` , error )
283
+ }
284
+ }
285
+ }
247
286
248
287
const data : ApiGetRun = {
249
288
run : matchingRun ,
250
289
error : coordinatorCrashed ,
251
- isOnlyRun : coordinator . dataStore . getNumRuns ( ) === 1 ,
290
+ isOnlyRun : totalRuns === 1 ,
252
291
}
253
292
254
293
// set header for streaming/non
@@ -263,7 +302,7 @@ async function main() {
263
302
}
264
303
265
304
const key = runKey (
266
- coordinator . dataStore . programId . toString ( ) ,
305
+ matchingRun . programId ,
267
306
matchingRun . info . id ,
268
307
matchingRun . info . index
269
308
)
@@ -301,24 +340,38 @@ async function main() {
301
340
)
302
341
303
342
fastify . get ( '/status' , async ( _ , res ) => {
343
+ // Aggregate status from all coordinators
344
+ const coordinatorStatuses : Record < string , any > = { }
345
+ for ( const [ programId , coordinator ] of coordinators ) {
346
+ try {
347
+ coordinatorStatuses [ programId ] = {
348
+ status : coordinatorCrashed ? coordinatorCrashed . toString ( ) : 'ok' ,
349
+ errors : coordinator . errors ,
350
+ trackedRuns : coordinator . dataStore
351
+ . getRunSummaries ( )
352
+ . runs . map ( ( r ) => ( { id : r . id , index : r . index , status : r . status , programId : r . programId } ) ) ,
353
+ chain : {
354
+ chainSlotHeight : await coordinatorRpc . getSlot ( 'confirmed' ) ,
355
+ indexedSlot :
356
+ coordinator . dataStore . lastUpdate ( ) . highestSignature ?. slot ?? 0 ,
357
+ programId : programId ,
358
+ networkGenesis : await coordinatorRpc . getGenesisHash ( ) ,
359
+ } ,
360
+ }
361
+ } catch ( error ) {
362
+ coordinatorStatuses [ programId ] = {
363
+ status : `error: ${ error } ` ,
364
+ errors : [ ] ,
365
+ trackedRuns : [ ] ,
366
+ chain : { programId }
367
+ }
368
+ }
369
+ }
370
+
304
371
const data = {
305
372
commit : process . env . GITCOMMIT ?? '???' ,
306
373
initTime,
307
- coordinator : {
308
- status : coordinatorCrashed ? coordinatorCrashed . toString ( ) : 'ok' ,
309
- errors : coordinator . errors ,
310
- trackedRuns : coordinator . dataStore
311
- . getRunSummaries ( )
312
- . runs . map ( ( r ) => ( { id : r . id , index : r . index , status : r . status } ) ) ,
313
- chain : {
314
- chainSlotHeight : await coordinatorRpc . getSlot ( 'confirmed' ) ,
315
- indexedSlot :
316
- coordinator . dataStore . lastUpdate ( ) . highestSignature ?. slot ?? 0 ,
317
- programId :
318
- process . env . COORDINATOR_PROGRAM_ID ?? coordinatorIdl . address ,
319
- networkGenesis : await coordinatorRpc . getGenesisHash ( ) ,
320
- } ,
321
- } ,
374
+ coordinators : coordinatorStatuses ,
322
375
miningPool : {
323
376
status : miningPoolCrashed ? miningPoolCrashed . toString ( ) : 'ok' ,
324
377
errors : miningPool . errors ,
0 commit comments