Skip to content

Commit

Permalink
feat(middleware): move trait to types folder to avoid cycle dependency.
Browse files Browse the repository at this point in the history
  • Loading branch information
andysim3d committed Sep 24, 2024
1 parent 868327d commit 36b3579
Show file tree
Hide file tree
Showing 11 changed files with 121 additions and 42 deletions.
4 changes: 2 additions & 2 deletions crates/pool/src/server/remote/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use async_trait::async_trait;
use ethers::types::{Address, H256};
use futures_util::StreamExt;
use rundler_task::{
grpc::{grpc_metrics::GrpcMetricsLayer, protos::from_bytes},
grpc::{grpc_metrics::HttpMethodExtractor, protos::from_bytes},
metrics::{MetricsLayer, RequestMethodNameInfo},
};
use rundler_types::{
Expand Down Expand Up @@ -81,7 +81,7 @@ pub(crate) async fn spawn_remote_mempool_server(
.set_serving::<OpPoolServer<OpPoolImpl>>()
.await;

let metrics_layer = MetricsLayer::new("op_pool_service".to_string(), "http-grpc".to_string());
let metrics_layer = MetricsLayer::<HttpMethodExtractor, http::Request>::new("op_pool_service".to_string(), "http-grpc".to_string());
let handle = tokio::spawn(async move {
Server::builder()
.layer(metrics_layer)
Expand Down
33 changes: 33 additions & 0 deletions crates/provider/src/traits/metrics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// This file is part of Rundler.
//
// Rundler is free software: you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later version.
//
// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with Rundler.
// If not, see https://www.gnu.org/licenses/.

/// Method extractor
use rundler_types::task::traits::RequestExtractor;
use alloy_json_rpc::RequestPacket;

#[derive(Clone, Copy)]
struct AlloyMethodExtractor;

impl RequestExtractor<RequestPacket> for RPCMethodExtractor {
fn get_method_name(req: &RequestPacket) -> String {
match req {
RequestPacket::Single(request) => {
request.method().to_string()
}
_ => {
// can't extract method name for batch.
"unknown".to_string()
}
}
}
}
11 changes: 7 additions & 4 deletions crates/rpc/src/rpc_metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@
// If not, see https://www.gnu.org/licenses/.

use jsonrpsee::types::Request;
use rundler_task::metrics::RequestMethodNameInfo;
use rundler_types::task::traits::RequestExtractor;

impl RequestMethodNameInfo for Request {
fn get_method_name(&self) -> String {
self.method_name().to_string()
#[derive(Copy)]
struct RPCMethodExtractor;

impl RequestExtractor<Request> for RPCMethodExtractor {
fn get_method_name(req: &Request) -> String {
req.method_name().to_string()
}
}
7 changes: 4 additions & 3 deletions crates/rpc/src/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ use std::{net::SocketAddr, sync::Arc, time::Duration};
use anyhow::{bail, Context};
use async_trait::async_trait;
use jsonrpsee::{
server::{middleware::http::ProxyGetRequestLayer, RpcServiceBuilder, ServerBuilder},
RpcModule,
server::{middleware::http::ProxyGetRequestLayer, RpcServiceBuilder, ServerBuilder}, types::Request, RpcModule
};
use rundler_provider::{EntryPointProvider, Provider};
use rundler_sim::{
Expand Down Expand Up @@ -46,6 +45,7 @@ use crate::{
rpc_metrics,
rundler::{RundlerApi, RundlerApiServer, Settings as RundlerApiSettings},
types::ApiNamespace,
rpc_metrics::RPCMethodExtractor,
};

/// RPC server arguments.
Expand Down Expand Up @@ -187,7 +187,8 @@ where
.layer(ProxyGetRequestLayer::new("/health", "system_health")?)
.timeout(self.args.rpc_timeout);

let rpc_metric_middleware = MetricsLayer::new("rundler-eth-service".to_string(), "rpc".to_string());
let rpc_metric_middleware =
MetricsLayer::<RPCMethodExtractor, Request>::new("rundler-eth-service".to_string(), "rpc".to_string());

let server = ServerBuilder::default()
.set_http_middleware(http_middleware)
Expand Down
1 change: 1 addition & 0 deletions crates/task/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ publish = false
[dependencies]
rundler-provider = { path = "../provider" }
rundler-utils = { path = "../utils" }
rundler-types = { path = "../types" }

alloy-primitives.workspace = true

Expand Down
11 changes: 7 additions & 4 deletions crates/task/src/grpc/grpc_metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@
// You should have received a copy of the GNU General Public License along with Rundler.
// If not, see https://www.gnu.org/licenses/.

use rundler_types::task::traits::RequestExtractor;
use tonic::codegen::http;

use crate::metrics::RequestMethodNameInfo;
/// http request method extractor.
#[derive(Copy, Clone)]
struct HttpMethodExtractor;

impl<Body> RequestMethodNameInfo for http::Request<Body> {
fn get_method_name(&self) -> String {
let method_name = self.uri().path().split('/').last().unwrap_or("unknown");
impl<Body> RequestExtractor<http::Request<Body>> for HttpMethodExtractor {
fn get_method_name(req: &http::Request<Body>) -> String {
let method_name = req.uri().path().split('/').last().unwrap_or("unknown");
method_name.to_string()
}
}
47 changes: 31 additions & 16 deletions crates/task/src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,66 +14,80 @@
//! Middleware for recording metrics for requests.

use std::{
marker::PhantomData,
task::{Context, Poll},
time::{Duration, Instant},
};

use futures::{future::BoxFuture, FutureExt};
use rundler_types::task::traits::RequestExtractor;
use tower::{Layer, Service};

/// Trait to expose request method name.
pub trait RequestMethodNameInfo {
/// Get method name.
fn get_method_name(&self) -> String;
}

/// tower network layer: https://github.com/tower-rs/tower/blob/master/guides/building-a-middleware-from-scratch.md
#[derive(Debug, Clone)]
pub struct MetricsLayer {
pub struct MetricsLayer<T, R> {
service_name: String,
protocal: String,
_request_extractor_: PhantomData<T>,
_request_type_: PhantomData<R>,
}

impl MetricsLayer {
impl<T, R> MetricsLayer<T, R>
where
T: RequestExtractor<R>,
{
/// Initialize a network layer wrappers the metric middleware.
pub fn new(service_name: String, protocal: String) -> Self {
MetricsLayer {
service_name,
protocal,
_request_extractor_: PhantomData,
_request_type_: PhantomData,
}
}
}

impl<S> Layer<S> for MetricsLayer {
type Service = MetricsMiddleware<S>;
impl<S, T, R> Layer<S> for MetricsLayer<T, R>
where
T: RequestExtractor<R>,
{
type Service = MetricsMiddleware<S, T, R>;
fn layer(&self, service: S) -> Self::Service {
MetricsMiddleware::new(service, self.service_name.clone(), self.protocal.clone())
MetricsMiddleware::<S, T, R>::new(service, self.service_name.clone(), self.protocal.clone())
}
}

/// Middleware implementation.
pub struct MetricsMiddleware<S> {
pub struct MetricsMiddleware<S, T, R> {
inner: S,
service_name: String,
protocal: String,
_request_extractor_: PhantomData<T>,
_request_type_: PhantomData<R>,
}

impl<S> MetricsMiddleware<S> {
impl<S, T, R> MetricsMiddleware<S, T, R>
where
T: RequestExtractor<R>,
{
/// Initialize a middleware.
pub fn new(inner: S, service_name: String, protocal: String) -> Self {
Self {
inner: inner,
service_name: service_name.clone(),
protocal: protocal,
_request_extractor_: PhantomData,
_request_type_: PhantomData,
}
}
}

impl<S, Request> Service<Request> for MetricsMiddleware<S>
impl<S, T, Request> Service<Request> for MetricsMiddleware<S, T, Request>
where
S: Service<Request> + Send + Sync + Clone + 'static,
S::Future: Send + Sync + 'static,
Request: RequestMethodNameInfo + Send + Sync + 'static,
T: RequestExtractor<Request> + 'static,
Request: Send + Sync + 'static,
{
type Response = S::Response;
type Error = S::Error;
Expand All @@ -84,7 +98,8 @@ where
}

fn call(&mut self, request: Request) -> Self::Future {
let method_name = request.get_method_name();
let method_name = T::get_method_name(&request);

MethodMetrics::increment_num_requests(
self.service_name.as_str(),
method_name.as_str(),
Expand Down
2 changes: 2 additions & 0 deletions crates/types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@ pub use validation_results::{
parse_validation_data, AggregatorInfo, StakeInfo, ValidationOutput, ValidationReturnInfo,
ValidationRevert,
};

pub mod task;
13 changes: 0 additions & 13 deletions crates/types/src/pool/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,6 @@
// You should have received a copy of the GNU General Public License along with Rundler.
// If not, see https://www.gnu.org/licenses/.

// This file is part of Rundler.
//
// Rundler is free software: you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later version.
//
// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with Rundler.
// If not, see https://www.gnu.org/licenses/.

//! Rundler pool types

mod error;
Expand Down
15 changes: 15 additions & 0 deletions crates/types/src/task/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// This file is part of Rundler.
//
// Rundler is free software: you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later version.
//
// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with Rundler.
// If not, see https://www.gnu.org/licenses/.

//! Rundler task traits.
pub mod traits;
19 changes: 19 additions & 0 deletions crates/types/src/task/traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// This file is part of Rundler.
//
// Rundler is free software: you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later version.
//
// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with Rundler.
// If not, see https://www.gnu.org/licenses/.

/// Trait to expose request method name.

pub trait RequestExtractor<R>: Copy + Sync + Send {
/// Get method name.
fn get_method_name(request: &R) -> String;
}

0 comments on commit 36b3579

Please sign in to comment.