11import { BlockInfo , ChronikClient , ConnectionStrategy , ScriptUtxo , Tx , WsConfig , WsEndpoint , WsMsgClient , WsSubScriptClient } from 'chronik-client'
22import { encodeCashAddress , decodeCashAddress } from 'ecashaddrjs'
33import { AddressWithTransaction , BlockchainInfo , TransactionDetails , ProcessedMessages , SubbedAddressesLog , SyncAndSubscriptionReturn , SubscriptionReturn , SimpleBlockInfo } from 'types/chronikTypes'
4- import { CHRONIK_MESSAGE_CACHE_DELAY , RESPONSE_MESSAGES , XEC_TIMESTAMP_THRESHOLD , XEC_NETWORK_ID , BCH_NETWORK_ID , BCH_TIMESTAMP_THRESHOLD , CHRONIK_FETCH_N_TXS_PER_PAGE , KeyValueT , NETWORK_IDS_FROM_SLUGS , SOCKET_MESSAGES , NETWORK_IDS , NETWORK_TICKERS , MainNetworkSlugsType , MAX_MEMPOOL_TXS_TO_PROCESS_AT_A_TIME , MEMPOOL_PROCESS_DELAY , CHRONIK_INITIALIZATION_DELAY , LATENCY_TEST_CHECK_DELAY , INITIAL_ADDRESS_SYNC_FETCH_CONCURRENTLY , TX_EMIT_BATCH_SIZE , DB_COMMIT_BATCH_SIZE , MAX_TXS_PER_ADDRESS } from 'constants/index'
4+ import { CHRONIK_MESSAGE_CACHE_DELAY , RESPONSE_MESSAGES , XEC_TIMESTAMP_THRESHOLD , XEC_NETWORK_ID , BCH_NETWORK_ID , BCH_TIMESTAMP_THRESHOLD , CHRONIK_FETCH_N_TXS_PER_PAGE , KeyValueT , NETWORK_IDS_FROM_SLUGS , SOCKET_MESSAGES , NETWORK_IDS , NETWORK_TICKERS , MainNetworkSlugsType , MAX_MEMPOOL_TXS_TO_PROCESS_AT_A_TIME , MEMPOOL_PROCESS_DELAY , CHRONIK_INITIALIZATION_DELAY , LATENCY_TEST_CHECK_DELAY , INITIAL_ADDRESS_SYNC_FETCH_CONCURRENTLY , TX_EMIT_BATCH_SIZE , DB_COMMIT_BATCH_SIZE , MAX_TXS_PER_ADDRESS , TX_BATCH_POLLING_DELAY } from 'constants/index'
55import { productionAddresses } from 'prisma-local/seeds/addresses'
66import prisma from 'prisma-local/clientInstance'
77import {
@@ -344,6 +344,9 @@ export class ChronikBlockchainClient {
344344
345345 console . log ( `${ logPrefix } >>> starting chronik fetching for ${ addressBatchSlice . length } addresses... (${ syncedAlready } /${ totalCount } synced)` )
346346
347+ // Track completed addresses
348+ const completedAddresses : string [ ] = [ ]
349+
347350 const perAddressWorkers = addressBatchSlice . map ( async ( address ) => {
348351 const addrLogPrefix = `${ logPrefix } > ${ address . address } :`
349352 const lastSyncedTimestampSeconds = this . getLastSyncTs ( address )
@@ -389,6 +392,7 @@ export class ChronikBlockchainClient {
389392 const newTxsInThisPage = pageTxs . length
390393 if ( newTxsInThisPage > 0 ) {
391394 chronikTxs . push ( ...pageTxs . map ( tx => ( { tx, address } ) ) )
395+ pageTxs = [ ]
392396 }
393397
394398 if ( oldestTs < lastSyncedTimestampSeconds ) {
@@ -404,22 +408,60 @@ export class ChronikBlockchainClient {
404408 if ( newTxs > 0 ) {
405409 console . log ( `${ addrLogPrefix } ${ newTxs } new txs.` )
406410 }
411+ completedAddresses . push ( address . address )
407412 } )
408413 syncedAlready += addressBatchSlice . length
409414
410- await Promise . all (
415+ // Start workers but don't wait - yield batches while they're running
416+ const workersPromise = Promise . all (
411417 perAddressWorkers . map ( async worker =>
412418 await worker . catch ( err => console . error ( `${ logPrefix } : address job failed: ${ err . message as string } ` ) )
413419 )
414420 )
415421
416- // Yield full TX batches when buffer reaches TX_EMIT_BATCH_SIZE
417- while ( chronikTxs . length >= TX_EMIT_BATCH_SIZE ) {
418- const chronikTxsSlice = chronikTxs . slice ( 0 , TX_EMIT_BATCH_SIZE )
419- chronikTxs = chronikTxs . slice ( TX_EMIT_BATCH_SIZE )
420- yield { chronikTxs : chronikTxsSlice , addressesSynced : [ ] }
422+ // Race between worker completion and periodic checks to yield batches incrementally
423+ let allWorkersDone = false
424+
425+ while ( ! allWorkersDone || chronikTxs . length > 0 ) {
426+ // Yield batches if buffer is large enough
427+ while ( chronikTxs . length >= TX_EMIT_BATCH_SIZE ) {
428+ const chronikTxsSlice = chronikTxs . splice ( 0 , TX_EMIT_BATCH_SIZE )
429+ yield { chronikTxs : chronikTxsSlice , addressesSynced : [ ] }
430+ }
431+
432+ // If workers are done, yield any remaining transactions (even if < batch size)
433+ if ( allWorkersDone && chronikTxs . length > 0 ) {
434+ // This clears chronikTxs so the below check for length === 0 is true
435+ const remaining = chronikTxs . splice ( 0 )
436+ yield { chronikTxs : remaining , addressesSynced : [ ] }
437+ }
438+
439+ // Yield completed addresses if any
440+ if ( completedAddresses . length > 0 ) {
441+ const completed = completedAddresses . splice ( 0 )
442+ yield { chronikTxs : [ ] , addressesSynced : completed }
443+ }
444+
445+ // If workers are done and no more transactions, break
446+ if ( allWorkersDone && chronikTxs . length === 0 ) {
447+ break
448+ }
449+
450+ // Wait a bit or until workers complete
451+ const raceResult = await Promise . race ( [
452+ workersPromise . then ( ( ) => true ) ,
453+ new Promise < boolean > ( resolve => setTimeout ( ( ) => resolve ( false ) , TX_BATCH_POLLING_DELAY ) )
454+ ] )
455+
456+ // Update flag if workers completed
457+ if ( raceResult ) {
458+ allWorkersDone = true
459+ }
421460 }
422461
462+ // Ensure all workers are finished
463+ await workersPromise
464+
423465 // Yield batch marker for completed address group
424466 yield { chronikTxs : [ ] , addressesSynced : lastBatchAddresses }
425467 }
@@ -836,19 +878,22 @@ export class ChronikBlockchainClient {
836878 }
837879
838880 if ( createdTxs . length > 0 ) {
839- const rawByHash = new Map ( commitTuples . map ( p => [ p . raw . txid , p . raw ] ) )
840881 const triggerBatch : BroadcastTxData [ ] = [ ]
841882 for ( const createdTx of createdTxs ) {
842- const raw = rawByHash . get ( createdTx . hash )
843- if ( raw == null ) {
883+ const tuple = commitTuples . find ( t => t . row . hash === createdTx . hash )
884+ if ( tuple == null ) {
844885 continue
845886 }
846- const bd = this . broadcastIncomingTx ( createdTx . address . address , raw , createdTx )
887+ const bd = this . broadcastIncomingTx ( createdTx . address . address , tuple . raw , createdTx )
847888 triggerBatch . push ( bd )
848889 }
849890 if ( runTriggers && triggerBatch . length > 0 ) {
850891 await executeTriggersBatch ( triggerBatch , this . networkId )
851892 }
893+
894+ // Release memory
895+ createdTxs . length = 0
896+ triggerBatch . length = 0
852897 }
853898
854899 // Get the latest timestamp of all committed transactions (including pre-existent) for each address.
@@ -933,11 +978,14 @@ export class ChronikBlockchainClient {
933978 }
934979
935980 toCommit . push ( ...tupleFromBatch )
981+ // Release memory
982+ tupleFromBatch . length = 0
936983
937984 if ( toCommit . length >= DB_COMMIT_BATCH_SIZE ) {
938- const commitPairs = toCommit . slice ( 0 , DB_COMMIT_BATCH_SIZE )
939- toCommit = toCommit . slice ( DB_COMMIT_BATCH_SIZE )
985+ const commitPairs = toCommit . splice ( 0 , DB_COMMIT_BATCH_SIZE )
940986 await this . commitTransactionsBatch ( commitPairs , productionAddressesIds , runTriggers )
987+ // Clear commitPairs
988+ commitPairs . length = 0
941989 }
942990 } catch ( err : any ) {
943991 console . error ( `${ this . CHRONIK_MSG_PREFIX } : ERROR in batch (scoped): ${ err . message as string } ` )
0 commit comments