Skip to content

Commit 6407eaa

Browse files
author
Stanisław Drozd
authored
pyth2wormhole-client:should_resend(): optimize interval-only batches (#338)
* pyth2wormhole-client:should_resend(): optimize interval-only batches Most attestation conditions in prod are interval-only. This change ensures that interval-only batches don't waste time on the on-chain state lookup request. * pyth2wormhole: Fix build error, redundant new_symbol_states decl * pyth2wormhole-client: is_onchain -> needs_onchain_lookup * pyth2wormhole-client: typo
1 parent 124589d commit 6407eaa

File tree

2 files changed

+69
-50
lines changed

2 files changed

+69
-50
lines changed

solana/pyth2wormhole/client/src/attestation_cfg.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,22 @@ pub struct AttestationConditions {
144144
pub publish_time_min_delta_secs: Option<u64>,
145145
}
146146

147+
impl AttestationConditions {
148+
/// Used by should_resend() to check if it needs to make the expensive RPC request
149+
pub fn need_onchain_lookup(&self) -> bool {
150+
// Bug trap for new fields that also need to be included in
151+
// the returned expression
152+
let AttestationConditions {
153+
min_interval_secs: _min_interval_secs,
154+
max_batch_jobs: _max_batch_jobs,
155+
price_changed_pct,
156+
publish_time_min_delta_secs,
157+
} = self;
158+
159+
price_changed_pct.is_some() || publish_time_min_delta_secs.is_some()
160+
}
161+
}
162+
147163
impl Default for AttestationConditions {
148164
fn default() -> Self {
149165
Self {

solana/pyth2wormhole/client/src/batch_state.rs

Lines changed: 53 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -57,35 +57,6 @@ impl<'a> BatchState<'a> {
5757
let sym_count = self.symbols.len();
5858
let pubkeys: Vec<_> = self.symbols.iter().map(|s| s.price_addr).collect();
5959

60-
// Always learn the current on-chain state for each symbol, use None values if lookup fails
61-
let mut new_symbol_states: Vec<Option<PriceAccount>> = match c
62-
.get_multiple_accounts(&pubkeys)
63-
.await
64-
{
65-
Ok(acc_opts) => {
66-
acc_opts
67-
.into_iter()
68-
.enumerate()
69-
.map(|(idx, opt)| {
70-
// Take each Some(acc), make it None and log on load_price_account() error
71-
opt.and_then(|acc| {
72-
pyth_sdk_solana::state::load_price_account(&acc.data)
73-
.cloned() // load_price_account() transmutes the data reference into another reference, and owning acc_opts is not enough
74-
.map_err(|e| {
75-
warn!("Could not parse symbol {}/{}: {}", idx, sym_count, e);
76-
e
77-
})
78-
.ok() // Err becomes None
79-
})
80-
})
81-
.collect()
82-
}
83-
Err(e) => {
84-
warn!("Could not look up any symbols on-chain: {}", e);
85-
vec![None; sym_count]
86-
}
87-
};
88-
8960
// min interval
9061
if self.last_job_finished_at.elapsed()
9162
> Duration::from_secs(self.conditions.min_interval_secs)
@@ -96,14 +67,47 @@ impl<'a> BatchState<'a> {
9667
));
9768
}
9869

99-
for (idx, old_new_tup) in self
100-
.last_known_symbol_states
101-
.iter_mut() // Borrow mutably to make the update easier
102-
.zip(new_symbol_states.iter())
103-
.enumerate()
104-
{
105-
// Only evaluate this symbol if a triggering condition is not already met
106-
if ret.is_none() {
70+
// Only lookup and compare symbols if the conditions require
71+
if self.conditions.need_onchain_lookup() {
72+
let mut new_symbol_states: Vec<Option<PriceAccount>> =
73+
match c.get_multiple_accounts(&pubkeys).await {
74+
Ok(acc_opts) => {
75+
acc_opts
76+
.into_iter()
77+
.enumerate()
78+
.map(|(idx, opt)| {
79+
// Take each Some(acc), make it None and log on load_price_account() error
80+
opt.and_then(|acc| {
81+
pyth_sdk_solana::state::load_price_account(&acc.data)
82+
.cloned() // load_price_account() transmutes the data reference into another reference, and owning acc_opts is not enough
83+
.map_err(|e| {
84+
warn!(
85+
"Could not parse symbol {}/{}: {}",
86+
idx, sym_count, e
87+
);
88+
e
89+
})
90+
.ok() // Err becomes None
91+
})
92+
})
93+
.collect()
94+
}
95+
Err(e) => {
96+
warn!("Could not look up any symbols on-chain: {}", e);
97+
vec![None; sym_count]
98+
}
99+
};
100+
101+
for (idx, old_new_tup) in self
102+
.last_known_symbol_states
103+
.iter_mut() // Borrow mutably to make the update easier
104+
.zip(new_symbol_states.iter())
105+
.enumerate()
106+
{
107+
// Only evaluate this symbol if a triggering condition is not already met
108+
if ret.is_some() {
109+
break;
110+
}
107111
match old_new_tup {
108112
(Some(old), Some(new)) => {
109113
// publish_time_changed
@@ -143,19 +147,18 @@ impl<'a> BatchState<'a> {
143147
}
144148
}
145149
}
146-
}
147-
148-
// Update with newer state only if a condition was met. We
149-
// don't want to shadow changes that may happen over a larger
150-
// period between state lookups.
151-
if ret.is_some() {
152-
for (old, new) in self
153-
.last_known_symbol_states
154-
.iter_mut()
155-
.zip(new_symbol_states.into_iter())
156-
{
157-
if new.is_some() {
158-
*old = new;
150+
// Update with newer state only if a condition was met. We
151+
// don't want to shadow changes that may happen over a larger
152+
// period between state lookups.
153+
if ret.is_some() {
154+
for (old, new) in self
155+
.last_known_symbol_states
156+
.iter_mut()
157+
.zip(new_symbol_states.into_iter())
158+
{
159+
if new.is_some() {
160+
*old = new;
161+
}
159162
}
160163
}
161164
}

0 commit comments

Comments
 (0)