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

[r2r] Tendermint multiple rpcs optimization #1568

Merged
merged 36 commits into from Dec 26, 2022

Conversation

laruh
Copy link
Member

@laruh laruh commented Dec 12, 2022

optimize handling multiple rpcs #1480 issue

Trait RpcCommonOps was implemented for TendermintCoin to be able to find first live client and move it to the front.
If the live client was the first in vector, than there is a no-op rotation, client will be just returned.

@laruh laruh changed the title Tendermint multiple rpcs optimization [r2r] Tendermint multiple rpcs optimization Dec 12, 2022
Copy link
Collaborator

@shamardy shamardy left a comment

Choose a reason for hiding this comment

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

Great work 🔥 ! First review iteration.

mm2src/coins/eth.rs Outdated Show resolved Hide resolved
mm2src/coins/lp_coins.rs Outdated Show resolved Hide resolved
mm2src/coins/lp_coins.rs Outdated Show resolved Hide resolved
mm2src/coins/tendermint/tendermint_coin.rs Outdated Show resolved Hide resolved
Copy link

@sergeyboyko0791 sergeyboyko0791 left a comment

Choose a reason for hiding this comment

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

Great enhancement!
Please consider my suggestions. Maybe I'm wrong, so I'll be glad to hear counterarguments :)

}

#[derive(Debug)]
pub enum RpcClientEnum {

Choose a reason for hiding this comment

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

Is it possible to avoid adding such common RPC enum?
In my opinion, RpcClientEnum could be used as a return type of MmCoinEnum::get_rpc_client. As I can see, currently, RpcCommonOps::get_rpc_client is used when the RPC client type is known.
https://github.com/KomodoPlatform/atomicDEX-API/blob/af6a7e7ad7f5ea329e081a63b5df732224b50c7c/mm2src/coins/tendermint/tendermint_coin.rs#L439-L445

In conclusion, I believe that we could refactor this way:

/// Use trait in the case, when we have to send requests to rpc client.
#[async_trait]
pub trait RpcCommonOps {
    type RpcClient;
    type Error;

    /// Returns an alive RPC client or returns an error if no RPC endpoint is currently available.
    async fn get_rpc_client(&self) -> Result<Self::RpcClient, Self::Error>;
}

Please note that RpcCommonOps::iterate_over_urls is going to be used within RpcCommonOps::get_rpc_client method implementation only, so we can remove it from from the public RpcCommonOps interface.

Copy link
Member Author

@laruh laruh Dec 15, 2022

Choose a reason for hiding this comment

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

Actually thank you for this hint!
I removed enums due to adding types in trait.
So I can just implement this. it's like you suggested below.

impl RpcCommonOps for TendermintCoin {
    type RpcClient = HttpClient;
    type Error = TendermintCoinRpcError;

