-
Notifications
You must be signed in to change notification settings - Fork 0
pyth-stream mvp #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
A few overall points:
|
| }; | ||
|
|
||
| if price_account.agg.status == PriceStatus::Trading | ||
| && (update.context.slot - price_account.agg.pub_slot) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a comment for context here? Looks like you want to discard account updates when the update slot is more than X slots ahead of the price aggregate slot. What is a invalid price update in this context?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took this from benchmarks/rust-streaming https://github.com/pyth-network/benchmarks/blob/main/rust-streaming/src/pythnet.rs#L56 - I don't really have full context on this myself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @ali-bahjati
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally we want to send price updates whenever the aggregate changes but sometimes the accounts can change without the aggregate changing; for example it happens when there is an update but min_pub is not reached and it can happen for feeds in closed market hours.
| price_feed: PriceFeed { | ||
| id: update.value.pubkey.to_string(), | ||
| price: PriceInfo { | ||
| price: price_account.agg.price.to_string(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd check with Ali if it makes sense to send the price and expo components separately or formatted as a regular decimal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @ayazabbas made it compatible with Hermes. I'd say let's keep it as our partners are trying it out with Hermes.
src/websocket_server.rs
Outdated
| Ok(jetstream) | ||
| } | ||
|
|
||
| async fn handle_connection(stream: TcpStream, clients: Clients) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is difficult to parse. In addition to the websocket sender/receiver this is defining two additional channels (tx/rx and outgoing_tx/outgoing_rx), I am assuming it is so you can send both price updates and responses from the client through the ws_sender. Is that correct? Is it not possible to clone the ws_sender and use it in more than one place?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea behind multiple channels is to separate message generation from message sending. The broadcast channel is used for distributing price updates to all interested clients, and the mspc channel is used to queue outgoing messages for a specific client.
I agree that this can be simplified but I'm not sure if cloning the ws_sender is safe/recommended. I think an easy simplification might be to eliminate the broadcast channel and just have the nats message handler forward price updates directly to the outgoing_tx channel for each client. Gonna try this.
P.S. Using the broadcast channel approach would be more efficient when you have many clients subscribed to the same price feeds but in this case the difference is probably negligible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if ws_sender is clone able, then we should do it to make things easier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. I left some comments that are mostly minor and language specific.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can separate it to two workflows: one for ci, and one for image build&push (and add manual dispatch there)
Cargo.toml
Outdated
| tokio-tungstenite = "0.24.0" | ||
| tracing = "0.1" | ||
| tracing-subscriber = { version = "0.3", features = ["env-filter"] } | ||
| zeroize = { version = "1.7", default-features = false } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need this after the patch below?
src/pyth_reader.rs
Outdated
|
|
||
| #[derive(Debug, Deserialize)] | ||
| struct NatsConfig { | ||
| url: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: you can use url::Url or nats ServerAddr here to make sure right argument is passed upon parsing the config.
src/pyth_reader.rs
Outdated
| http_addr: String, | ||
| websocket_addr: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above
src/pyth_reader.rs
Outdated
| program_key: String, | ||
| mapping_key: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use Pubkey here.
src/websocket_server.rs
Outdated
|
|
||
| info!(client_addr = %addr, "New WebSocket connection established"); | ||
|
|
||
| let (tx, mut rx) = broadcast::channel(100); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: let's extract 100 and make it a constant CHANNEL_SIZE
src/websocket_server.rs
Outdated
| #[derive(Debug, Serialize, Deserialize)] | ||
| struct ClientMessage { | ||
| #[serde(rename = "type")] | ||
| message_type: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can have this as enum and act based on that. right? (like this)
src/websocket_server.rs
Outdated
| let jetstream_clone = jetstream.clone(); | ||
| let clients_clone = clients.clone(); | ||
| tokio::spawn(async move { | ||
| handle_nats_messages(jetstream_clone, clients_clone).await; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
one nit thing here is that if these threads fail the app will enter a dead state and we might not want it. So either reconnection is good here or panic there and set the panic behaviour to abort the process (see this)
src/websocket_server.rs
Outdated
| Ok(jetstream) | ||
| } | ||
|
|
||
| async fn handle_connection(stream: TcpStream, clients: Clients) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if ws_sender is clone able, then we should do it to make things easier.
src/websocket_server.rs
Outdated
| let outgoing_tx_clone = outgoing_tx.clone(); | ||
| let broadcast_task = tokio::spawn(async move { | ||
| while let Ok(msg) = rx.recv().await { | ||
| if let Err(e) = outgoing_tx_clone.send(Message::Text(msg)).await { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for future: as we have dozens of prices coming at each slot, it might be better to feed them first, and then flush once we've sent all of them.
add configuration options to readme use specific types in structs use expect or context instead of unwrap remove use of mapping account spawn separate threads for blocking operations move duplicate nats code to utils move binary entrypoints under src/bin add reconnection logic and abort process on panic
No description provided.