move error handling into a function
This commit is contained in:
parent
8a2535da74
commit
8aacd84955
@ -1,12 +1,11 @@
|
|||||||
use crate::config::Web3ConnectionConfig;
|
use crate::config::Web3ConnectionConfig;
|
||||||
use crate::connections::Web3Connections;
|
use crate::connections::Web3Connections;
|
||||||
use crate::jsonrpc::JsonRpcErrorData;
|
|
||||||
use crate::jsonrpc::JsonRpcForwardedResponse;
|
use crate::jsonrpc::JsonRpcForwardedResponse;
|
||||||
use crate::jsonrpc::JsonRpcForwardedResponseEnum;
|
use crate::jsonrpc::JsonRpcForwardedResponseEnum;
|
||||||
use crate::jsonrpc::JsonRpcRequest;
|
use crate::jsonrpc::JsonRpcRequest;
|
||||||
use crate::jsonrpc::JsonRpcRequestEnum;
|
use crate::jsonrpc::JsonRpcRequestEnum;
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use ethers::prelude::{HttpClientError, ProviderError, WsClientError, H256};
|
use ethers::prelude::H256;
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use linkedhashmap::LinkedHashMap;
|
use linkedhashmap::LinkedHashMap;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
@ -44,7 +43,7 @@ pub struct Web3ProxyApp {
|
|||||||
balanced_rpcs: Arc<Web3Connections>,
|
balanced_rpcs: Arc<Web3Connections>,
|
||||||
/// Send private requests (like eth_sendRawTransaction) to all these servers
|
/// Send private requests (like eth_sendRawTransaction) to all these servers
|
||||||
private_rpcs: Arc<Web3Connections>,
|
private_rpcs: Arc<Web3Connections>,
|
||||||
active_requests: ActiveRequestsMap,
|
incoming_requests: ActiveRequestsMap,
|
||||||
response_cache: ResponseLrcCache,
|
response_cache: ResponseLrcCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +114,7 @@ impl Web3ProxyApp {
|
|||||||
Ok(Web3ProxyApp {
|
Ok(Web3ProxyApp {
|
||||||
balanced_rpcs,
|
balanced_rpcs,
|
||||||
private_rpcs,
|
private_rpcs,
|
||||||
active_requests: Default::default(),
|
incoming_requests: Default::default(),
|
||||||
response_cache: Default::default(),
|
response_cache: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -129,7 +128,7 @@ impl Web3ProxyApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_active_requests(&self) -> &ActiveRequestsMap {
|
pub fn get_active_requests(&self) -> &ActiveRequestsMap {
|
||||||
&self.active_requests
|
&self.incoming_requests
|
||||||
}
|
}
|
||||||
|
|
||||||
/// send the request to the approriate RPCs
|
/// send the request to the approriate RPCs
|
||||||
@ -301,7 +300,7 @@ impl Web3ProxyApp {
|
|||||||
|
|
||||||
let (cache_key, response_cache) = match self.get_cached_response(&request) {
|
let (cache_key, response_cache) = match self.get_cached_response(&request) {
|
||||||
(cache_key, Ok(response)) => {
|
(cache_key, Ok(response)) => {
|
||||||
let _ = self.active_requests.remove(&cache_key);
|
let _ = self.incoming_requests.remove(&cache_key);
|
||||||
|
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
@ -309,27 +308,28 @@ impl Web3ProxyApp {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// check if this request is already in flight
|
// check if this request is already in flight
|
||||||
let (in_flight_tx, in_flight_rx) = watch::channel(true);
|
// TODO: move this logic into an IncomingRequestHandler (ActiveRequestHandler has an rpc, but this won't)
|
||||||
let mut other_in_flight_rx = None;
|
let (incoming_tx, incoming_rx) = watch::channel(true);
|
||||||
match self.active_requests.entry(cache_key.clone()) {
|
let mut other_incoming_rx = None;
|
||||||
|
match self.incoming_requests.entry(cache_key.clone()) {
|
||||||
dashmap::mapref::entry::Entry::Occupied(entry) => {
|
dashmap::mapref::entry::Entry::Occupied(entry) => {
|
||||||
other_in_flight_rx = Some(entry.get().clone());
|
other_incoming_rx = Some(entry.get().clone());
|
||||||
}
|
}
|
||||||
dashmap::mapref::entry::Entry::Vacant(entry) => {
|
dashmap::mapref::entry::Entry::Vacant(entry) => {
|
||||||
entry.insert(in_flight_rx);
|
entry.insert(incoming_rx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(mut other_in_flight_rx) = other_in_flight_rx {
|
if let Some(mut other_incoming_rx) = other_incoming_rx {
|
||||||
// wait for the other request to finish. it can finish successfully or with an error
|
// wait for the other request to finish. it might have finished successfully or with an error
|
||||||
trace!("{:?} waiting on in-flight request", request);
|
trace!("{:?} waiting on in-flight request", request);
|
||||||
|
|
||||||
let _ = other_in_flight_rx.changed().await;
|
let _ = other_incoming_rx.changed().await;
|
||||||
|
|
||||||
// now that we've waited, lets check the cache again
|
// now that we've waited, lets check the cache again
|
||||||
if let Some(cached) = response_cache.read().get(&cache_key) {
|
if let Some(cached) = response_cache.read().get(&cache_key) {
|
||||||
let _ = self.active_requests.remove(&cache_key);
|
let _ = self.incoming_requests.remove(&cache_key);
|
||||||
let _ = in_flight_tx.send(false);
|
let _ = incoming_tx.send(false);
|
||||||
|
|
||||||
// TODO: emit a stat
|
// TODO: emit a stat
|
||||||
trace!(
|
trace!(
|
||||||
@ -379,77 +379,21 @@ impl Web3ProxyApp {
|
|||||||
drop(response_cache);
|
drop(response_cache);
|
||||||
|
|
||||||
// TODO: needing to remove manually here makes me think we should do this differently
|
// TODO: needing to remove manually here makes me think we should do this differently
|
||||||
let _ = self.active_requests.remove(&cache_key);
|
let _ = self.incoming_requests.remove(&cache_key);
|
||||||
let _ = in_flight_tx.send(false);
|
let _ = incoming_tx.send(false);
|
||||||
|
|
||||||
response
|
response
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// send now since we aren't going to cache an error response
|
// send now since we aren't going to cache an error response
|
||||||
let _ = in_flight_tx.send(false);
|
let _ = incoming_tx.send(false);
|
||||||
|
|
||||||
// TODO: move this to a helper function?
|
JsonRpcForwardedResponse::from_ethers_error(e, request.id)
|
||||||
let code;
|
|
||||||
let message: String;
|
|
||||||
let data;
|
|
||||||
|
|
||||||
match e {
|
|
||||||
ProviderError::JsonRpcClientError(e) => {
|
|
||||||
// TODO: we should check what type the provider is rather than trying to downcast both types of errors
|
|
||||||
if let Some(e) = e.downcast_ref::<HttpClientError>() {
|
|
||||||
match &*e {
|
|
||||||
HttpClientError::JsonRpcError(e) => {
|
|
||||||
code = e.code;
|
|
||||||
message = e.message.clone();
|
|
||||||
data = e.data.clone();
|
|
||||||
}
|
|
||||||
e => {
|
|
||||||
// TODO: improve this
|
|
||||||
code = -32603;
|
|
||||||
message = format!("{}", e);
|
|
||||||
data = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if let Some(e) = e.downcast_ref::<WsClientError>() {
|
|
||||||
match &*e {
|
|
||||||
WsClientError::JsonRpcError(e) => {
|
|
||||||
code = e.code;
|
|
||||||
message = e.message.clone();
|
|
||||||
data = e.data.clone();
|
|
||||||
}
|
|
||||||
e => {
|
|
||||||
// TODO: improve this
|
|
||||||
code = -32603;
|
|
||||||
message = format!("{}", e);
|
|
||||||
data = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
code = -32603;
|
|
||||||
message = format!("{}", e);
|
|
||||||
data = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonRpcForwardedResponse {
|
|
||||||
jsonrpc: "2.0".to_string(),
|
|
||||||
id: request.id,
|
|
||||||
result: None,
|
|
||||||
error: Some(JsonRpcErrorData {
|
|
||||||
code,
|
|
||||||
message,
|
|
||||||
data,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: needing to remove manually here makes me think we should do this differently
|
// TODO: needing to remove manually here makes me think we should do this differently
|
||||||
let _ = self.active_requests.remove(&cache_key);
|
let _ = self.incoming_requests.remove(&cache_key);
|
||||||
|
|
||||||
if response.error.is_some() {
|
if response.error.is_some() {
|
||||||
trace!("Sending error reply: {:?}", response);
|
trace!("Sending error reply: {:?}", response);
|
||||||
@ -458,7 +402,7 @@ impl Web3ProxyApp {
|
|||||||
} else {
|
} else {
|
||||||
trace!("Sending reply: {:?}", response);
|
trace!("Sending reply: {:?}", response);
|
||||||
|
|
||||||
let _ = in_flight_tx.send(false);
|
let _ = incoming_tx.send(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
@ -468,8 +412,8 @@ impl Web3ProxyApp {
|
|||||||
warn!("No servers in sync!");
|
warn!("No servers in sync!");
|
||||||
|
|
||||||
// TODO: needing to remove manually here makes me think we should do this differently
|
// TODO: needing to remove manually here makes me think we should do this differently
|
||||||
let _ = self.active_requests.remove(&cache_key);
|
let _ = self.incoming_requests.remove(&cache_key);
|
||||||
let _ = in_flight_tx.send(false);
|
let _ = incoming_tx.send(false);
|
||||||
|
|
||||||
return Err(anyhow::anyhow!("no servers in sync"));
|
return Err(anyhow::anyhow!("no servers in sync"));
|
||||||
}
|
}
|
||||||
@ -482,8 +426,8 @@ impl Web3ProxyApp {
|
|||||||
sleep(retry_after).await;
|
sleep(retry_after).await;
|
||||||
|
|
||||||
// TODO: needing to remove manually here makes me think we should do this differently
|
// TODO: needing to remove manually here makes me think we should do this differently
|
||||||
let _ = self.active_requests.remove(&cache_key);
|
let _ = self.incoming_requests.remove(&cache_key);
|
||||||
let _ = in_flight_tx.send(false);
|
let _ = incoming_tx.send(false);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use ethers::prelude::{HttpClientError, ProviderError, WsClientError};
|
||||||
use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor};
|
use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::value::RawValue;
|
use serde_json::value::RawValue;
|
||||||
@ -160,6 +161,68 @@ impl fmt::Debug for JsonRpcForwardedResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl JsonRpcForwardedResponse {
|
||||||
|
pub fn from_ethers_error(e: ProviderError, id: Box<serde_json::value::RawValue>) -> Self {
|
||||||
|
// TODO: move turning ClientError into json to a helper function?
|
||||||
|
let code;
|
||||||
|
let message: String;
|
||||||
|
let data;
|
||||||
|
|
||||||
|
match e {
|
||||||
|
ProviderError::JsonRpcClientError(e) => {
|
||||||
|
// TODO: we should check what type the provider is rather than trying to downcast both types of errors
|
||||||
|
if let Some(e) = e.downcast_ref::<HttpClientError>() {
|
||||||
|
match &*e {
|
||||||
|
HttpClientError::JsonRpcError(e) => {
|
||||||
|
code = e.code;
|
||||||
|
message = e.message.clone();
|
||||||
|
data = e.data.clone();
|
||||||
|
}
|
||||||
|
e => {
|
||||||
|
// TODO: improve this
|
||||||
|
code = -32603;
|
||||||
|
message = format!("{}", e);
|
||||||
|
data = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Some(e) = e.downcast_ref::<WsClientError>() {
|
||||||
|
match &*e {
|
||||||
|
WsClientError::JsonRpcError(e) => {
|
||||||
|
code = e.code;
|
||||||
|
message = e.message.clone();
|
||||||
|
data = e.data.clone();
|
||||||
|
}
|
||||||
|
e => {
|
||||||
|
// TODO: improve this
|
||||||
|
code = -32603;
|
||||||
|
message = format!("{}", e);
|
||||||
|
data = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
code = -32603;
|
||||||
|
message = format!("{}", e);
|
||||||
|
data = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
jsonrpc: "2.0".to_string(),
|
||||||
|
id,
|
||||||
|
result: None,
|
||||||
|
error: Some(JsonRpcErrorData {
|
||||||
|
code,
|
||||||
|
message,
|
||||||
|
data,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// JSONRPC Responses can include one or many response objects.
|
/// JSONRPC Responses can include one or many response objects.
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
|
@ -19,8 +19,11 @@ use crate::app::Web3ProxyApp;
|
|||||||
use crate::config::{CliConfig, RpcConfig};
|
use crate::config::{CliConfig, RpcConfig};
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
|
// if RUST_LOG isn't set, configure a default
|
||||||
// TODO: is there a better way to do this?
|
// TODO: is there a better way to do this?
|
||||||
// TODO: what should the default log level be?
|
if std::env::var("RUST_LOG").is_err() {
|
||||||
|
std::env::set_var("RUST_LOG", "info,web3_proxy=debug");
|
||||||
|
}
|
||||||
|
|
||||||
// install global collector configured based on RUST_LOG env var.
|
// install global collector configured based on RUST_LOG env var.
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
|
Loading…
Reference in New Issue
Block a user