1- use anyhow:: { anyhow, Context } ;
1+ use anyhow:: { anyhow, bail , Context } ;
22use graph:: cheap_clone:: CheapClone ;
33use graph:: prelude:: rand:: { self , seq:: IteratorRandom } ;
44use std:: cmp:: Ordering ;
@@ -23,12 +23,26 @@ pub struct EthereumNetworkAdapter {
2323 limit : usize ,
2424}
2525
26- #[ derive( Clone ) ]
26+ impl EthereumNetworkAdapter {
27+ fn is_call_only ( & self ) -> bool {
28+ self . adapter . is_call_only ( )
29+ }
30+ }
31+
32+ #[ derive( Clone , Default ) ]
2733pub struct EthereumNetworkAdapters {
2834 pub adapters : Vec < EthereumNetworkAdapter > ,
35+ pub call_only_adapters : Vec < EthereumNetworkAdapter > ,
2936}
3037
3138impl EthereumNetworkAdapters {
39+ pub fn push_adapter ( & mut self , adapter : EthereumNetworkAdapter ) {
40+ if adapter. is_call_only ( ) {
41+ self . call_only_adapters . push ( adapter) ;
42+ } else {
43+ self . adapters . push ( adapter) ;
44+ }
45+ }
3246 pub fn all_cheapest_with (
3347 & self ,
3448 required_capabilities : & NodeCapabilities ,
@@ -73,6 +87,42 @@ impl EthereumNetworkAdapters {
7387 self . adapters
7488 . retain ( |adapter| adapter. adapter . provider ( ) != provider) ;
7589 }
90+
91+ pub fn call_or_cheapest (
92+ & self ,
93+ capabilities : Option < & NodeCapabilities > ,
94+ ) -> anyhow:: Result < Arc < EthereumAdapter > > {
95+ match self . call_only_adapter ( ) ? {
96+ Some ( adapter) => Ok ( adapter) ,
97+ None => self . cheapest_with ( capabilities. unwrap_or ( & NodeCapabilities {
98+ // Archive is required for call_only
99+ archive : true ,
100+ traces : false ,
101+ } ) ) ,
102+ }
103+ }
104+
105+ pub fn call_only_adapter ( & self ) -> anyhow:: Result < Option < Arc < EthereumAdapter > > > {
106+ if self . call_only_adapters . is_empty ( ) {
107+ return Ok ( None ) ;
108+ }
109+
110+ let adapters = self
111+ . call_only_adapters
112+ . iter ( )
113+ . min_by_key ( |x| Arc :: strong_count ( & x. adapter ) )
114+ . ok_or ( anyhow ! ( "no available call only endpoints" ) ) ?;
115+
116+ // TODO: This will probably blow up a lot sooner than [limit] amount of
117+ // subgraphs, since we probably use a few instances.
118+ if Arc :: strong_count ( & adapters. adapter ) >= adapters. limit {
119+ bail ! ( "call only adapter has reached the concurrency limit" ) ;
120+ }
121+
122+ // Cloning here ensure we have the correct count at any given time, if we return a reference it can be cloned later
123+ // which could cause a high number of endpoints to be given away before accounting for them.
124+ Ok ( Some ( adapters. adapter . clone ( ) ) )
125+ }
76126}
77127
78128#[ derive( Clone ) ]
@@ -97,8 +147,9 @@ impl EthereumNetworks {
97147 let network_adapters = self
98148 . networks
99149 . entry ( name)
100- . or_insert ( EthereumNetworkAdapters { adapters : vec ! [ ] } ) ;
101- network_adapters. adapters . push ( EthereumNetworkAdapter {
150+ . or_insert ( EthereumNetworkAdapters :: default ( ) ) ;
151+
152+ network_adapters. push_adapter ( EthereumNetworkAdapter {
102153 capabilities,
103154 adapter,
104155 limit,
@@ -160,6 +211,14 @@ impl EthereumNetworks {
160211
161212#[ cfg( test) ]
162213mod tests {
214+ use std:: sync:: Arc ;
215+
216+ use graph:: { prelude:: MetricsRegistry , tokio, url:: Url } ;
217+ use graph_mock:: MockMetricsRegistry ;
218+ use http:: HeaderMap ;
219+
220+ use crate :: { EthereumAdapter , EthereumNetworks , ProviderEthRpcMetrics , Transport } ;
221+
163222 use super :: NodeCapabilities ;
164223
165224 #[ test]
@@ -216,4 +275,104 @@ mod tests {
216275 assert_eq ! ( true , & full_traces >= & full) ;
217276 assert_eq ! ( true , & full_traces >= & full_traces) ;
218277 }
278+
279+ #[ tokio:: test]
280+ async fn adapter_selector_selects_eth_call ( ) {
281+ let chain = "mainnet" . to_string ( ) ;
282+ let logger = graph:: log:: logger ( true ) ;
283+ let mock_registry: Arc < dyn MetricsRegistry > = Arc :: new ( MockMetricsRegistry :: new ( ) ) ;
284+ let transport =
285+ Transport :: new_rpc ( Url :: parse ( "http://127.0.0.1" ) . unwrap ( ) , HeaderMap :: new ( ) ) ;
286+ let provider_metrics = Arc :: new ( ProviderEthRpcMetrics :: new ( mock_registry. clone ( ) ) ) ;
287+
288+ let eth_call_adapter = Arc :: new (
289+ EthereumAdapter :: new (
290+ logger. clone ( ) ,
291+ String :: new ( ) ,
292+ "http://127.0.0.1" ,
293+ transport. clone ( ) ,
294+ provider_metrics. clone ( ) ,
295+ true ,
296+ true ,
297+ )
298+ . await ,
299+ ) ;
300+
301+ let eth_adapter = Arc :: new (
302+ EthereumAdapter :: new (
303+ logger. clone ( ) ,
304+ String :: new ( ) ,
305+ "http://127.0.0.1" ,
306+ transport. clone ( ) ,
307+ provider_metrics. clone ( ) ,
308+ true ,
309+ false ,
310+ )
311+ . await ,
312+ ) ;
313+
314+ let mut adapters = {
315+ let mut ethereum_networks = EthereumNetworks :: new ( ) ;
316+ ethereum_networks. insert (
317+ chain. clone ( ) ,
318+ NodeCapabilities {
319+ archive : true ,
320+ traces : false ,
321+ } ,
322+ eth_call_adapter. clone ( ) ,
323+ 3 ,
324+ ) ;
325+ ethereum_networks. insert (
326+ chain. clone ( ) ,
327+ NodeCapabilities {
328+ archive : true ,
329+ traces : false ,
330+ } ,
331+ eth_adapter. clone ( ) ,
332+ 3 ,
333+ ) ;
334+ ethereum_networks. networks . get ( & chain) . unwrap ( ) . clone ( )
335+ } ;
336+ // one reference above and one inside adapters struct
337+ assert_eq ! ( Arc :: strong_count( & eth_call_adapter) , 2 ) ;
338+ assert_eq ! ( Arc :: strong_count( & eth_adapter) , 2 ) ;
339+
340+ {
341+ // Not Found
342+ assert ! ( adapters
343+ . cheapest_with( & NodeCapabilities {
344+ archive: false ,
345+ traces: true ,
346+ } )
347+ . is_err( ) ) ;
348+
349+ // Check cheapest is not call only
350+ let adapter = adapters
351+ . cheapest_with ( & NodeCapabilities {
352+ archive : true ,
353+ traces : false ,
354+ } )
355+ . unwrap ( ) ;
356+ assert_eq ! ( adapter. is_call_only( ) , false ) ;
357+ }
358+
359+ // Check limits
360+ {
361+ let adapter = adapters. call_or_cheapest ( None ) . unwrap ( ) ;
362+ assert ! ( adapter. is_call_only( ) ) ;
363+ assert ! ( adapters. call_or_cheapest( None ) . is_err( ) ) ;
364+ }
365+
366+ // Check empty falls back to call only
367+ {
368+ adapters. call_only_adapters = vec ! [ ] ;
369+ let adapter = adapters
370+ . call_or_cheapest ( Some ( & NodeCapabilities {
371+ archive : true ,
372+ traces : false ,
373+ } ) )
374+ . unwrap ( ) ;
375+ assert_eq ! ( adapter. is_call_only( ) , false ) ;
376+ }
377+ }
219378}
0 commit comments