@@ -133,11 +133,11 @@ export class SDKClient {
133133 * This limiter tracks hbar expenses and limits.
134134 * @private
135135 */
136- private readonly hbarLimiter : HbarLimit ;
136+ private static hbarLimiter : HbarLimit ;
137137
138- private consensusNodeClientHistogramCost ;
139- private consensusNodeClientHistogramGasFee ;
140- private operatorAccountGauge ;
138+ private static consensusNodeClientHistogramCost ;
139+ private static consensusNodeClientHistogramGasFee ;
140+ private static operatorAccountGauge ;
141141 private operatorAccountId ;
142142
143143 // populate with consensusnode requests via SDK
@@ -154,49 +154,59 @@ export class SDKClient {
154154 this . register = register ;
155155 this . operatorAccountId = clientMain . operatorAccountId ? clientMain . operatorAccountId . toString ( ) : 'UNKNOWN' ;
156156
157- // clear and create metrics in registry
158- const metricHistogramCost = 'rpc_relay_consensusnode_response' ;
159- register . removeSingleMetric ( metricHistogramCost ) ;
160- this . consensusNodeClientHistogramCost = new Histogram ( {
161- name : metricHistogramCost ,
162- help : 'Relay consensusnode mode type status cost histogram' ,
163- labelNames : [ 'mode' , 'type' , 'status' , 'caller' , 'interactingEntity' ] ,
164- registers : [ register ]
165- } ) ;
166- const metricHistogramGasFee = 'rpc_relay_consensusnode_gasfee' ;
167- register . removeSingleMetric ( metricHistogramGasFee ) ;
168- this . consensusNodeClientHistogramGasFee = new Histogram ( {
169- name : metricHistogramGasFee ,
170- help : 'Relay consensusnode mode type status gas fee histogram' ,
171- labelNames : [ 'mode' , 'type' , 'status' , 'caller' , 'interactingEntity' ] ,
172- registers : [ register ]
173- } ) ;
174-
175- const metricGaugeName = 'rpc_relay_operator_balance' ;
176- register . removeSingleMetric ( metricGaugeName ) ;
177- this . operatorAccountGauge = new Gauge ( {
178- name : metricGaugeName ,
179- help : 'Relay operator balance gauge' ,
180- labelNames : [ 'mode' , 'type' , 'accountId' ] ,
181- registers : [ register ] ,
182- async collect ( ) {
183- // Invoked when the registry collects its metrics' values.
184- // Allows for updated account balance tracking
185- try {
186- const accountBalance = await ( new AccountBalanceQuery ( )
187- . setAccountId ( clientMain . operatorAccountId ! ) )
188- . execute ( clientMain ) ;
189- this . labels ( { 'accountId' : clientMain . operatorAccountId ! . toString ( ) } )
190- . set ( accountBalance . hbars . toTinybars ( ) . toNumber ( ) ) ;
191- } catch ( e : any ) {
192- logger . error ( e , `Error collecting operator balance. Skipping balance set` ) ;
193- }
194- } ,
195- } ) ;
157+ // Long lived instances were moved to a singleton private static instance of this class, so we can reuse them among all instances
158+ if ( SDKClient . consensusNodeClientHistogramCost === undefined ) {
159+ // clear and create metrics in registry
160+ const metricHistogramCost = 'rpc_relay_consensusnode_response' ;
161+ register . removeSingleMetric ( metricHistogramCost ) ;
162+ SDKClient . consensusNodeClientHistogramCost = new Histogram ( {
163+ name : metricHistogramCost ,
164+ help : 'Relay consensusnode mode type status cost histogram' ,
165+ labelNames : [ 'mode' , 'type' , 'status' , 'caller' , 'interactingEntity' ] ,
166+ registers : [ register ]
167+ } ) ;
168+ }
169+
170+ if ( SDKClient . consensusNodeClientHistogramGasFee === undefined ) {
171+ const metricHistogramGasFee = 'rpc_relay_consensusnode_gasfee' ;
172+ register . removeSingleMetric ( metricHistogramGasFee ) ;
173+ SDKClient . consensusNodeClientHistogramGasFee = new Histogram ( {
174+ name : metricHistogramGasFee ,
175+ help : 'Relay consensusnode mode type status gas fee histogram' ,
176+ labelNames : [ 'mode' , 'type' , 'status' , 'caller' , 'interactingEntity' ] ,
177+ registers : [ register ]
178+ } ) ;
179+ }
180+
181+ if ( SDKClient . operatorAccountGauge === undefined ) {
182+ const metricGaugeName = 'rpc_relay_operator_balance' ;
183+ register . removeSingleMetric ( metricGaugeName ) ;
184+ SDKClient . operatorAccountGauge = new Gauge ( {
185+ name : metricGaugeName ,
186+ help : 'Relay operator balance gauge' ,
187+ labelNames : [ 'mode' , 'type' , 'accountId' ] ,
188+ registers : [ register ] ,
189+ async collect ( ) {
190+ // Invoked when the registry collects its metrics' values.
191+ // Allows for updated account balance tracking
192+ try {
193+ const accountBalance = await ( new AccountBalanceQuery ( )
194+ . setAccountId ( clientMain . operatorAccountId ! ) )
195+ . execute ( clientMain ) ;
196+ this . labels ( { 'accountId' : clientMain . operatorAccountId ! . toString ( ) } )
197+ . set ( accountBalance . hbars . toTinybars ( ) . toNumber ( ) ) ;
198+ } catch ( e : any ) {
199+ logger . error ( e , `Error collecting operator balance. Skipping balance set` ) ;
200+ }
201+ } ,
202+ } ) ;
203+ }
196204
197- const duration = parseInt ( process . env . HBAR_RATE_LIMIT_DURATION ! ) ;
198- const total = parseInt ( process . env . HBAR_RATE_LIMIT_TINYBAR ! ) ;
199- this . hbarLimiter = new HbarLimit ( logger . child ( { name : 'hbar-rate-limit' } ) , Date . now ( ) , total , duration , register ) ;
205+ if ( SDKClient . hbarLimiter === undefined ) {
206+ const duration = parseInt ( process . env . HBAR_RATE_LIMIT_DURATION ! ) ;
207+ const total = parseInt ( process . env . HBAR_RATE_LIMIT_TINYBAR ! ) ;
208+ SDKClient . hbarLimiter = new HbarLimit ( logger . child ( { name : 'hbar-rate-limit' } ) , Date . now ( ) , total , duration , register ) ;
209+ }
200210 }
201211
202212 async getAccountBalance ( account : string , callerName : string , requestId ?: string ) : Promise < AccountBalance > {
@@ -389,7 +399,7 @@ export class SDKClient {
389399 const requestIdPrefix = formatRequestIdMessage ( requestId ) ;
390400 const currentDateNow = Date . now ( ) ;
391401 try {
392- const shouldLimit = this . hbarLimiter . shouldLimit ( currentDateNow , SDKClient . queryMode , callerName ) ;
402+ const shouldLimit = SDKClient . hbarLimiter . shouldLimit ( currentDateNow , SDKClient . queryMode , callerName ) ;
393403 if ( shouldLimit ) {
394404 throw predefined . HBAR_RATE_LIMIT_EXCEEDED ;
395405 }
@@ -400,7 +410,7 @@ export class SDKClient {
400410 const res = await this . increaseCostAndRetryExecution ( query , baseCost , client , 3 , 0 , requestId ) ;
401411 resp = res . resp ;
402412 cost = res . cost . toTinybars ( ) . toNumber ( ) ;
403- this . hbarLimiter . addExpense ( cost , currentDateNow ) ;
413+ SDKClient . hbarLimiter . addExpense ( cost , currentDateNow ) ;
404414 }
405415 else {
406416 resp = await query . execute ( client ) ;
@@ -431,7 +441,7 @@ export class SDKClient {
431441 interactingEntity ) ;
432442 this . logger . trace ( `${ requestIdPrefix } ${ query . paymentTransactionId } ${ callerName } ${ query . constructor . name } status: ${ sdkClientError . status } (${ sdkClientError . status . _code } ), cost: ${ query . _queryPayment } ` ) ;
433443 if ( cost ) {
434- this . hbarLimiter . addExpense ( cost , currentDateNow ) ;
444+ SDKClient . hbarLimiter . addExpense ( cost , currentDateNow ) ;
435445 }
436446
437447 if ( e instanceof PrecheckStatusError && e . contractFunctionResult ?. errorMessage ) {
@@ -454,7 +464,7 @@ export class SDKClient {
454464 const requestIdPrefix = formatRequestIdMessage ( requestId ) ;
455465 const currentDateNow = Date . now ( ) ;
456466 try {
457- const shouldLimit = this . hbarLimiter . shouldLimit ( currentDateNow , SDKClient . transactionMode , callerName ) ;
467+ const shouldLimit = SDKClient . hbarLimiter . shouldLimit ( currentDateNow , SDKClient . transactionMode , callerName ) ;
458468 if ( shouldLimit ) {
459469 throw predefined . HBAR_RATE_LIMIT_EXCEEDED ;
460470 }
@@ -488,7 +498,7 @@ export class SDKClient {
488498 callerName ,
489499 interactingEntity ) ;
490500
491- this . hbarLimiter . addExpense ( transactionFee . toTinybars ( ) . toNumber ( ) , currentDateNow ) ;
501+ SDKClient . hbarLimiter . addExpense ( transactionFee . toTinybars ( ) . toNumber ( ) , currentDateNow ) ;
492502 } catch ( err : any ) {
493503 const recordQueryError = new SDKClientError ( err , err . message ) ;
494504 this . logger . error ( recordQueryError , `${ requestIdPrefix } Error raised during TransactionRecordQuery for ${ transaction . transactionId } ` ) ;
@@ -510,14 +520,14 @@ export class SDKClient {
510520 if ( ! resp . getRecord ) {
511521 throw new SDKClientError ( { } , `${ requestIdPrefix } Invalid response format, expected record availability: ${ JSON . stringify ( resp ) } ` ) ;
512522 }
513- const shouldLimit = this . hbarLimiter . shouldLimit ( currentDateNow , SDKClient . recordMode , transactionName ) ;
523+ const shouldLimit = SDKClient . hbarLimiter . shouldLimit ( currentDateNow , SDKClient . recordMode , transactionName ) ;
514524 if ( shouldLimit ) {
515525 throw predefined . HBAR_RATE_LIMIT_EXCEEDED ;
516526 }
517527
518528 const transactionRecord : TransactionRecord = await resp . getRecord ( this . clientMain ) ;
519529 const cost = transactionRecord . transactionFee . toTinybars ( ) . toNumber ( ) ;
520- this . hbarLimiter . addExpense ( cost , currentDateNow ) ;
530+ SDKClient . hbarLimiter . addExpense ( cost , currentDateNow ) ;
521531 this . logger . info ( `${ requestIdPrefix } ${ resp . transactionId } ${ callerName } ${ transactionName } record status: ${ Status . Success } (${ Status . Success . _code } ), cost: ${ transactionRecord . transactionFee } ` ) ;
522532 this . captureMetrics (
523533 SDKClient . transactionMode ,
@@ -528,7 +538,7 @@ export class SDKClient {
528538 callerName ,
529539 interactingEntity ) ;
530540
531- this . hbarLimiter . addExpense ( cost , currentDateNow ) ;
541+ SDKClient . hbarLimiter . addExpense ( cost , currentDateNow ) ;
532542
533543 return transactionRecord ;
534544 }
@@ -555,7 +565,7 @@ export class SDKClient {
555565 callerName ,
556566 interactingEntity ) ;
557567
558- this . hbarLimiter . addExpense ( transactionFee . toTinybars ( ) . toNumber ( ) , currentDateNow ) ;
568+ SDKClient . hbarLimiter . addExpense ( transactionFee . toTinybars ( ) . toNumber ( ) , currentDateNow ) ;
559569 } catch ( err : any ) {
560570 const recordQueryError = new SDKClientError ( err , err . message ) ;
561571 this . logger . error ( recordQueryError , `${ requestIdPrefix } Error raised during TransactionRecordQuery for ${ resp . transactionId } ` ) ;
@@ -574,14 +584,14 @@ export class SDKClient {
574584 private captureMetrics = ( mode , type , status , cost , gas , caller , interactingEntity ) => {
575585 const resolvedCost = cost ? cost : 0 ;
576586 const resolvedGas = typeof gas === 'object' ? gas . toInt ( ) : 0 ;
577- this . consensusNodeClientHistogramCost . labels (
587+ SDKClient . consensusNodeClientHistogramCost . labels (
578588 mode ,
579589 type ,
580590 status ,
581591 caller ,
582592 interactingEntity )
583593 . observe ( resolvedCost ) ;
584- this . consensusNodeClientHistogramGasFee . labels (
594+ SDKClient . consensusNodeClientHistogramGasFee . labels (
585595 mode ,
586596 type ,
587597 status ,
0 commit comments