use std::fmt;
use std::net::SocketAddr;
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex, PoisonError};
use std::time::{Duration, Instant};
use crate::client::IngressKind;
use crate::config::{ListenerSource, ProfileId};
use crate::error::ServeError;
use crate::health::{Health, HealthState, ListenerInfo, ListenerState};
use crate::net::ConnectionId;
use crate::net::{ControlLine, ControlState};
use crate::notify::Notifier;
use crate::reload::{ReloadSummary, Reloader, ServerConfig};
use crate::reply::Waker;
use crate::stats::{Counters, Stats};
pub(crate) struct Merged<C: ControlLine> {
owner: Arc<C>,
epoch: crate::sync::AtomicU64,
drain: crate::sync::Mutex<Option<Instant>>,
upgraded: crate::sync::Mutex<std::collections::HashMap<ConnectionId, Arc<dyn Waker>>>,
goodbyes: crate::sync::Mutex<std::collections::HashSet<ConnectionId>>,
}
impl<C: ControlLine> Merged<C> {
pub(crate) fn new(owner: Arc<C>) -> Self {
Merged {
owner,
epoch: crate::sync::AtomicU64::new(0),
drain: crate::sync::Mutex::new(None),
upgraded: crate::sync::Mutex::new(std::collections::HashMap::new()),
goodbyes: crate::sync::Mutex::new(std::collections::HashSet::new()),
}
}
#[cfg(serve_net_loom)]
pub(crate) fn own_deadline(&self) -> Option<Instant> {
self.own()
}
#[cfg(serve_net_loom)]
pub(crate) fn goodbyes_empty(&self) -> bool {
self.goodbyes
.lock()
.unwrap_or_else(PoisonError::into_inner)
.is_empty()
}
pub(crate) fn said_goodbye(&self, connection: ConnectionId) {
self.goodbyes
.lock()
.unwrap_or_else(PoisonError::into_inner)
.remove(&connection);
}
#[cfg(feature = "ws")]
pub(crate) fn register_upgraded(&self, connection: ConnectionId, waker: Arc<dyn Waker>) {
self.upgraded
.lock()
.unwrap_or_else(PoisonError::into_inner)
.insert(connection, waker);
}
#[cfg(feature = "ws")]
pub(crate) fn unregister_upgraded(&self, connection: ConnectionId) {
let mut registry = self.upgraded.lock().unwrap_or_else(PoisonError::into_inner);
registry.remove(&connection);
self.said_goodbye(connection);
drop(registry);
}
fn own(&self) -> Option<Instant> {
*self.drain.lock().unwrap_or_else(PoisonError::into_inner)
}
}
impl<C: ControlLine> ControlLine for Merged<C> {
fn epoch(&self) -> u64 {
self.owner
.epoch()
.wrapping_add(self.epoch.load(Ordering::Acquire))
}
fn state(&self) -> ControlState {
let owner = self.owner.state();
match (self.own(), owner) {
(Some(_), ControlState::Stopping) | (None, _) => owner,
(Some(_), _) => ControlState::Draining,
}
}
fn deadline(&self) -> Option<Instant> {
let owner = match self.owner.state() {
ControlState::Draining => self.owner.deadline(),
_ => None,
};
match (self.own(), owner) {
(Some(a), Some(b)) => Some(a.min(b)),
(a, b) => a.or(b),
}
}
}
pub(crate) trait DrainSwitch: Send + Sync + 'static {
fn request(&self, deadline: Instant);
fn is_draining(&self) -> bool;
fn await_goodbyes(&self, until: Instant);
}
impl<C: ControlLine> DrainSwitch for Merged<C> {
fn request(&self, deadline: Instant) {
let mut own = self.drain.lock().unwrap_or_else(PoisonError::into_inner);
let next = match *own {
Some(d) if d <= deadline => return,
_ => deadline,
};
*own = Some(next);
drop(own);
self.epoch.fetch_add(1, Ordering::AcqRel);
let upgraded: Vec<(ConnectionId, Arc<dyn Waker>)> = {
let registry = self.upgraded.lock().unwrap_or_else(PoisonError::into_inner);
let list: Vec<_> = registry
.iter()
.map(|(id, w)| (*id, Arc::clone(w)))
.collect();
self.goodbyes
.lock()
.unwrap_or_else(PoisonError::into_inner)
.extend(list.iter().map(|(id, _)| *id));
list
};
for (id, waker) in upgraded {
if waker.post(id, crate::reply::WAKE_DRAIN, 0).is_err() {
self.said_goodbye(id);
}
}
}
fn await_goodbyes(&self, until: Instant) {
while Instant::now() < until
&& !self
.goodbyes
.lock()
.unwrap_or_else(PoisonError::into_inner)
.is_empty()
{
std::thread::sleep(Duration::from_millis(1));
}
}
fn is_draining(&self) -> bool {
matches!(
self.state(),
ControlState::Draining | ControlState::Stopping
)
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DrainReport {
pub graceful: u32,
pub killed: u32,
pub requests_finished: u64,
pub refused_while_draining: u64,
pub elapsed: Duration,
pub deadline_hit: bool,
}
impl DrainReport {
fn from_net(r: net_io::DrainReport, counters: &Counters) -> Self {
let (requests_finished, refused_while_draining) = counters.draining();
DrainReport {
graceful: r.graceful,
killed: r.killed,
requests_finished,
refused_while_draining,
elapsed: r.took,
deadline_hit: r.killed > 0,
}
}
}
#[derive(Clone)]
pub struct DrainHandle {
switch: Arc<dyn DrainSwitch>,
handle: net_io::ServerHandle,
notifier: Arc<Notifier>,
}
impl DrainHandle {
pub fn drain(&self, deadline: Duration) {
self.notifier.drain_deadline(deadline);
self.switch.request(Instant::now() + deadline);
self.handle.wake_all();
}
#[must_use]
pub fn is_draining(&self) -> bool {
self.switch.is_draining()
}
}
impl fmt::Debug for DrainHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DrainHandle")
.field("draining", &self.is_draining())
.finish()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct ListenerMeta {
pub(crate) name: String,
pub(crate) ingress: IngressKind,
pub(crate) profile: ProfileId,
pub(crate) public_bind: Option<&'static str>,
pub(crate) source: ListenerSource,
pub(crate) transport: String,
pub(crate) settings: String,
}
impl ListenerMeta {
pub(crate) fn same_settings(&self, other: &ListenerMeta) -> bool {
self.settings == other.settings
&& self.profile == other.profile
&& self.ingress == other.ingress
}
}
pub(crate) struct Parts {
pub(crate) server: net_io::Server,
pub(crate) listeners: Vec<ListenerMeta>,
pub(crate) switch: Arc<dyn DrainSwitch>,
pub(crate) counters: Arc<Counters>,
pub(crate) health: Arc<HealthState>,
pub(crate) notifier: Arc<Notifier>,
pub(crate) reloader: Reloader,
}
const ACCEPT_SETTLE_TICK: Duration = Duration::from_millis(1);
const ACCEPT_SETTLE_QUIET: u32 = 3;
const ACCEPT_SETTLE_MAX: Duration = Duration::from_millis(50);
const GOODBYE_MAX: Duration = Duration::from_millis(250);
const DRAIN_START_MAX: Duration = Duration::from_millis(100);
struct Inner {
server: Mutex<net_io::Server>,
handle: net_io::ServerHandle,
listeners: Mutex<Vec<Option<ListenerMeta>>>,
switch: Arc<dyn DrainSwitch>,
counters: Arc<Counters>,
health: Arc<HealthState>,
notifier: Arc<Notifier>,
reloader: Reloader,
}
impl Inner {
fn server(&self) -> std::sync::MutexGuard<'_, net_io::Server> {
self.server.lock().unwrap_or_else(PoisonError::into_inner)
}
fn metas(&self) -> std::sync::MutexGuard<'_, Vec<Option<ListenerMeta>>> {
self.listeners
.lock()
.unwrap_or_else(PoisonError::into_inner)
}
fn listener_infos(&self, opened: bool) -> Vec<ListenerInfo> {
let door = self.handle.door();
let metas = self.metas();
let server = self.server();
server
.listeners()
.iter()
.filter_map(|l| {
let i = usize::from(l.id.get());
let meta = metas.get(i)?.as_ref()?;
let state = if self.health.listener_failing(i) {
ListenerState::Failing
} else if !opened {
ListenerState::Bound
} else if door == net_io::DoorState::Open {
ListenerState::Accepting
} else {
ListenerState::DoorClosed
};
Some(ListenerInfo {
name: meta.name.clone(),
local: l.local.clone(),
ingress: meta.ingress,
profile: meta.profile,
state,
public_bind_reason: meta.public_bind,
})
})
.collect()
}
fn settle_accepts(&self) {
self.handle.close_door();
let give_up = Instant::now() + ACCEPT_SETTLE_MAX;
let mut last = self.handle.stats().accepted;
let mut quiet = 0;
while quiet < ACCEPT_SETTLE_QUIET && Instant::now() < give_up {
std::thread::sleep(ACCEPT_SETTLE_TICK);
let accepted = self.handle.stats().accepted;
let settled = accepted == last && self.counters.opened() >= accepted;
quiet = if settled { quiet + 1 } else { 0 };
last = accepted;
}
}
fn await_drain_start(&self) {
self.handle.wake_all();
let give_up = Instant::now() + DRAIN_START_MAX;
while !self.notifier.is_drain_started() && Instant::now() < give_up {
std::thread::sleep(ACCEPT_SETTLE_TICK);
}
}
fn local_addrs(&self) -> Vec<(String, SocketAddr)> {
let metas = self.metas();
let server = self.server();
server
.listeners()
.iter()
.filter_map(|l| {
let name = metas
.get(usize::from(l.id.get()))?
.as_ref()
.map_or_else(String::new, |m| m.name.clone());
l.local.socket_addr().map(|a| (name, a))
})
.collect()
}
fn into_server(self) -> net_io::Server {
self.server
.into_inner()
.unwrap_or_else(PoisonError::into_inner)
}
}
#[must_use = "a Bound server accepts nothing until open() is called"]
pub struct Bound {
inner: Inner,
addrs: Vec<(String, SocketAddr)>,
}
impl Bound {
pub(crate) fn new(parts: Parts) -> Self {
let handle = parts.server.handle();
let inner = Inner {
server: Mutex::new(parts.server),
handle,
listeners: Mutex::new(parts.listeners.into_iter().map(Some).collect()),
switch: parts.switch,
counters: parts.counters,
health: parts.health,
notifier: parts.notifier,
reloader: parts.reloader,
};
let addrs = inner.local_addrs();
Bound { inner, addrs }
}
pub fn local_addrs(&self) -> impl Iterator<Item = (&str, SocketAddr)> + '_ {
self.addrs.iter().map(|(name, addr)| (name.as_str(), *addr))
}
#[must_use]
pub fn listeners(&self) -> Vec<ListenerInfo> {
self.inner.listener_infos(false)
}
pub fn open(self) -> Result<Running, ServeError> {
self.inner.server().open_door().map_err(ServeError::Start)?;
self.inner.notifier.ready();
Ok(Running {
inner: Some(self.inner),
})
}
}
impl fmt::Debug for Bound {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Bound")
.field("listeners", &self.local_addrs().collect::<Vec<_>>())
.finish()
}
}
#[must_use = "dropping Running closes every connection at once; call wait() or drain()"]
pub struct Running {
inner: Option<Inner>,
}
impl Running {
fn inner(&self) -> &Inner {
self.inner.as_ref().unwrap_or_else(|| unreachable!())
}
#[must_use]
pub fn local_addr(&self) -> Option<SocketAddr> {
self.inner().local_addrs().first().map(|(_, a)| *a)
}
#[must_use]
pub fn listeners(&self) -> Vec<ListenerInfo> {
self.inner().listener_infos(true)
}
pub fn close_door(&self) {
self.inner().handle.close_door();
}
#[must_use]
pub fn stats(&self) -> Stats {
let inner = self.inner();
let mut s = inner.counters.sum();
s.transport = inner.handle.stats();
s
}
#[must_use]
pub fn health(&self) -> Health {
let inner = self.inner();
let names: Vec<(usize, String)> = inner
.metas()
.iter()
.enumerate()
.filter_map(|(i, m)| m.as_ref().map(|m| (i, m.name.clone())))
.collect();
inner.health.compute(
&names,
&inner.counters,
inner.switch.is_draining(),
inner.notifier.is_failing(),
)
}
pub fn drain(mut self, deadline: Duration) -> DrainReport {
let inner = self.inner.take().unwrap_or_else(|| unreachable!());
inner.notifier.drain_deadline(deadline);
let deadline = Instant::now() + deadline;
inner.settle_accepts();
inner.switch.request(deadline);
inner
.switch
.await_goodbyes(deadline.min(Instant::now() + GOODBYE_MAX));
inner.await_drain_start();
let (counters, notifier) = (Arc::clone(&inner.counters), Arc::clone(&inner.notifier));
let report = DrainReport::from_net(inner.into_server().shutdown(deadline), &counters);
notifier.drain_finished(&report);
report
}
#[must_use]
pub fn drain_handle(&self) -> DrainHandle {
let inner = self.inner();
DrainHandle {
switch: Arc::clone(&inner.switch),
handle: inner.handle.clone(),
notifier: Arc::clone(&inner.notifier),
}
}
pub fn wait(mut self) -> DrainReport {
let inner = self.inner.take().unwrap_or_else(|| unreachable!());
let (counters, notifier) = (Arc::clone(&inner.counters), Arc::clone(&inner.notifier));
let report = DrainReport::from_net(inner.into_server().wait_for_stop(), &counters);
notifier.drain_finished(&report);
report
}
pub(crate) fn vitals(&self) -> (Arc<Counters>, Arc<Notifier>) {
let inner = self.inner();
(Arc::clone(&inner.counters), Arc::clone(&inner.notifier))
}
#[must_use]
pub fn server_handle(&self) -> net_io::ServerHandle {
self.inner().handle.clone()
}
pub fn reload(&self, next: ServerConfig) -> Result<ReloadSummary, ServeError> {
let inner = self.inner();
if inner.switch.is_draining() {
return Err(ServeError::Draining);
}
let mut server = inner.server();
let mut metas = inner.metas();
inner.reloader.run(&mut server, &mut metas, next)
}
}
impl Drop for Running {
fn drop(&mut self) {
if let Some(inner) = self.inner.take() {
let notifier = Arc::clone(&inner.notifier);
let _ = inner.into_server().shutdown(Instant::now());
notifier.finish();
}
}
}
impl fmt::Debug for Running {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_struct("Running");
if let Some(inner) = &self.inner {
d.field("listeners", &inner.local_addrs());
}
d.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::net::SimpleControl;
#[test]
fn test_merged_line_drain_and_owner_stop() {
let owner = Arc::new(SimpleControl::new());
let line = Merged::new(Arc::clone(&owner));
let e0 = line.epoch();
assert_eq!(line.state(), ControlState::Normal);
let soon = Instant::now() + Duration::from_secs(5);
line.request(soon);
assert!(line.epoch() > e0);
assert_eq!(line.state(), ControlState::Draining);
assert_eq!(line.deadline(), Some(soon));
let e1 = line.epoch();
line.request(soon + Duration::from_secs(10));
assert_eq!(line.epoch(), e1);
line.request(soon - Duration::from_secs(1));
assert_eq!(line.deadline(), Some(soon - Duration::from_secs(1)));
owner.set(ControlState::Stopping, None);
assert_eq!(line.state(), ControlState::Stopping);
}
}