2020-07-10 23:11:07 -06:00
|
|
|
---
|
|
|
|
title: Rust Clients
|
|
|
|
---
|
2020-04-06 16:38:03 -06:00
|
|
|
|
|
|
|
## Problem
|
|
|
|
|
|
|
|
High-level tests, such as bench-tps, are written in terms of the `Client`
|
2020-07-10 23:11:07 -06:00
|
|
|
trait. When we execute these tests as part of the test suite, we use the
|
2020-04-06 16:38:03 -06:00
|
|
|
low-level `BankClient` implementation. When we need to run the same test
|
|
|
|
against a cluster, we use the `ThinClient` implementation. The problem with
|
|
|
|
that approach is that it means the trait will continually expand to include new
|
|
|
|
utility functions and all implementations of it need to add the new
|
|
|
|
functionality. By separating the user-facing object from the trait that abstracts
|
|
|
|
the network interface, we can expand the user-facing object to include all sorts
|
|
|
|
of useful functionality, such as the "spinner" from RpcClient, without concern
|
|
|
|
for needing to extend the trait and its implementations.
|
|
|
|
|
|
|
|
## Proposed Solution
|
|
|
|
|
|
|
|
Instead of implementing the `Client` trait, `ThinClient` should be constructed
|
|
|
|
with an implementation of it. That way, all utility functions currently in the
|
|
|
|
`Client` trait can move into `ThinClient`. `ThinClient` could then move into
|
|
|
|
`solana-sdk` since all its network dependencies would be in the implementation
|
|
|
|
of `Client`. We would then add a new implementation of `Client`, called
|
|
|
|
`ClusterClient`, and that would live in the `solana-client` crate, where
|
|
|
|
`ThinClient` currently resides.
|
|
|
|
|
|
|
|
After this reorg, any code needing a client would be written in terms of
|
2020-07-10 23:11:07 -06:00
|
|
|
`ThinClient`. In unit tests, the functionality would be invoked with
|
2020-04-06 16:38:03 -06:00
|
|
|
`ThinClient<BankClient>`, whereas `main()` functions, benchmarks and
|
|
|
|
integration tests would invoke it with `ThinClient<ClusterClient>`.
|
|
|
|
|
|
|
|
If higher-level components require more functionality than what could be
|
|
|
|
implemented by `BankClient`, it should be implemented by a second object
|
|
|
|
that implements a second trait, following the same pattern described here.
|
|
|
|
|
|
|
|
### Error Handling
|
|
|
|
|
|
|
|
The `Client` should use the existing `TransportError` enum for errors, except
|
|
|
|
that the `Custom(String)` field should be changed to `Custom(Box<dyn Error>)`.
|
|
|
|
|
|
|
|
### Implementation Strategy
|
|
|
|
|
|
|
|
1. Add new object to `solana-sdk`, `RpcClientTng`, where the `Tng` suffix is
|
|
|
|
temporary and stands for "The Next Generation"
|
|
|
|
2. Initialize `RpcClientTng` with a `SyncClient` implementation.
|
|
|
|
3. Add new object to `solana-sdk`, `ThinClientTng`; initialize it with
|
|
|
|
`RpcClientTng` and an `AsyncClient` implementation
|
|
|
|
4. Move all unit-tests from `BankClient` to `ThinClientTng<BankClient>`
|
|
|
|
5. Add `ClusterClient`
|
2020-07-10 23:11:07 -06:00
|
|
|
6. Move `ThinClient` users to `ThinClientTng<ClusterClient>`
|
|
|
|
7. Delete `ThinClient` and rename `ThinClientTng` to `ThinClient`
|
|
|
|
8. Move `RpcClient` users to new `ThinClient<ClusterClient>`
|
|
|
|
9. Delete `RpcClient` and rename `RpcClientTng` to `RpcClient`
|