diff --git a/web3_proxy/src/app/mod.rs b/web3_proxy/src/app/mod.rs index ffc38a0b..298e4c1d 100644 --- a/web3_proxy/src/app/mod.rs +++ b/web3_proxy/src/app/mod.rs @@ -1161,6 +1161,8 @@ impl Web3ProxyApp { // TODO: trace/kafka log request.params before we send them to _proxy_request_with_caching which might modify them + // turn some of the Web3ProxyErrors into Ok results + // TODO: move this into a helper function let (code, response_data) = match self ._proxy_request_with_caching( &request.method, @@ -1178,6 +1180,20 @@ impl Web3ProxyApp { (StatusCode::OK, response_data) } + Err(err @ Web3ProxyError::NullJsonRpcResult) => { + request_metadata + .error_response + .store(false, Ordering::Release); + + err.as_response_parts() + } + Err(Web3ProxyError::JsonRpcResponse(response_data)) => { + request_metadata + .error_response + .store(response_data.is_error(), Ordering::Release); + + (StatusCode::OK, response_data) + } Err(err) => { request_metadata .error_response @@ -1706,11 +1722,15 @@ impl Web3ProxyApp { let to_block_num = cache_key.to_block_num().copied(); let cache_jsonrpc_errors = cache_key.cache_errors(); + // don't cache anything larger than 2 MiB + let max_response_cache_bytes = 2 * (1024 ^ 2); // self.config.max_response_cache_bytes; + // TODO: try to fetch out of s3 self .jsonrpc_response_cache .try_get_with::<_, Web3ProxyError>(cache_key.hash(), async { + // TODO: think more about this timeout and test that it works well! let response_data = timeout( backend_request_timetout + Duration::from_millis(100), self.balanced_rpcs @@ -1737,6 +1757,9 @@ impl Web3ProxyApp { if response_data.is_null() { // don't ever cache "null" as a success. its too likely to be a problem Err(Web3ProxyError::NullJsonRpcResult) + } else if response_data.num_bytes() > max_response_cache_bytes { + // don't cache really large requests + Err(Web3ProxyError::JsonRpcResponse(response_data)) } else { // TODO: response data should maybe be Arc>>, but that's more work Ok(response_data) diff --git a/web3_proxy/src/errors.rs b/web3_proxy/src/errors.rs index fa899fe0..cc458215 100644 --- a/web3_proxy/src/errors.rs +++ b/web3_proxy/src/errors.rs @@ -23,7 +23,6 @@ use redis_rate_limiter::redis::RedisError; use redis_rate_limiter::RedisPoolError; use reqwest::header::ToStrError; use rust_decimal::Error as DecimalError; -use serde::Serialize; use serde_json::json; use serde_json::value::RawValue; use siwe::VerificationError; @@ -124,6 +123,10 @@ pub enum Web3ProxyError { #[from(ignore)] NotImplemented(Cow<'static, str>), NoVolatileRedisDatabase, + /// make it easy to skip caching large results + #[error(ignore)] + #[display(fmt = "{:?}", _0)] + JsonRpcResponse(JsonRpcResponseEnum>), /// make it easy to skip caching null results NullJsonRpcResult, OriginRequired, @@ -181,7 +184,7 @@ pub enum Web3ProxyError { impl Web3ProxyError { /// turn the error into an axum response. /// - pub fn as_response_parts(&self) -> (StatusCode, JsonRpcResponseEnum) { + pub fn as_response_parts(&self) -> (StatusCode, JsonRpcResponseEnum>) { // TODO: include a unique request id in the data let (code, err): (StatusCode, JsonRpcErrorData) = match self { Self::Abi(err) => { @@ -761,6 +764,10 @@ impl Web3ProxyError { }, ) } + Self::JsonRpcResponse(response_enum) => { + // TODO: shame we have to clone, but its an Arc so its not terrible + return (StatusCode::OK, response_enum.clone()); + } Self::NullJsonRpcResult => { return (StatusCode::OK, JsonRpcResponseEnum::NullResult); } diff --git a/web3_proxy/src/frontend/authorization.rs b/web3_proxy/src/frontend/authorization.rs index 3b6c123b..968d2ced 100644 --- a/web3_proxy/src/frontend/authorization.rs +++ b/web3_proxy/src/frontend/authorization.rs @@ -464,7 +464,7 @@ impl ResponseOrBytes<'_> { .len(), Self::Bytes(num_bytes) => *num_bytes, Self::Error(x) => { - let (_, x) = x.as_response_parts::<()>(); + let (_, x) = x.as_response_parts(); x.num_bytes() as usize } @@ -579,6 +579,8 @@ impl RequestMetadata { self.response_timestamp .store(Utc::now().timestamp(), atomic::Ordering::Release); + // TODO: set user_error_response and error_response here instead of outside this function + if let Some(kafka_debug_logger) = self.kafka_debug_logger.as_ref() { if let ResponseOrBytes::Response(response) = response { kafka_debug_logger.log_debug_response(response); diff --git a/web3_proxy/src/response_cache.rs b/web3_proxy/src/response_cache.rs index 03409230..6e018d5a 100644 --- a/web3_proxy/src/response_cache.rs +++ b/web3_proxy/src/response_cache.rs @@ -109,6 +109,14 @@ impl JsonRpcResponseEnum { Self::RpcError { num_bytes, .. } => *num_bytes, } } + + pub fn is_error(&self) -> bool { + match self { + Self::NullResult => false, + Self::Result { .. } => false, + Self::RpcError { .. } => true, + } + } } impl JsonRpcResponseEnum> { @@ -157,19 +165,25 @@ impl From> for JsonRpcResponseEnum> { } } -impl TryFrom for JsonRpcResponseEnum { +impl TryFrom for JsonRpcResponseEnum> { type Error = Web3ProxyError; fn try_from(value: Web3ProxyError) -> Result { - if let Web3ProxyError::EthersProvider(ref err) = value { - if let Ok(x) = JsonRpcErrorData::try_from(err) { - let x = x.into(); + match value { + Web3ProxyError::EthersProvider(err) => { + if let Ok(x) = JsonRpcErrorData::try_from(&err) { + let x = x.into(); - return Ok(x); + Ok(x) + } else { + Err(err.into()) + } } + Web3ProxyError::NullJsonRpcResult => Ok(JsonRpcResponseEnum::NullResult), + Web3ProxyError::JsonRpcResponse(x) => Ok(x), + Web3ProxyError::JsonRpcErrorData(err) => Ok(err.into()), + err => Err(err), } - - Err(value) } }