    async fn get_live_client(&self) -> Result<Self::RpcClient, Self::Error> {

please note, that we use get_live_client function like one line only in rpc_client method from trait TendermintCommons.
But I can not remove rpc_client from trait and use just get_live_client instead, bcz we need this function regarding trait CoinCapabilities: TendermintCommons + CoinWithTxHistoryV2 + MmCoin + MarketCoinOps {}.
This explanation is just in case there is a question whether rpc_client should be removed.

Comment on lines 174 to 190
match rpc_client.perform(HealthRequest).await {
Ok(_) => Ok(RpcClientEnum::TendermintHttpClient(rpc_client.clone())),
// try HealthRequest one more time
Err(_) => match rpc_client.perform(HealthRequest).await {
Ok(_) => Ok(RpcClientEnum::TendermintHttpClient(rpc_client.clone())),
Err(_) => {
let new_client = self.iterate_over_urls().await?;
match new_client {
RpcClientEnum::TendermintHttpClient(client) => {
*rpc_client = client.clone();
Ok(RpcClientEnum::TendermintHttpClient(client))
},
_ => Err(RpcCommonError::WrongRpcClient),
}
},
},
}

Choose a reason for hiding this comment

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

Please consider the following changes:

#[derive(Clone)]
struct TendermintRpcClient(Arc<AsyncMutex<TendermintRpcClientImpl>>);

struct TendermintRpcClientImpl {
    rpc_clients: Vec<HttpClient>,
    current: usize,
}

#[async_trait]
impl RpcCommonOps for TendermintCoin {
    type RpcClient = TendermintRpcClient;
    type Error = TendermintCoinRpcError;

    async fn get_rpc_client(&self) -> Result<Self::RpcClient, Self::Error> {
        let mut rpc /*: AsyncGuard<TendermintRpcClientImpl>*/ = self.rpc_client.lock().await;

        match rpc.perform(HealthRequest).await {
            // As I suggested above.
            Ok(_) => return Ok(self.rpc_client.clone()),
            Err(_) => (),
        }

        // Let's imagine there are 3 RPC clients, and we had an active RPC2:
        // | RPC1 | RPC2 | RPC3 |
        //           |
        //         Active
        //
        // Then we should give every RPC client one attempt, but starting with `RPC3`. The order is:
        // RPC3, RPC1, RPC2 (one more time).
        for ...
    }
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Sorry, could you tell, why do we need Arc here? As I can see we dont need to clone structure and share its instances.
Should we just use this ?

struct TendermintRpcClient {
    rpc_urls: Vec<String>,
    rpc_client: AsyncMutex<HttpClient>,
    current: AtomicUsize,
}

Choose a reason for hiding this comment

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

If we don't need to clone, let's avoid using Arc

Copy link
Member Author

@laruh laruh Dec 15, 2022

Choose a reason for hiding this comment

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

Thanks for the code example. I left struct TendermintRpcClient(AsyncMutex<TendermintRpcClientImpl>) and used

type RpcClient = HttpClient;
type Error = TendermintCoinRpcError;

UPD also I implemented pushing current url to the end

@laruh laruh changed the title [r2r] Tendermint multiple rpcs optimization [wip] Tendermint multiple rpcs optimization Dec 14, 2022
@laruh laruh changed the title [wip] Tendermint multiple rpcs optimization [r2r] Tendermint multiple rpcs optimization Dec 15, 2022
Copy link
Collaborator

@shamardy shamardy left a comment

Choose a reason for hiding this comment

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

Thanks for the fixes! Please consider the following changes.

mm2src/coins/tendermint/mod.rs Outdated Show resolved Hide resolved
mm2src/coins/tendermint/tendermint_coin.rs Outdated Show resolved Hide resolved
mm2src/coins/tendermint/tendermint_coin.rs Outdated Show resolved Hide resolved
@laruh laruh changed the title [r2r] Tendermint multiple rpcs optimization [wip] Tendermint multiple rpcs optimization Dec 16, 2022
@laruh laruh changed the title [wip] Tendermint multiple rpcs optimization [r2r] Tendermint multiple rpcs optimization Dec 16, 2022
Copy link
Collaborator

@shamardy shamardy left a comment

Choose a reason for hiding this comment

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

One last comment :)

mm2src/coins/tendermint/tendermint_coin.rs Outdated Show resolved Hide resolved
shamardy
shamardy previously approved these changes Dec 16, 2022
Copy link
Collaborator

@shamardy shamardy left a comment

Choose a reason for hiding this comment

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

🔥

Copy link

@sergeyboyko0791 sergeyboyko0791 left a comment

Choose a reason for hiding this comment

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

Thank you for the fixes!
A few more notes

mm2src/coins/tendermint/tendermint_coin.rs Show resolved Hide resolved
mm2src/coins/tendermint/tendermint_coin.rs Outdated Show resolved Hide resolved
@laruh laruh changed the title [r2r] Tendermint multiple rpcs optimization [wip] Tendermint multiple rpcs optimization Dec 19, 2022
@laruh laruh changed the title [wip] Tendermint multiple rpcs optimization [r2r] Tendermint multiple rpcs optimization Dec 19, 2022
@artemii235
Copy link
Member

@sergeyboyko0791 I'm waiting for your approval to give it a final review 🙂

Copy link

@sergeyboyko0791 sergeyboyko0791 left a comment

Choose a reason for hiding this comment

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

Only one question

mm2src/coins/tendermint/tendermint_coin.rs Show resolved Hide resolved
@artemii235
Copy link
Member

@sergeyboyko0791 Your change request is still pending, could you please recheck this PR?

Copy link

@sergeyboyko0791 sergeyboyko0791 left a comment

Choose a reason for hiding this comment

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

LGTM!
Sorry, I haven't noticed that the PR is ready...

@sergeyboyko0791 sergeyboyko0791 merged commit 47b1a71 into dev Dec 26, 2022
@sergeyboyko0791 sergeyboyko0791 deleted the tendermint-multiple-rpcs-optimization branch December 26, 2022 20:10
@laruh laruh mentioned this pull request Feb 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants