-
Notifications
You must be signed in to change notification settings - Fork 4.5k
RPC Block Subscription #21787
RPC Block Subscription #21787
Conversation
Codecov Report
@@ Coverage Diff @@
## master #21787 +/- ##
=========================================
+ Coverage 81.2% 81.4% +0.1%
=========================================
Files 516 516
Lines 144284 145338 +1054
=========================================
+ Hits 117256 118318 +1062
+ Misses 27028 27020 -8 |
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.
Definitely seems useful. I think we can make this work.
I would like to see some perf testing to see how many all
subscriptions can be supported without bogging down the rpc node.
Given the potential for large notification messages, do you think this subscription ought to be opt-in for nodes, like voteSubscribe?
(optimistically adding the v1.9 label as I think there are some that would love to see this feature sooner than in v1.10) |
Ran some local perf tests locally. Setup:
|
does blockstore store processed blocks? does it make sense to allow processed here if getBlock doesn't allow it? |
This won't support processed blocks until |
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.
Good stuff here.
I have a handful of nits, plus a few more substantive things we need to handle.
(CI failure seems unrelated)
It's my understanding that |
for "all" it looks like we'll hit the NIC limit before we bog down the node lol. we started running tests on our laptop, but wasn't reliable, so we moved it onto the ax161. this is running from a few commits ago, but seems like no large perf changes since then. test setup: script:
block subscription params: fn new_block_sub_client(ws_url: String) -> BlockSubscription {
PubsubClient::block_subscribe(
&ws_url,
RpcBlockSubscribeFilter::All,
Some(RpcBlockSubscribeConfig {
commitment: Some(CommitmentConfig {
commitment: CommitmentLevel::Confirmed,
}),
encoding: Some(UiTransactionEncoding::Base64),
transaction_details: Some(TransactionDetails::Full),
show_rewards: Some(false),
}),
)
.expect("websocket to subscribe")
} raw data: |
the slot diff thing is weird, perhaps reorgs? |
That's correct; it becomes "processed" when the block is complete (all shreds received) and processed by ReplayStage (frozen) and ready to be voted on. |
rpc/src/rpc_subscriptions.rs
Outdated
let ancestors = bank_forks.read().unwrap().ancestors(); | ||
let ancestors = ancestors.get(&slot); | ||
let empty_set = &HashSet::new(); | ||
let ancestors = ancestors.unwrap_or(empty_set); | ||
for s in *w_last_unnotified_slot..slot + 1 { |
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.
let ancestors = bank_forks.read().unwrap().ancestors(); | |
let ancestors = ancestors.get(&slot); | |
let empty_set = &HashSet::new(); | |
let ancestors = ancestors.unwrap_or(empty_set); | |
for s in *w_last_unnotified_slot..slot + 1 { | |
let bank = bank_forks.read().unwrap().get(&slot); | |
let mut slots_to_notify: Vec<_> = bank.proper_ancestors().filter(|ancestor| ancestor >= *w_last_unnotified_slot).collect(); | |
slots_to_notify.sort(); | |
slots_to_notify.push(slot); | |
for s in slots_to_notify { |
Sorry, I should have steered you to the Bank method instead. It should be faster, since it just reads the ancestors of one slot.
Wdyt of this approach, skipping non-ancestors in the loop entirely?
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.
So I was thinking of going this route but opted out just because of time complexity. It may be negligible (maybe not?) But from a glance this would be O(n*log(n)) cause of the sort and still iterates through all ancestors in filter vs. O(1) access of the HashSet approach. Perhaps the sort
can be omitted completely if we do something like:
let mut slots_to_notify: Vec<_> = (w_last_unnotified_slot..slot).collect();
slots_to_notify = slots_to_notify.into_iter().filter(|slot| ancestors.contains(slot)).collect();
slots_to_notify.push(slot);
Wdyt of the three approaches?
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.
(Sorry for delay!) I think it may be negligible, but fair point. I like approach 3 without the sort the best!
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.
just noticed proper_ancestors
is only crate visible plus doesn't return HashSet
, so the bank_forks function call might be better suited here
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.
actually I have an idea!
I don't recognize that CI failure, incidentally. If you wouldn't mind, can you rebase on master when you push any new changes? if the failure recurs, I'll take a closer look. |
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 last thing: #21787 (comment)
And then lgtm :)
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.
CI is green :) Let's get this in, and we can iterate if needed. Thank you for all the polish, @segfaultdoc !
hell ya! lfg @segfaultdoc |
Oh sorry, one last thing: in a separate PR, can you please add the new method to docs here? https://github.com/solana-labs/solana/blob/master/docs/src/developing/clients/jsonrpc-api.md (with voteSubscribe, for now) |
* add stuff * compiling * add notify block * wip * feat: add blockSubscribe pubsub method * address PR comments Co-authored-by: Lucas B <[email protected]> Co-authored-by: Zano <[email protected]> (cherry picked from commit 76098dd) # Conflicts: # Cargo.lock # client-test/Cargo.toml # rpc/src/rpc_subscriptions.rs
* RPC Block Subscription (#21787) * add stuff * compiling * add notify block * wip * feat: add blockSubscribe pubsub method * address PR comments Co-authored-by: Lucas B <[email protected]> Co-authored-by: Zano <[email protected]> (cherry picked from commit 76098dd) # Conflicts: # Cargo.lock # client-test/Cargo.toml # rpc/src/rpc_subscriptions.rs * Fix conflicts Co-authored-by: segfaultdoctor <[email protected]> Co-authored-by: Tyera Eulberg <[email protected]>
most certainly! thx for bearing with me :) |
Here it is: #22002 |
Problem
Wintermute and other HFTs would like a way to subscribe to an account (i.e. serum token account, serum market etc.) and receive transactions that mention the specified account per block.
Summary of Changes
This adds a
blockSubscribe
websocket method. Example usages:{"jsonrpc": "2.0", "id": "1", "method": "blockSubscribe", "params": ["all"]}
{"jsonrpc": "2.0", "id": "1", "method": "blockSubscribe", "params": [{"mentionsAccountOrProgram": "pubkey"}]}
{"jsonrpc": "2.0", "id": "1", "method": "blockSubscribe", "params": [{"mentionsAccountOrProgram": "pubkey"}, {"commitment": "confirmed"}]}
If no txs match the filters for a given block then no data is returned.