3
3
clap:: { Parser , Subcommand } ,
4
4
futures:: future:: { self , Either , FutureExt } ,
5
5
jsonrpsee:: http_client:: HttpClientBuilder ,
6
+ solana_client:: rpc_client:: RpcClientConfig ,
7
+ solana_rpc_client:: http_sender:: HttpSender ,
6
8
solana_sdk:: {
7
9
commitment_config:: CommitmentConfig ,
8
10
pubkey:: Pubkey ,
27
29
yellowstone_jet:: {
28
30
blockhash_queue:: BlockhashQueue ,
29
31
cluster_tpu_info:: { BlocklistUpdater , ClusterTpuInfo , LeadersSelector } ,
30
- config:: { load_config, ConfigJet , ConfigJetGatewayClient , ConfigMetricsUpstream } ,
32
+ config:: {
33
+ load_config, ConfigJet , ConfigJetGatewayClient , ConfigMetricsUpstream , RpcErrorStrategy ,
34
+ } ,
31
35
feature_flags:: FeatureSet ,
32
36
grpc_geyser:: { GeyserStreams , GeyserSubscriber } ,
33
37
grpc_jet:: GrpcServer ,
37
41
quic_solana:: ConnectionCache ,
38
42
rpc:: { rpc_admin:: RpcClient , rpc_solana_like:: RpcServerImpl , RpcServer , RpcServerType } ,
39
43
setup_tracing,
40
- stake:: StakeInfo ,
44
+ solana_rpc_utils:: { RetryRpcSender , RetryRpcSenderStrategy } ,
45
+ stake:: { self , spawn_cache_stake_info_map, StakeInfoMap } ,
41
46
task_group:: TaskGroup ,
42
47
transactions:: { GrpcRootedTxReceiver , SendTransactionsPool } ,
43
48
util:: { IdentityFlusherWaitGroup , PubkeySigner , ValueObserver , WaitShutdown } ,
@@ -153,6 +158,7 @@ async fn run_cmd_admin(config: ConfigJet, admin_cmd: ArgsCommandAdmin) -> anyhow
153
158
}
154
159
155
160
async fn spawn_jet_gw_listener (
161
+ stake_info : StakeInfoMap ,
156
162
jet_gw_config : ConfigJetGatewayClient ,
157
163
mut identity_observer : ValueObserver < PubkeySigner > ,
158
164
tx_sender : RpcServerImpl ,
@@ -166,6 +172,7 @@ async fn spawn_jet_gw_listener(
166
172
let features = features. clone ( ) ;
167
173
let mut identity_observer2 = identity_observer. clone ( ) ;
168
174
let ( stop_tx2, stop_rx2) = tokio:: sync:: oneshot:: channel ( ) ;
175
+ let stake_info2 = stake_info. clone ( ) ;
169
176
let fut = identity_observer. until_value_change ( move |current_identity| {
170
177
if let Some ( expected_identity) = expected_identity {
171
178
if current_identity. pubkey ( ) != expected_identity {
@@ -176,6 +183,7 @@ async fn spawn_jet_gw_listener(
176
183
} else {
177
184
GrpcServer :: run_with (
178
185
Arc :: new ( current_identity) ,
186
+ stake_info2,
179
187
jet_gw_config2. clone ( ) ,
180
188
tx_sender2. clone ( ) ,
181
189
features,
@@ -185,6 +193,7 @@ async fn spawn_jet_gw_listener(
185
193
} else {
186
194
GrpcServer :: run_with (
187
195
Arc :: new ( current_identity) ,
196
+ stake_info2,
188
197
jet_gw_config2. clone ( ) ,
189
198
tx_sender2. clone ( ) ,
190
199
features,
@@ -252,12 +261,89 @@ fn spawn_lewis_metric_subscriber(
252
261
} )
253
262
}
254
263
264
+ ///
265
+ /// This task keeps the stake metrics up to date for the current identity.
266
+ ///
267
+ async fn keep_stake_metrics_up_to_date_task (
268
+ mut stake_info_identity_observer : ValueObserver < Pubkey > ,
269
+ stake_info_map : StakeInfoMap ,
270
+ ) {
271
+ loop {
272
+ let current_identy = stake_info_identity_observer. get_current ( ) ;
273
+
274
+ let ( stake, total_stake) = stake_info_map
275
+ . get_stake_info_with_total_stake ( current_identy)
276
+ . unwrap_or ( ( 0 , 0 ) ) ;
277
+
278
+ let max_pps = stake:: stake_to_per100ms_limit ( stake, total_stake) ;
279
+ let max_streams = stake:: stake_to_max_stream ( stake, total_stake) ;
280
+
281
+ metrics:: cluster_identity_stake_set ( metrics:: ClusterIdentityStakeKind :: Jet , stake) ;
282
+ metrics:: cluster_identity_stake_set ( metrics:: ClusterIdentityStakeKind :: Total , total_stake) ;
283
+ metrics:: cluster_identity_stake_set (
284
+ metrics:: ClusterIdentityStakeKind :: MaxPermitPer100ms ,
285
+ max_pps,
286
+ ) ;
287
+ metrics:: cluster_identity_stake_set (
288
+ metrics:: ClusterIdentityStakeKind :: MaxStreams ,
289
+ max_streams,
290
+ ) ;
291
+
292
+ tokio:: select! {
293
+ _ = tokio:: time:: sleep( std:: time:: Duration :: from_secs( 30 ) ) => { }
294
+ _ = stake_info_identity_observer. observe( ) => { }
295
+ }
296
+ }
297
+ }
298
+
255
299
async fn run_jet ( config : ConfigJet ) -> anyhow:: Result < ( ) > {
256
300
metrics:: init ( ) ;
257
301
if let Some ( identity) = config. identity . expected {
258
302
metrics:: quic_set_identity_expected ( identity) ;
259
303
}
260
304
305
+ let retry_strategy = match config. upstream . rpc_on_error . clone ( ) {
306
+ RpcErrorStrategy :: Fixed { interval, retries } => Some ( RetryRpcSenderStrategy :: FixedDelay {
307
+ delay : interval,
308
+ max_retries : retries. get ( ) ,
309
+ } ) ,
310
+ RpcErrorStrategy :: Exponential {
311
+ base,
312
+ factor,
313
+ retries,
314
+ } => Some ( RetryRpcSenderStrategy :: ExponentialBackoff {
315
+ base,
316
+ exp : factor,
317
+ max_retries : retries. get ( ) ,
318
+ } ) ,
319
+ RpcErrorStrategy :: Fail => None ,
320
+ } ;
321
+
322
+ // We are building a special HttpSender that automatically retry on transient network failure.
323
+ // This allow client not to worry about retry logic.
324
+ let rpc_sender = HttpSender :: new ( config. upstream . rpc . clone ( ) ) ;
325
+ let rpc_client_config = RpcClientConfig :: with_commitment ( CommitmentConfig :: finalized ( ) ) ;
326
+ let rpc_client = match retry_strategy {
327
+ Some ( strategy) => {
328
+ let rpc_sender = RetryRpcSender :: new ( rpc_sender, strategy) ;
329
+ solana_client:: nonblocking:: rpc_client:: RpcClient :: new_sender (
330
+ rpc_sender,
331
+ rpc_client_config,
332
+ )
333
+ }
334
+ None => solana_client:: nonblocking:: rpc_client:: RpcClient :: new_sender (
335
+ rpc_sender,
336
+ rpc_client_config,
337
+ ) ,
338
+ } ;
339
+
340
+ let ( stake_info_map, stake_info_bg_fut) = spawn_cache_stake_info_map (
341
+ rpc_client,
342
+ config. upstream . stake_update_interval ,
343
+ stake:: SpawnMode :: Detached ,
344
+ )
345
+ . await ;
346
+
261
347
let leaders_selector = Arc :: new (
262
348
LeadersSelector :: new_from_blockchain (
263
349
config. upstream . rpc . clone ( ) ,
@@ -303,6 +389,7 @@ async fn run_jet(config: ConfigJet) -> anyhow::Result<()> {
303
389
let ( quic_session, quic_identity_man) = ConnectionCache :: new (
304
390
config. quic . clone ( ) ,
305
391
initial_identity,
392
+ stake_info_map. clone ( ) ,
306
393
identity_flusher_wg. clone ( ) ,
307
394
) ;
308
395
@@ -329,16 +416,7 @@ async fn run_jet(config: ConfigJet) -> anyhow::Result<()> {
329
416
. add_flusher ( Box :: new ( send_transactions. clone ( ) ) )
330
417
. await ;
331
418
332
- let rpc = solana_client:: nonblocking:: rpc_client:: RpcClient :: new_with_commitment (
333
- config. upstream . rpc . clone ( ) ,
334
- CommitmentConfig :: finalized ( ) ,
335
- ) ;
336
- let stake = StakeInfo :: new (
337
- rpc,
338
- config. upstream . stake_update_interval ,
339
- quic_identity_man. observe_identity_change ( ) ,
340
- ) ;
341
-
419
+ let stake_info_identity_observer = quic_identity_man. observe_identity_change ( ) ;
342
420
let quic_identity_observer = quic_identity_man. observe_signer_change ( ) ;
343
421
// Run RPC admin
344
422
let rpc_admin = RpcServer :: new (
@@ -383,8 +461,10 @@ async fn run_jet(config: ConfigJet) -> anyhow::Result<()> {
383
461
. expect ( "rpc server impl" ) ;
384
462
385
463
info ! ( "starting jet-gateway listener" ) ;
464
+ let stake_info = stake_info_map. clone ( ) ;
386
465
let h = tokio:: spawn ( async move {
387
466
spawn_jet_gw_listener (
467
+ stake_info,
388
468
jet_gw_config,
389
469
quic_identity_observer,
390
470
tx_sender,
@@ -407,6 +487,13 @@ async fn run_jet(config: ConfigJet) -> anyhow::Result<()> {
407
487
408
488
let mut tg = TaskGroup :: default ( ) ;
409
489
490
+ tg. spawn_cancelable ( "stake_cache_refresh_task" , stake_info_bg_fut) ;
491
+
492
+ tg. spawn_cancelable (
493
+ "stake_info_metrics_update" ,
494
+ keep_stake_metrics_up_to_date_task ( stake_info_identity_observer, stake_info_map. clone ( ) ) ,
495
+ ) ;
496
+
410
497
tg. spawn_cancelable ( "lewis" , async move {
411
498
lewis. await . expect ( "lewis" ) ;
412
499
} ) ;
@@ -445,17 +532,17 @@ async fn run_jet(config: ConfigJet) -> anyhow::Result<()> {
445
532
446
533
tg. spawn_cancelable ( "send_transactions_pool" , send_tx_pool_fut) ;
447
534
448
- tg. spawn_with_shutdown ( "stake" , |mut stop| async move {
449
- tokio:: select! {
450
- result = stake. clone( ) . wait_shutdown( ) => {
451
- result. expect( "stake" ) ;
452
- } ,
453
- _ = & mut stop => {
454
- stake. shutdown( ) ;
455
- stake. wait_shutdown( ) . await . expect( "stake shutdown" ) ;
456
- } ,
457
- }
458
- } ) ;
535
+ // tg.spawn_with_shutdown("stake", |mut stop| async move {
536
+ // tokio::select! {
537
+ // result = stake.clone().wait_shutdown() => {
538
+ // result.expect("stake");
539
+ // },
540
+ // _ = &mut stop => {
541
+ // stake.shutdown();
542
+ // stake.wait_shutdown().await.expect("stake shutdown");
543
+ // },
544
+ // }
545
+ // });
459
546
460
547
if let Some ( mut jet_gw_listener) = jet_gw_listener {
461
548
tg. spawn_with_shutdown ( "jet_gw_listener" , |mut stop| async move {
0 commit comments