Skip to content
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

Provide async support for direct request/response communication #521

Merged
merged 22 commits into from
Apr 20, 2023

Conversation

Niederb
Copy link
Contributor

@Niederb Niederb commented Apr 5, 2023

  • Provide async support for direct request/response communication
    • This means communication based on the request() method
    • Subscription based communication is still to do
  • Use the maybe-async crate to provide a sync and async API with little changes in the code
  • Is breaking backwards compatibility because compilation with default-features=false now defaults to compiling the api in async mode
    • This is because the default mode of maybe-async is async
  • Is a first step towards Support async calls #278

let metadata_future = Self::get_metadata(&client);
let runtime_version_future = Self::get_runtime_version(&client);

let (genesis_hash, metadata, runtime_version) = futures::future::try_join3(
Copy link
Contributor Author

@Niederb Niederb Apr 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I'm not quite sure: On the one hand it's nice to actually take advantage of async by awaiting the futures in parallel. On the other hand the function is duplicated because there is a second implementation for the sync case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's fine. The async improvement is worth the small duplication.

src/lib.rs Outdated
@@ -16,6 +16,7 @@
*/
#![cfg_attr(not(feature = "std"), no_std)]
#![feature(error_in_core)]
#![feature(async_fn_in_trait)]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using async in traits is not available on stable yet

@Niederb
Copy link
Contributor Author

Niederb commented Apr 11, 2023

@haerdib This is not finished yet, but can you have a first look?
Most of it is boilerplate of converting functions to async and using await.
Some thoughts (also see comments):

  • We need a new nightly feature to have support for async in traits
  • It would be nice to use features from futures::future but this needs std
  • I'm not sure what the situation is for executor support in no_std. For example in wasm. I'm just wondering if we limit our potential use-cases by switching to async.

@Niederb Niederb requested a review from haerdib April 11, 2023 07:48
@Niederb Niederb self-assigned this Apr 11, 2023
Copy link
Contributor

@haerdib haerdib left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what the situation is for executor support in no_std. For example in wasm. I'm just wondering if we limit our potential use-cases by switching to async.

This would certainly need thorough investigation. But instead of switching completely to async, have you considered providing both, async and sync client? That would solve the question if it's available in wasm and such - if it's not, one can simply use the sync client.

Ofc, that would need quite some thought on how to provide two clients without having to duplicate everything.

@Niederb
Copy link
Contributor Author

Niederb commented Apr 11, 2023

I'm not sure what the situation is for executor support in no_std. For example in wasm. I'm just wondering if we limit our potential use-cases by switching to async.

This would certainly need thorough investigation. But instead of switching completely to async, have you considered providing both, async and sync client? That would solve the question if it's available in wasm and such - if it's not, one can simply use the sync client.

Ofc, that would need quite some thought on how to provide two clients without having to duplicate everything.

It certainly would be nice if we can support both use cases sync and async without duplicating all the code.
Our problem is basically described here nicely: https://blog.rust-lang.org/inside-rust/2022/07/27/keyword-generics.html#memories-of-the-present-async-today
Except in our case the block_on is also unfortunate, because this typically this is problematic for no_std.
maybe-async looks quite interesting though. It seems to "remove" the async/await keywords completely and I think then we could manage without block_on

@Niederb
Copy link
Contributor Author

Niederb commented Apr 13, 2023

  • I looked a bit more into maybe-async. It uses the async_trait crate to support async on traits (instead of the nightly feature).
  • The nightly implementation currently does not support dynamic dispatch which seems quite limiting to me so we might be better of using async-trait anyway.

@Niederb
Copy link
Contributor Author

Niederb commented Apr 13, 2023

Still not finished but I think it is closer to what we want.
The approach is now quite different:

  • Use maybe-async and async-trait crates instead of nightly implementation.
    • This allows us to provide a sync and an async API by changing a compiler flag
    • Examples compile without changes
  • The only difference between async and sync is in JsonrpseeClient::request. There is now a separate implementation for each.
  • maybe-async does not work with macros, so I had to take the nonce out of the macro. This is not so nice.
    @haerdib What do you think?

@Niederb Niederb requested a review from haerdib April 13, 2023 13:46
Copy link
Contributor

@haerdib haerdib left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Examples compile without changes

Does that mean that this PR does not introduce any breaking changes? E.g. the users do not need to add the #[maybe_async::maybe_async(?Send)] in case they use the good old sync variant?

Cargo.toml Show resolved Hide resolved
compose-macros/src/lib.rs Show resolved Hide resolved
@Niederb
Copy link
Contributor Author

Niederb commented Apr 17, 2023

  • Examples compile without changes

Does that mean that this PR does not introduce any breaking changes? E.g. the users do not need to add the #[maybe_async::maybe_async(?Send)] in case they use the good old sync variant?

Yes, at least it's my understanding that as long as we compile maybe-async with features = ["is_sync"] then all the async and await will basically be removed by the macro. Therefore it compiles as before.

# Conflicts:
#	Cargo.lock
#	src/api/rpc_api/author.rs
#	src/api/rpc_api/chain.rs
#	src/api/rpc_api/events.rs
#	src/api/rpc_api/frame_system.rs
#	src/api/rpc_api/pallet_transaction_payment.rs
#	src/api/rpc_api/state.rs
# Conflicts:
#	src/api/rpc_api/chain.rs
@Niederb Niederb marked this pull request as ready for review April 19, 2023 10:38
@Niederb
Copy link
Contributor Author

Niederb commented Apr 19, 2023

I think this is now ready for merging. The nonce handling is now the same as before.
I created a follow up task for implementing an example #543

@flipchan
Copy link

@Niederb awesome new feature! is this stable?

Copy link
Contributor

@haerdib haerdib left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely awesome, thanks a lot!

One thing: Could you rename the PR to a little more meaningful name and link the corresponding issue (though not close it I guess because Subscription is not yet async)

@@ -24,11 +24,13 @@ members = [

[dependencies]
# crates.io no_std
async-trait = "0.1.68"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is a transitive dependency from maybe-async and it does not compile without it. Dependencies for macros is a bit of a mystery for me...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. Thanks for the explanation!

Cargo.toml Outdated Show resolved Hide resolved
compose-macros/src/lib.rs Outdated Show resolved Hide resolved
$module: expr,
$call: expr
$(, $args: expr) *) => {
{
use $crate::log::debug;
use $crate::log::debug;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that indent correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I fixed it now. Seems cargo fmt works differently in macros. Interesting

compose-macros/src/lib.rs Outdated Show resolved Hide resolved
let metadata_future = Self::get_metadata(&client);
let runtime_version_future = Self::get_runtime_version(&client);

let (genesis_hash, metadata, runtime_version) = futures::future::try_join3(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's fine. The async improvement is worth the small duplication.

Co-authored-by: Bigna Härdi <73821294+haerdib@users.noreply.github.com>
@Niederb Niederb changed the title Experiment with async Provide async support for direct request/response communication Apr 19, 2023
@Niederb Niederb linked an issue Apr 19, 2023 that may be closed by this pull request
@Niederb
Copy link
Contributor Author

Niederb commented Apr 19, 2023

@Niederb awesome new feature! is this stable?

@flipchan Thanks! I'm not quite sure what you mean by stable? I hope this PR will be merged very soon and then async will be available for the more simple communication that uses a single request/response (more complex communication based on subscriptions is not done yet).
substrate-api-client needs a nightly rust compiler because of other features we need.

@Niederb
Copy link
Contributor Author

Niederb commented Apr 19, 2023

Absolutely awesome, thanks a lot!

One thing: Could you rename the PR to a little more meaningful name and link the corresponding issue (though not close it I guess because Subscription is not yet async)

Done, but linking without closing is apparently not supported. See https://github.com/orgs/community/discussions/17308
I guess we can just repopen the issue after the merge 😉

@Niederb Niederb requested a review from haerdib April 19, 2023 13:59
@haerdib
Copy link
Contributor

haerdib commented Apr 19, 2023

My workaround for that was to simply add a link in the PR description. E.g. something like "related to #278".

Copy link
Contributor

@haerdib haerdib left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏

@@ -24,11 +24,13 @@ members = [

[dependencies]
# crates.io no_std
async-trait = "0.1.68"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. Thanks for the explanation!

@haerdib haerdib added the F8-newfeature Introduces a new feature label Apr 19, 2023
@Niederb Niederb removed a link to an issue Apr 20, 2023
@Niederb Niederb mentioned this pull request Apr 20, 2023
Copy link
Contributor

@echevrier echevrier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot. Really nice!

@Niederb Niederb merged commit 303eceb into master Apr 20, 2023
@Niederb Niederb deleted the tn/async-experiment branch April 20, 2023 08:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
E2-breaksapi F8-newfeature Introduces a new feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants