Track timestamps in lazer_exporter to avoid duplicate messages#172
Track timestamps in lazer_exporter to avoid duplicate messages#172devin-ai-integration[bot] wants to merge 3 commits intomainfrom
Conversation
Keep track of the most recent PriceInfo::timestamp for each pyth_sdk::Identifier and skip objects that do not have a newer timestamp. This prevents sending duplicate FeedUpdate messages when the local state has not been updated. Co-Authored-By: Mike Rolish <merolish@mit.edu>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
Co-Authored-By: Mike Rolish <merolish@mit.edu>
ali-behjati
left a comment
There was a problem hiding this comment.
Please bump the version @devin
Co-Authored-By: Mike Rolish <merolish@mit.edu>
|
Version bumped to 3.0.7 in commit 7e19cf2. |
| special_fields: Default::default(), | ||
| }) | ||
| }); | ||
| last_sent_timestamps.insert(identifier, price_info.timestamp); |
There was a problem hiding this comment.
🔴 last_sent_timestamps updated before transaction is serialized and sent, causing price updates to be silently dropped on failure
The last_sent_timestamps map is updated eagerly at line 462, inside the per-price loop, before the batch transaction is serialized (line 475) and broadcast to relayers (line 496). If serialization fails (write_to_bytes error at line 475-479) or the broadcast send fails (no active receivers at line 496-500), the code continues or logs an error, but the timestamps have already been recorded as "sent". On the next publish tick, these price updates will be skipped by the dedup check at line 440-443 (price_info.timestamp <= *last_timestamp), even though they were never actually delivered.
Root Cause and Impact
The root cause is that last_sent_timestamps.insert(identifier, price_info.timestamp) at line 462 is executed during the collection phase, not after successful delivery. The timestamps should only be committed after the transaction has been successfully sent.
For example, if write_to_bytes() fails at line 475:
Err(e) => {
tracing::warn!("Failed to encode Lazer transaction to bytes: {:?}", e);
continue; // timestamps already recorded!
}Or if relayer_sender.send() fails at line 496 (e.g., no active receivers):
Err(e) => {
tracing::error!("Error sending transaction to relayer receivers: {e}");
// timestamps already recorded, these updates are lost
}Impact: Price updates are permanently lost until a newer-timestamped update arrives for each affected feed. In a scenario where the broadcast channel has no receivers (all relayer connections are down), every price update will be marked as sent but never delivered, causing a complete data gap.
Prompt for agents
In src/agent/services/lazer_exporter.rs, the last_sent_timestamps.insert() call at line 462 should be moved to after the transaction is successfully sent. One approach: collect the timestamps to update into a temporary HashMap or Vec during the for loop (lines 438-464), and only apply them to last_sent_timestamps after the relayer_sender.send() succeeds at line 496. For example, replace line 462 with collecting into a local variable like `pending_timestamps.insert(identifier, price_info.timestamp)`, and then after the successful send at line 497, iterate over pending_timestamps and insert them into last_sent_timestamps. Make sure the pending_timestamps are NOT applied when serialization fails (line 475-479 continue) or when the broadcast send fails (line 498-500).
Was this helpful? React with 👍 or 👎 to provide feedback.
Track timestamps in lazer_exporter to avoid duplicate messages
Summary
Previously, the lazer_exporter would periodically copy all price infos from the local state via
state.get_all_price_infos()and send FeedUpdate messages for every price feed, even if the data hadn't changed. This led to many duplicate messages being sent to the relayer.This PR adds a HashMap to track the most recent
PriceInfo::timestampfor eachpyth_sdk::Identifier. Before sending a FeedUpdate, we now check if the price info has a newer timestamp than what was last sent. If not, we skip sending that update.Changes:
last_sent_timestampsHashMap to track the last sent timestamp per identifierReview & Testing Checklist for Human
Risk level: Yellow - Logic change that affects message deduplication behavior
last_sent_timestampsHashMap size is acceptable in production. It will grow to the number of unique identifiers ever seen during the agent's lifetime. Consider whether a bounded cache or TTL-based cleanup is needed for long-running processes.<=is correct (skip if timestamp is less than OR equal to last sent). Should we ever send updates with the same timestamp twice?Test Plan
Notes