@@ -13,17 +13,19 @@ use solana_metrics::datapoint_info;
1313use solana_sdk:: account:: Account ;
1414use solana_sdk:: commitment_config:: CommitmentConfig ;
1515use solana_sdk:: pubkey:: Pubkey ;
16+ use std:: collections:: HashMap ;
1617use std:: collections:: HashSet ;
1718use std:: path:: PathBuf ;
1819use std:: str:: FromStr ;
20+ use std:: sync:: Arc ;
1921use std:: time:: Instant ;
22+ use tokio:: sync:: Mutex ;
2023use tokio:: task;
2124use tokio:: time:: Duration ;
2225use tokio_stream:: StreamExt ;
2326use tracing:: { debug, error, info, warn} ;
2427use tracing_subscriber:: { fmt, EnvFilter } ;
2528use url:: Url ;
26-
2729#[ derive( Debug , Serialize , Deserialize ) ]
2830struct PriceUpdate {
2931 #[ serde( rename = "type" ) ]
@@ -33,13 +35,15 @@ struct PriceUpdate {
3335
3436#[ derive( Debug , Serialize , Deserialize ) ]
3537struct PublisherPriceUpdate {
36- #[ serde( rename = "type" ) ]
37- update_type : String ,
3838 publisher : String ,
3939 feed_id : String ,
40- price_info : PriceInfo ,
40+ slot : u64 , // Add this field
41+ price : String ,
4142}
4243
44+ type PublisherKey = ( String , String ) ; // (feed_id, publisher)
45+ type PublisherBuffer = Arc < Mutex < HashMap < PublisherKey , PublisherPriceUpdate > > > ;
46+
4347#[ derive( Debug , Serialize , Deserialize ) ]
4448struct PriceFeed {
4549 id : String ,
@@ -99,6 +103,7 @@ struct Args {
99103
100104async fn fetch_price_updates ( jetstream : jetstream:: Context , config : & AppConfig ) -> Result < ( ) > {
101105 info ! ( "Starting Pyth reader" ) ;
106+ let publisher_buffer: PublisherBuffer = Arc :: new ( Mutex :: new ( HashMap :: new ( ) ) ) ;
102107 let client = PubsubClient :: new ( config. pyth . websocket_addr . as_str ( ) ) . await ?;
103108 info ! (
104109 "Connected to Pyth WebSocket at {}" ,
@@ -123,6 +128,47 @@ async fn fetch_price_updates(jetstream: jetstream::Context, config: &AppConfig)
123128 let mut update_count = 0 ;
124129 let mut unique_price_feeds = HashSet :: new ( ) ;
125130 let mut last_report_time = Instant :: now ( ) ;
131+ let jetstream_clone = jetstream. clone ( ) ;
132+ let buffer_clone = publisher_buffer. clone ( ) ;
133+ let mut msg_id_counter = 0 ;
134+ tokio:: spawn ( async move {
135+ let mut interval = tokio:: time:: interval ( Duration :: from_millis ( 100 ) ) ;
136+ loop {
137+ interval. tick ( ) . await ;
138+
139+ let updates: Vec < PublisherPriceUpdate > = {
140+ let mut buf = buffer_clone. lock ( ) . await ;
141+ if buf. is_empty ( ) {
142+ continue ;
143+ }
144+ buf. drain ( ) . map ( |( _, v) | v) . collect ( )
145+ } ; // <- lock released here
146+
147+ // Serialize as JSON array
148+ let body = match serde_json:: to_string ( & updates) {
149+ Ok ( b) => b,
150+ Err ( e) => {
151+ warn ! ( "Failed to serialize batch of publisher updates: {}" , e) ;
152+ continue ;
153+ }
154+ } ;
155+
156+ // Use a random ID as Nats-Msg-Id for the batch
157+ let msg_id = format ! ( "publisher_batch:{}" , msg_id_counter) ;
158+ msg_id_counter += 1 ;
159+ let mut headers = HeaderMap :: new ( ) ;
160+ headers. insert ( "Nats-Msg-Id" , msg_id. as_str ( ) ) ;
161+ info ! ( "body: {:#?},msg_id: {}" , body. len( ) , msg_id) ;
162+ if let Err ( e) = jetstream_clone
163+ . publish_with_headers ( "pyth.publisher.updates" , headers, body. into ( ) )
164+ . await
165+ {
166+ warn ! ( "Failed to publish batch publisher updates: {}" , e) ;
167+ } else {
168+ debug ! ( "Published {} publisher updates in a batch" , updates. len( ) ) ;
169+ }
170+ }
171+ } ) ;
126172
127173 while let Some ( update) = notif. next ( ) . await {
128174 debug ! ( "Received price update" ) ;
@@ -133,13 +179,15 @@ async fn fetch_price_updates(jetstream: jetstream::Context, config: &AppConfig)
133179 continue ;
134180 }
135181 } ;
182+
136183 let price_account: PythnetPriceAccount = match load_price_account ( & account. data ) {
137184 Ok ( pyth_account) => * pyth_account,
138185 Err ( _) => {
139186 debug ! ( "Not a price account, skipping" ) ;
140187 continue ;
141188 }
142189 } ;
190+
143191 // info!(
144192 // "Price Account: {:#?}, account: {:#?} \n\n",
145193 // price_account, account
@@ -207,7 +255,6 @@ async fn fetch_price_updates(jetstream: jetstream::Context, config: &AppConfig)
207255 } ) ;
208256
209257 for component in price_account. comp {
210- let jetstream_clone = jetstream. clone ( ) ;
211258 let publisher = component. publisher . to_string ( ) ;
212259 let publisher_price_update_message_id = format ! (
213260 "{}:{}:{}" ,
@@ -219,37 +266,19 @@ async fn fetch_price_updates(jetstream: jetstream::Context, config: &AppConfig)
219266 . insert ( "Nats-Msg-Id" , publisher_price_update_message_id. as_str ( ) ) ;
220267
221268 let publisher_price_update = PublisherPriceUpdate {
222- update_type : "publisher_price_update" . to_string ( ) ,
223- feed_id : price_account. prod . to_string ( ) ,
224- publisher : publisher,
225- price_info : PriceInfo {
226- price : price_account. agg . price . to_string ( ) ,
227- conf : price_account. agg . conf . to_string ( ) ,
228- expo : price_account. expo ,
229- publish_time : price_account. timestamp ,
230- slot : update. context . slot , // Add this field
231- } ,
269+ feed_id : update. value . pubkey . to_string ( ) ,
270+ publisher : publisher. clone ( ) ,
271+ price : price_account. agg . price . to_string ( ) ,
272+ slot : update. context . slot , // Add this field
232273 } ;
233- info ! ( "Publisher price update: {:?}" , publisher_price_update) ;
234- let publisher_price_update_message =
235- serde_json:: to_string ( & publisher_price_update) ?;
236-
237- task:: spawn ( async move {
238- match jetstream_clone
239- . publish_with_headers (
240- "pyth.publisher.updates" ,
241- publisher_price_updates,
242- publisher_price_update_message. into ( ) ,
243- )
244- . await
245- {
246- Ok ( _) => debug ! (
247- "Published publisher price update to JetStream with ID: {}" ,
248- publisher_price_update_message_id
249- ) ,
250- Err ( e) => warn ! ( "Failed to publish price update to JetStream: {}" , e) ,
251- }
252- } ) ;
274+ if publisher_price_update. feed_id == "7jAVut34sgRj6erznsYvLYvjc9GJwXTpN88ThZSDJ65G"
275+ && publisher == "6DNocjFJjocPLZnKBZyEJAC5o2QaiT5Mx8AkphfxDm5i"
276+ {
277+ info ! ( "publisher_price_update: {:#?}" , publisher_price_update) ;
278+ }
279+ let key = ( publisher_price_update. feed_id . clone ( ) , publisher) ;
280+ let mut buf = publisher_buffer. lock ( ) . await ;
281+ buf. insert ( key, publisher_price_update) ;
253282 }
254283 update_count += 1 ;
255284 unique_price_feeds. insert ( price_account. prod ) ;
0 commit comments