@@ -46,10 +46,14 @@ pub struct Config {
46
46
pub history_url : Url ,
47
47
pub relayer_urls : Vec < Url > ,
48
48
pub authorization_token : String ,
49
- #[ serde( with = "humantime_serde" ) ]
49
+ #[ serde( with = "humantime_serde" , default = "default_publish_interval" ) ]
50
50
pub publish_interval_duration : Duration ,
51
51
}
52
52
53
+ fn default_publish_interval ( ) -> Duration {
54
+ Duration :: from_millis ( 10 )
55
+ }
56
+
53
57
struct RelayerSender {
54
58
ws_senders : Vec < SplitSink < WebSocketStream < MaybeTlsStream < TcpStream > > , Message > > ,
55
59
}
@@ -111,20 +115,22 @@ async fn connect_to_relayers(
111
115
Ok ( ( sender, relayer_receivers) )
112
116
}
113
117
118
+ // TODO: This is copied from history-service; move to Lazer protocol sdk.
114
119
#[ derive( Deserialize ) ]
115
120
struct SymbolResponse {
116
- pub pyth_lazer_id : u32 ,
117
- pub name : String ,
118
- pub symbol : String ,
119
- pub description : String ,
120
- pub asset_type : String ,
121
- pub exponent : i32 ,
122
- pub cmc_id : Option < u32 > ,
123
- pub interval : Option < String > ,
124
- pub min_publishers : u16 ,
125
- pub min_channel : String ,
126
- pub state : String ,
127
- pub hermes_id : Option < String > ,
121
+ pub pyth_lazer_id : u32 ,
122
+ pub _name : String ,
123
+ pub _symbol : String ,
124
+ pub _description : String ,
125
+ pub _asset_type : String ,
126
+ pub _exponent : i16 ,
127
+ pub _cmc_id : Option < u32 > ,
128
+ pub _interval : Option < String > ,
129
+ pub _min_publishers : u16 ,
130
+ pub _min_channel : String ,
131
+ pub _state : String ,
132
+ pub _schedule : String ,
133
+ pub hermes_id : Option < String > ,
128
134
}
129
135
130
136
async fn fetch_symbols ( history_url : & Url ) -> Result < Vec < SymbolResponse > > {
@@ -138,7 +144,6 @@ async fn fetch_symbols(history_url: &Url) -> Result<Vec<SymbolResponse>> {
138
144
139
145
#[ instrument( skip( config, state) ) ]
140
146
pub fn lazer_exporter ( config : Config , state : Arc < state:: State > ) -> Vec < JoinHandle < ( ) > > {
141
- // TODO: add loop to handle relayer failure/retry
142
147
let mut handles = Vec :: new ( ) ;
143
148
handles. push ( tokio:: spawn ( lazer_exporter:: lazer_exporter (
144
149
config. clone ( ) ,
@@ -158,6 +163,7 @@ mod lazer_exporter {
158
163
} ,
159
164
state:: local:: LocalStore ,
160
165
} ,
166
+ anyhow:: bail,
161
167
futures_util:: StreamExt ,
162
168
pyth_lazer_protocol:: {
163
169
publisher:: PriceFeedDataV1 ,
@@ -185,21 +191,26 @@ mod lazer_exporter {
185
191
let retry_duration = Duration :: from_secs ( 1 ) ;
186
192
187
193
loop {
188
- run ( & config, state. clone ( ) ) . await ;
189
-
190
- failure_count += 1 ;
191
- tracing:: error!(
192
- "Lazer exporter failed {} times; retrying in {:?}" ,
193
- failure_count,
194
- retry_duration
195
- ) ;
196
- tokio:: time:: sleep ( retry_duration) . await ;
197
-
198
- // TODO: Back off or crash altogether on persistent failure
194
+ match run ( & config, state. clone ( ) ) . await {
195
+ Ok ( ( ) ) => {
196
+ tracing:: info!( "lazer_exporter graceful shutdown" ) ;
197
+ return ;
198
+ }
199
+ Err ( e) => {
200
+ failure_count += 1 ;
201
+ tracing:: error!(
202
+ "lazer_exporter failed with error: {:?}, failure_count: {}; retrying in {:?}" ,
203
+ e,
204
+ failure_count,
205
+ retry_duration
206
+ ) ;
207
+ tokio:: time:: sleep ( retry_duration) . await ;
208
+ }
209
+ }
199
210
}
200
211
}
201
212
202
- async fn run < S > ( config : & Config , state : Arc < S > )
213
+ async fn run < S > ( config : & Config , state : Arc < S > ) -> anyhow :: Result < ( ) >
203
214
where
204
215
S : LocalStore ,
205
216
S : Send + Sync + ' static ,
@@ -213,15 +224,13 @@ mod lazer_exporter {
213
224
. collect ( ) ,
214
225
Err ( e) => {
215
226
tracing:: error!( "Failed to fetch Lazer symbols: {e:?}" ) ;
216
- return ;
227
+ bail ! ( "Failed to fetch Lazer symbols: {e:?}" ) ;
217
228
}
218
229
} ;
219
230
220
231
// Establish relayer connections
221
232
// Relayer will drop the connection if no data received in 5s
222
- let ( mut relayer_sender, relayer_receivers) = connect_to_relayers ( & config)
223
- . await
224
- . expect ( "failed to connect to relayers" ) ;
233
+ let ( mut relayer_sender, relayer_receivers) = connect_to_relayers ( & config) . await ?;
225
234
let mut stream_map = StreamMap :: new ( ) ;
226
235
for ( i, receiver) in relayer_receivers. into_iter ( ) . enumerate ( ) {
227
236
stream_map. insert ( config. relayer_urls [ i] . clone ( ) , receiver) ;
@@ -232,6 +241,7 @@ mod lazer_exporter {
232
241
loop {
233
242
tokio:: select! {
234
243
_ = publish_interval. tick( ) => {
244
+ // TODO: This read locks and clones local::Store::prices, which may not meet performance needs.
235
245
for ( identifier, price_info) in state. get_all_price_infos( ) . await {
236
246
if let Some ( symbol) = lazer_symbols. get( & identifier. to_string( ) ) {
237
247
if let Err ( e) = relayer_sender. send_price_update( & PriceFeedDataV1 {
@@ -243,7 +253,7 @@ mod lazer_exporter {
243
253
source_timestamp_us: TimestampUs ( price_info. timestamp. and_utc( ) . timestamp_micros( ) as u64 ) ,
244
254
} ) . await {
245
255
tracing:: error!( "Error sending price update to relayer: {e:?}" ) ;
246
- return ;
256
+ bail! ( "Failed to send price update to relayer: {e:?}" ) ;
247
257
}
248
258
}
249
259
}
@@ -260,7 +270,7 @@ mod lazer_exporter {
260
270
None => {
261
271
// TODO: Probably still appropriate to return here, but retry in caller.
262
272
tracing:: error!( "relayer connection closed" ) ;
263
- return ;
273
+ bail! ( "relayer connection closed" ) ;
264
274
}
265
275
}
266
276
}
0 commit comments