#![allow(dead_code)]
use crate::client::Channel;
use std::sync::Arc;
use std::sync::atomic::{AtomicU8, AtomicUsize, Ordering};
const STATE_READY: u8 = 0;
const STATE_TRANSIENT_FAILURE: u8 = 1;
const STATE_UNHEALTHY: u8 = 2;
#[derive(Clone, Debug)]
pub(crate) struct ServerConnection {
inner: Arc<Inner>,
}
#[derive(Debug)]
struct Inner {
address: String,
channel: Channel,
state: AtomicU8,
active_requests: AtomicUsize,
}
#[must_use = "if unused the request count will decrement immediately"]
pub(crate) struct ActiveRequestGuard {
inner: Arc<Inner>,
}
impl Drop for ActiveRequestGuard {
fn drop(&mut self) {
let _ =
self.inner
.active_requests
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |val| {
Some(val.saturating_sub(1))
});
}
}
impl ServerConnection {
pub(crate) fn new(address: String, channel: Channel) -> Self {
Self {
inner: Arc::new(Inner {
address,
channel,
state: AtomicU8::new(STATE_READY),
active_requests: AtomicUsize::new(0),
}),
}
}
pub(crate) fn address(&self) -> &str {
&self.inner.address
}
pub(crate) fn channel(&self) -> &Channel {
&self.inner.channel
}
pub(crate) fn is_healthy(&self) -> bool {
self.inner.state.load(Ordering::Acquire) == STATE_READY
}
pub(crate) fn is_transient_failure(&self) -> bool {
self.inner.state.load(Ordering::Acquire) == STATE_TRANSIENT_FAILURE
}
pub(crate) fn set_ready(&self) {
self.inner.state.store(STATE_READY, Ordering::Release);
}
pub(crate) fn set_transient_failure(&self) {
self.inner
.state
.store(STATE_TRANSIENT_FAILURE, Ordering::Release);
}
pub(crate) fn set_unhealthy(&self) {
self.inner.state.store(STATE_UNHEALTHY, Ordering::Release);
}
pub(crate) fn increment_active_requests(&self) {
self.inner.active_requests.fetch_add(1, Ordering::Relaxed);
}
pub(crate) fn decrement_active_requests(&self) {
let _ =
self.inner
.active_requests
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |val| {
Some(val.saturating_sub(1))
});
}
pub(crate) fn active_request_count(&self) -> usize {
self.inner.active_requests.load(Ordering::Relaxed)
}
pub(crate) fn acquire_request_guard(&self) -> ActiveRequestGuard {
self.increment_active_requests();
ActiveRequestGuard {
inner: Arc::clone(&self.inner),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Barrier;
use std::thread;
#[derive(Debug)]
struct DummyStub;
impl crate::generated::gapic_dataplane::stub::Spanner for DummyStub {}
fn create_test_connection(address: &str) -> ServerConnection {
let channel = Channel::new_for_test(DummyStub);
ServerConnection::new(address.to_string(), channel)
}
#[test]
fn test_server_connection_health_state_transitions() {
let conn = create_test_connection("10.0.0.1:15000");
assert_eq!(conn.address(), "10.0.0.1:15000");
assert!(conn.is_healthy());
assert!(!conn.is_transient_failure());
conn.set_transient_failure();
assert!(!conn.is_healthy());
assert!(conn.is_transient_failure());
conn.set_unhealthy();
assert!(!conn.is_healthy());
assert!(!conn.is_transient_failure());
conn.set_ready();
assert!(conn.is_healthy());
assert!(!conn.is_transient_failure());
}
#[test]
fn test_server_connection_active_requests_and_underflow_protection() {
let conn = create_test_connection("10.0.0.1:15000");
assert_eq!(conn.active_request_count(), 0);
conn.increment_active_requests();
conn.increment_active_requests();
conn.increment_active_requests();
assert_eq!(conn.active_request_count(), 3);
conn.decrement_active_requests();
assert_eq!(conn.active_request_count(), 2);
conn.decrement_active_requests();
conn.decrement_active_requests();
assert_eq!(conn.active_request_count(), 0);
conn.decrement_active_requests();
conn.decrement_active_requests();
assert_eq!(conn.active_request_count(), 0);
}
#[test]
fn test_server_connection_channel_accessor() {
let conn = create_test_connection("10.0.0.1:15000");
let channel = conn.channel();
assert!(format!("{:?}", channel).contains("Channel"));
}
#[test]
fn test_server_connection_acquire_request_guard() {
let conn = create_test_connection("10.0.0.1:15000");
assert_eq!(conn.active_request_count(), 0);
{
let _guard1 = conn.acquire_request_guard();
assert_eq!(conn.active_request_count(), 1);
{
let _guard2 = conn.acquire_request_guard();
assert_eq!(conn.active_request_count(), 2);
}
assert_eq!(conn.active_request_count(), 1);
}
assert_eq!(conn.active_request_count(), 0);
}
#[test]
fn test_server_connection_concurrent_guards() {
let conn = create_test_connection("10.0.0.1:15000");
let worker_count = 10;
let iterations = 100;
let barrier = Barrier::new(worker_count);
thread::scope(|scope| {
for _ in 0..worker_count {
scope.spawn(|| {
barrier.wait();
for _ in 0..iterations {
let _guard = conn.acquire_request_guard();
assert!(conn.active_request_count() > 0);
}
});
}
});
assert_eq!(conn.active_request_count(), 0);
}
#[tokio::test]
async fn test_server_connection_guard_static_lifetime() {
let conn = create_test_connection("10.0.0.1:15000");
let guard = conn.acquire_request_guard();
assert_eq!(conn.active_request_count(), 1);
let conn_clone = conn.clone();
tokio::spawn(async move {
let _moved_guard = guard;
assert_eq!(conn_clone.active_request_count(), 1);
})
.await
.expect("spawned task should complete");
assert_eq!(conn.active_request_count(), 0);
}
}