mirror of
https://github.com/deuxfleurs-org/garage.git
synced 2026-08-10 06:26:52 +00:00
RPC performance changes
- configurable ping timeout - single, much higher, configurable RPC timeout - no more concurrency semaphore
This commit is contained in:
+1
-1
@@ -45,7 +45,7 @@ tokio = { version = "1.0", default-features = false, features = ["rt", "rt-multi
|
||||
tokio-stream = { version = "0.1", features = ["net"] }
|
||||
opentelemetry = "0.17"
|
||||
|
||||
netapp = { version = "0.5.1", features = ["telemetry"] }
|
||||
netapp = { version = "0.5.2", features = ["telemetry"] }
|
||||
|
||||
hyper = { version = "0.14", features = ["client", "http1", "runtime", "tcp"] }
|
||||
|
||||
|
||||
+1
-18
@@ -1,31 +1,18 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use opentelemetry::{global, metrics::*};
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
/// TableMetrics reference all counter used for metrics
|
||||
pub struct RpcMetrics {
|
||||
pub(crate) _rpc_available_permits: ValueObserver<u64>,
|
||||
|
||||
pub(crate) rpc_counter: Counter<u64>,
|
||||
pub(crate) rpc_timeout_counter: Counter<u64>,
|
||||
pub(crate) rpc_netapp_error_counter: Counter<u64>,
|
||||
pub(crate) rpc_garage_error_counter: Counter<u64>,
|
||||
|
||||
pub(crate) rpc_duration: ValueRecorder<f64>,
|
||||
pub(crate) rpc_queueing_time: ValueRecorder<f64>,
|
||||
}
|
||||
impl RpcMetrics {
|
||||
pub fn new(sem: Arc<Semaphore>) -> Self {
|
||||
pub fn new() -> Self {
|
||||
let meter = global::meter("garage_rpc");
|
||||
RpcMetrics {
|
||||
_rpc_available_permits: meter
|
||||
.u64_value_observer("rpc.available_permits", move |observer| {
|
||||
observer.observe(sem.available_permits() as u64, &[])
|
||||
})
|
||||
.with_description("Number of available RPC permits")
|
||||
.init(),
|
||||
|
||||
rpc_counter: meter
|
||||
.u64_counter("rpc.request_counter")
|
||||
.with_description("Number of RPC requests emitted")
|
||||
@@ -46,10 +33,6 @@ impl RpcMetrics {
|
||||
.f64_value_recorder("rpc.duration")
|
||||
.with_description("Duration of RPCs")
|
||||
.init(),
|
||||
rpc_queueing_time: meter
|
||||
.f64_value_recorder("rpc.queueing_time")
|
||||
.with_description("Time RPC requests were queued for before being sent")
|
||||
.init(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
-29
@@ -7,7 +7,7 @@ use futures::stream::futures_unordered::FuturesUnordered;
|
||||
use futures::stream::StreamExt;
|
||||
use futures_util::future::FutureExt;
|
||||
use tokio::select;
|
||||
use tokio::sync::{watch, Semaphore};
|
||||
use tokio::sync::watch;
|
||||
|
||||
use opentelemetry::KeyValue;
|
||||
use opentelemetry::{
|
||||
@@ -32,32 +32,30 @@ use garage_util::metrics::RecordDuration;
|
||||
use crate::metrics::RpcMetrics;
|
||||
use crate::ring::Ring;
|
||||
|
||||
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
// Don't allow more than 100 concurrent outgoing RPCs.
|
||||
const MAX_CONCURRENT_REQUESTS: usize = 100;
|
||||
// Default RPC timeout = 5 minutes
|
||||
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(300);
|
||||
|
||||
/// Strategy to apply when making RPC
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct RequestStrategy {
|
||||
/// Max time to wait for reponse
|
||||
pub rs_timeout: Duration,
|
||||
/// Min number of response to consider the request successful
|
||||
pub rs_quorum: Option<usize>,
|
||||
/// Should requests be dropped after enough response are received
|
||||
pub rs_interrupt_after_quorum: bool,
|
||||
/// Request priority
|
||||
pub rs_priority: RequestPriority,
|
||||
/// Deactivate timeout for this request
|
||||
pub rs_no_timeout: bool,
|
||||
}
|
||||
|
||||
impl RequestStrategy {
|
||||
/// Create a RequestStrategy with default timeout and not interrupting when quorum reached
|
||||
pub fn with_priority(prio: RequestPriority) -> Self {
|
||||
RequestStrategy {
|
||||
rs_timeout: DEFAULT_TIMEOUT,
|
||||
rs_quorum: None,
|
||||
rs_interrupt_after_quorum: false,
|
||||
rs_priority: prio,
|
||||
rs_no_timeout: false,
|
||||
}
|
||||
}
|
||||
/// Set quorum to be reached for request
|
||||
@@ -65,17 +63,17 @@ impl RequestStrategy {
|
||||
self.rs_quorum = Some(quorum);
|
||||
self
|
||||
}
|
||||
/// Set timeout of the strategy
|
||||
pub fn with_timeout(mut self, timeout: Duration) -> Self {
|
||||
self.rs_timeout = timeout;
|
||||
self
|
||||
}
|
||||
/// Set if requests can be dropped after quorum has been reached
|
||||
/// In general true for read requests, and false for write
|
||||
pub fn interrupt_after_quorum(mut self, interrupt: bool) -> Self {
|
||||
self.rs_interrupt_after_quorum = interrupt;
|
||||
self
|
||||
}
|
||||
/// Deactivate timeout for this request
|
||||
pub fn without_timeout(mut self) -> Self {
|
||||
self.rs_no_timeout = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -86,8 +84,8 @@ struct RpcHelperInner {
|
||||
fullmesh: Arc<FullMeshPeeringStrategy>,
|
||||
background: Arc<BackgroundRunner>,
|
||||
ring: watch::Receiver<Arc<Ring>>,
|
||||
request_buffer_semaphore: Arc<Semaphore>,
|
||||
metrics: RpcMetrics,
|
||||
rpc_timeout: Duration,
|
||||
}
|
||||
|
||||
impl RpcHelper {
|
||||
@@ -96,21 +94,24 @@ impl RpcHelper {
|
||||
fullmesh: Arc<FullMeshPeeringStrategy>,
|
||||
background: Arc<BackgroundRunner>,
|
||||
ring: watch::Receiver<Arc<Ring>>,
|
||||
rpc_timeout: Option<Duration>,
|
||||
) -> Self {
|
||||
let sem = Arc::new(Semaphore::new(MAX_CONCURRENT_REQUESTS));
|
||||
|
||||
let metrics = RpcMetrics::new(sem.clone());
|
||||
let metrics = RpcMetrics::new();
|
||||
|
||||
Self(Arc::new(RpcHelperInner {
|
||||
our_node_id,
|
||||
fullmesh,
|
||||
background,
|
||||
ring,
|
||||
request_buffer_semaphore: sem,
|
||||
metrics,
|
||||
rpc_timeout: rpc_timeout.unwrap_or(DEFAULT_TIMEOUT),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn rpc_timeout(&self) -> Duration {
|
||||
self.0.rpc_timeout
|
||||
}
|
||||
|
||||
pub async fn call<M, N, H, S>(
|
||||
&self,
|
||||
endpoint: &Endpoint<M, H>,
|
||||
@@ -129,13 +130,6 @@ impl RpcHelper {
|
||||
KeyValue::new("to", format!("{:?}", to)),
|
||||
];
|
||||
|
||||
let permit = self
|
||||
.0
|
||||
.request_buffer_semaphore
|
||||
.acquire()
|
||||
.record_duration(&self.0.metrics.rpc_queueing_time, &metric_tags)
|
||||
.await?;
|
||||
|
||||
self.0.metrics.rpc_counter.add(1, &metric_tags);
|
||||
|
||||
let node_id = to.into();
|
||||
@@ -143,10 +137,16 @@ impl RpcHelper {
|
||||
.call_streaming(&node_id, msg, strat.rs_priority)
|
||||
.record_duration(&self.0.metrics.rpc_duration, &metric_tags);
|
||||
|
||||
let timeout = async {
|
||||
if strat.rs_no_timeout {
|
||||
futures::future::pending().await
|
||||
} else {
|
||||
tokio::time::sleep(self.0.rpc_timeout).await
|
||||
}
|
||||
};
|
||||
|
||||
select! {
|
||||
res = rpc_call => {
|
||||
drop(permit);
|
||||
|
||||
if res.is_err() {
|
||||
self.0.metrics.rpc_netapp_error_counter.add(1, &metric_tags);
|
||||
}
|
||||
@@ -158,8 +158,7 @@ impl RpcHelper {
|
||||
|
||||
Ok(res?)
|
||||
}
|
||||
_ = tokio::time::sleep(strat.rs_timeout) => {
|
||||
drop(permit);
|
||||
() = timeout => {
|
||||
self.0.metrics.rpc_timeout_counter.add(1, &metric_tags);
|
||||
Err(Error::Timeout)
|
||||
}
|
||||
|
||||
+12
-4
@@ -37,7 +37,6 @@ use crate::rpc_helper::*;
|
||||
|
||||
const DISCOVERY_INTERVAL: Duration = Duration::from_secs(60);
|
||||
const STATUS_EXCHANGE_INTERVAL: Duration = Duration::from_secs(10);
|
||||
const SYSTEM_RPC_TIMEOUT: Duration = Duration::from_secs(15);
|
||||
|
||||
/// Version tag used for version check upon Netapp connection.
|
||||
/// Cluster nodes with different version tags are deemed
|
||||
@@ -280,6 +279,9 @@ impl System {
|
||||
|
||||
let netapp = NetApp::new(GARAGE_VERSION_TAG, network_key, node_key);
|
||||
let fullmesh = FullMeshPeeringStrategy::new(netapp.clone(), vec![], rpc_public_addr);
|
||||
if let Some(ping_timeout) = config.rpc_ping_timeout_msec {
|
||||
fullmesh.set_ping_timeout_millis(ping_timeout);
|
||||
}
|
||||
|
||||
let system_endpoint = netapp.endpoint(SYSTEM_RPC_PATH.into());
|
||||
|
||||
@@ -317,7 +319,13 @@ impl System {
|
||||
node_status: RwLock::new(HashMap::new()),
|
||||
netapp: netapp.clone(),
|
||||
fullmesh: fullmesh.clone(),
|
||||
rpc: RpcHelper::new(netapp.id.into(), fullmesh, background.clone(), ring.clone()),
|
||||
rpc: RpcHelper::new(
|
||||
netapp.id.into(),
|
||||
fullmesh,
|
||||
background.clone(),
|
||||
ring.clone(),
|
||||
config.rpc_timeout_msec.map(Duration::from_millis),
|
||||
),
|
||||
system_endpoint,
|
||||
replication_factor,
|
||||
rpc_listen_addr: config.rpc_bind_addr,
|
||||
@@ -600,7 +608,7 @@ impl System {
|
||||
.broadcast(
|
||||
&self.system_endpoint,
|
||||
SystemRpc::AdvertiseStatus(local_status),
|
||||
RequestStrategy::with_priority(PRIO_HIGH).with_timeout(SYSTEM_RPC_TIMEOUT),
|
||||
RequestStrategy::with_priority(PRIO_HIGH),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -724,7 +732,7 @@ impl System {
|
||||
&self.system_endpoint,
|
||||
peer,
|
||||
SystemRpc::PullClusterLayout,
|
||||
RequestStrategy::with_priority(PRIO_HIGH).with_timeout(SYSTEM_RPC_TIMEOUT),
|
||||
RequestStrategy::with_priority(PRIO_HIGH),
|
||||
)
|
||||
.await;
|
||||
if let Ok(SystemRpc::AdvertiseClusterLayout(layout)) = resp {
|
||||
|
||||
Reference in New Issue
Block a user