1mod bootstrap_services;
18pub mod configuration;
19#[cfg(unix)]
20mod daemon;
21#[cfg(unix)]
22pub(crate) mod transfer_fd;
23
24use async_trait::async_trait;
25#[cfg(unix)]
26use daemon::daemonize;
27use daggy::NodeIndex;
28use log::{error, info, warn};
29use parking_lot::Mutex;
30#[cfg(all(feature = "dial9", feature = "dial9-worker-s3"))]
31pub use pingora_runtime::Dial9S3UploadOpts;
32use pingora_runtime::{BlockingPoolOpts, Runtime, RuntimeBuilder};
33#[cfg(feature = "dial9")]
34pub use pingora_runtime::{
35 Dial9RuntimeOpts, DEFAULT_DIAL9_MAX_FILE_SIZE, DEFAULT_DIAL9_MAX_TOTAL_SIZE,
36};
37pub use pingora_runtime::{RuntimeMetricsOpts, RuntimeOpts};
38use pingora_timeout::fast_timeout;
39#[cfg(feature = "sentry")]
40use sentry::ClientOptions;
41use std::sync::Arc;
42use std::thread;
43use std::time::{Instant, SystemTime};
44#[cfg(unix)]
45use tokio::signal::unix;
46use tokio::sync::{broadcast, watch};
47use tokio::time::{sleep, Duration};
48
49use crate::prelude::background_service;
50use crate::server::bootstrap_services::{Bootstrap, BootstrapService};
51use crate::services::{
52 DependencyGraph, ServiceHandle, ServiceReadyNotifier, ServiceReadyWatch, ServiceWithDependents,
53};
54use configuration::{Opt, ServerConf};
55use std::collections::HashMap;
56#[cfg(unix)]
57pub use transfer_fd::Fds;
58
59use pingora_error::{Error, ErrorType, Result};
60
61const EXIT_TIMEOUT: u64 = 60 * 5;
64const CLOSE_TIMEOUT: u64 = 5;
67
68enum ShutdownType {
69 Graceful,
70 Quick,
71}
72
73pub(crate) struct ServiceWrapper {
75 ready_notifier: Option<ServiceReadyNotifier>,
76 service: Box<dyn ServiceWithDependents>,
77 service_handle: ServiceHandle,
78}
79
80#[derive(Clone, Debug)]
82#[non_exhaustive]
83pub enum ExecutionPhase {
84 Setup,
86
87 Bootstrap,
91
92 BootstrapComplete,
94
95 Running,
97
98 GracefulUpgradeTransferringFds,
102
103 GracefulUpgradeCloseTimeout,
106
107 GracefulTerminate,
109
110 ShutdownStarted,
112
113 ShutdownGracePeriod,
115
116 ShutdownRuntimes,
118
119 Terminated,
121}
122
123pub type ShutdownWatch = watch::Receiver<bool>;
126#[cfg(unix)]
127pub type ListenFds = Arc<Mutex<Fds>>;
128
129#[derive(Debug)]
131pub enum ShutdownSignal {
132 GracefulUpgrade,
135 GracefulTerminate,
138 FastShutdown,
140}
141
142#[async_trait]
145pub trait ShutdownSignalWatch {
146 async fn recv(&self) -> ShutdownSignal;
148}
149
150#[cfg(unix)]
156pub struct UnixShutdownSignalWatch;
157
158#[cfg(unix)]
159#[async_trait]
160impl ShutdownSignalWatch for UnixShutdownSignalWatch {
161 async fn recv(&self) -> ShutdownSignal {
162 let mut graceful_upgrade_signal = unix::signal(unix::SignalKind::quit()).unwrap();
163 let mut graceful_terminate_signal = unix::signal(unix::SignalKind::terminate()).unwrap();
164 let mut fast_shutdown_signal = unix::signal(unix::SignalKind::interrupt()).unwrap();
165
166 tokio::select! {
167 _ = graceful_upgrade_signal.recv() => {
168 ShutdownSignal::GracefulUpgrade
169 },
170 _ = graceful_terminate_signal.recv() => {
171 ShutdownSignal::GracefulTerminate
172 },
173 _ = fast_shutdown_signal.recv() => {
174 ShutdownSignal::FastShutdown
175 },
176 }
177 }
178}
179
180pub struct RunArgs {
182 #[cfg(unix)]
184 pub shutdown_signal: Box<dyn ShutdownSignalWatch>,
185}
186
187impl Default for RunArgs {
188 #[cfg(unix)]
189 fn default() -> Self {
190 Self {
191 shutdown_signal: Box::new(UnixShutdownSignalWatch),
192 }
193 }
194
195 #[cfg(windows)]
196 fn default() -> Self {
197 Self {}
198 }
199}
200
201pub struct Server {
207 services: HashMap<NodeIndex, ServiceWrapper>,
208 shutdown_watch: watch::Sender<bool>,
209 shutdown_recv: ShutdownWatch,
211
212 execution_phase_watch: broadcast::Sender<ExecutionPhase>,
216
217 dependencies: Arc<Mutex<DependencyGraph>>,
219
220 bootstrap: Arc<Mutex<Bootstrap>>,
222
223 pub configuration: Arc<ServerConf>,
225 pub options: Option<Opt>,
227}
228
229impl Server {
232 pub fn watch_execution_phase(&self) -> broadcast::Receiver<ExecutionPhase> {
236 self.execution_phase_watch.subscribe()
237 }
238
239 #[cfg(unix)]
240 async fn main_loop(&self, run_args: RunArgs) -> ShutdownType {
241 self.execution_phase_watch
244 .send(ExecutionPhase::Running)
245 .ok();
246
247 match run_args.shutdown_signal.recv().await {
248 ShutdownSignal::FastShutdown => {
249 info!("SIGINT received, exiting");
250 ShutdownType::Quick
251 }
252 ShutdownSignal::GracefulTerminate => {
253 info!("SIGTERM received, gracefully exiting");
255 info!("Broadcasting graceful shutdown");
257 match self.shutdown_watch.send(true) {
258 Ok(_) => {
259 info!("Graceful shutdown started!");
260 }
261 Err(e) => {
262 error!("Graceful shutdown broadcast failed: {e}");
263 }
264 }
265 info!("Broadcast graceful shutdown complete");
266
267 self.execution_phase_watch
268 .send(ExecutionPhase::GracefulTerminate)
269 .ok();
270
271 ShutdownType::Graceful
272 }
273 ShutdownSignal::GracefulUpgrade => {
274 info!("SIGQUIT received, sending socks and gracefully exiting");
277
278 self.execution_phase_watch
279 .send(ExecutionPhase::GracefulUpgradeTransferringFds)
280 .ok();
281
282 let sent_fds = {
283 let fds = self.listen_fds();
284 let fds = fds.lock();
285 if fds.is_empty() {
286 info!("No socks to send, shutting down.");
287 false
288 } else {
289 info!("Trying to send socks");
290 match fds.send_to_sock(self.configuration.as_ref().upgrade_sock.as_str()) {
291 Ok(_) => {
292 info!("listener sockets sent");
293 }
294 Err(e) => {
295 error!("Unable to send listener sockets to new process: {e}");
296 #[cfg(all(not(debug_assertions), feature = "sentry"))]
297 sentry::capture_error(&e);
298 }
299 }
300 true
301 }
302 };
303 if sent_fds {
304 self.execution_phase_watch
305 .send(ExecutionPhase::GracefulUpgradeCloseTimeout)
306 .ok();
307 sleep(Duration::from_secs(CLOSE_TIMEOUT)).await;
308 }
309 info!("Broadcasting graceful shutdown");
310 match self.shutdown_watch.send(true) {
312 Ok(_) => {
313 info!("Graceful shutdown started!");
314 }
315 Err(e) => {
316 error!("Graceful shutdown broadcast failed: {e}");
317 return ShutdownType::Graceful;
319 }
320 }
321 info!("Broadcast graceful shutdown complete");
322 ShutdownType::Graceful
323 }
324 }
325 }
326
327 #[cfg(windows)]
328 async fn main_loop(&self, _run_args: RunArgs) -> ShutdownType {
329 self.execution_phase_watch
332 .send(ExecutionPhase::Running)
333 .ok();
334
335 match tokio::signal::ctrl_c().await {
336 Ok(()) => {
337 info!("Ctrl+C received, gracefully exiting");
338 info!("Broadcasting graceful shutdown");
340 match self.shutdown_watch.send(true) {
341 Ok(_) => {
342 info!("Graceful shutdown started!");
343 }
344 Err(e) => {
345 error!("Graceful shutdown broadcast failed: {e}");
346 }
347 }
348 info!("Broadcast graceful shutdown complete");
349
350 self.execution_phase_watch
351 .send(ExecutionPhase::GracefulTerminate)
352 .ok();
353
354 ShutdownType::Graceful
355 }
356 Err(e) => {
357 error!("Unable to listen for shutdown signal: {}", e);
358 ShutdownType::Quick
359 }
360 }
361 }
362
363 #[cfg(feature = "sentry")]
364 #[cfg_attr(docsrs, doc(cfg(feature = "sentry")))]
365 pub fn set_sentry_config(&mut self, sentry_config: ClientOptions) {
369 self.bootstrap.lock().set_sentry_config(Some(sentry_config));
370 }
371
372 #[cfg(unix)]
374 fn listen_fds(&self) -> ListenFds {
375 self.bootstrap.lock().get_fds()
376 }
377
378 #[cfg(unix)]
380 fn collect_listen_addresses(&self) -> Option<std::collections::HashSet<String>> {
381 self.services
382 .values()
383 .try_fold(std::collections::HashSet::new(), |mut addrs, wrapper| {
384 addrs.extend(wrapper.service.listen_addresses()?);
385 Some(addrs)
386 })
387 }
388
389 #[allow(clippy::too_many_arguments)]
390 fn run_service(
391 mut service: Box<dyn ServiceWithDependents>,
392 #[cfg(unix)] fds: ListenFds,
393 shutdown: ShutdownWatch,
394 threads: usize,
395 work_stealing: bool,
396 listeners_per_fd: usize,
397 ready_notifier: ServiceReadyNotifier,
398 dependency_watches: Vec<ServiceReadyWatch>,
399 blocking_opts: BlockingPoolOpts,
400 runtime_opts: RuntimeOpts,
401 ) -> Runtime
402{
405 let service_runtime = Server::create_runtime(
406 service.name(),
407 threads,
408 work_stealing,
409 blocking_opts,
410 runtime_opts,
411 );
412 let service_name = service.name().to_string();
413 service_runtime.get_handle().spawn(async move {
414 let mut time_waited_opt: Option<Duration> = None;
416 for mut watch in dependency_watches {
417 let start = SystemTime::now();
418
419 if watch.wait_for(|&ready| ready).await.is_err() {
420 error!(
421 "Service '{}' dependency channel closed before ready",
422 service_name
423 );
424 }
425
426 *time_waited_opt.get_or_insert_default() += start.elapsed().unwrap_or_default()
427 }
428
429 if let Some(time_waited) = time_waited_opt {
430 service.on_startup_delay(time_waited);
431 }
432
433 service
435 .start_service(
436 #[cfg(unix)]
437 Some(fds),
438 shutdown,
439 listeners_per_fd,
440 ready_notifier,
441 )
442 .await;
443 info!("service '{}' exited.", service_name);
444 });
445 service_runtime
446 }
447
448 pub fn new_with_opt_and_conf(raw_opt: impl Into<Option<Opt>>, mut conf: ServerConf) -> Server {
456 let opt = raw_opt.into();
457 if let Some(opts) = &opt {
458 if let Some(c) = opts.conf.as_ref() {
459 warn!("Ignoring command line argument using '{c}' as configuration, and using provided configuration instead.");
460 }
461 conf.merge_with_opt(opts);
462 }
463
464 let (tx, rx) = watch::channel(false);
465
466 let execution_phase_watch = broadcast::channel(100).0;
467 let bootstrap = Arc::new(Mutex::new(Bootstrap::new(
468 &opt,
469 &conf,
470 &execution_phase_watch,
471 )));
472
473 Server {
474 services: Default::default(),
475 shutdown_watch: tx,
476 shutdown_recv: rx,
477 execution_phase_watch,
478 configuration: Arc::new(conf),
479 options: opt,
480 dependencies: Arc::new(Mutex::new(DependencyGraph::new())),
481 bootstrap,
482 }
483 }
484
485 pub fn new(opt: impl Into<Option<Opt>>) -> Result<Server> {
493 let opt = opt.into();
494 let (tx, rx) = watch::channel(false);
495
496 let execution_phase_watch = broadcast::channel(100).0;
497 let conf = if let Some(opt) = opt.as_ref() {
498 opt.conf.as_ref().map_or_else(
499 || {
500 ServerConf::new_with_opt_override(opt).ok_or_else(|| {
502 Error::explain(ErrorType::ReadError, "Conf generation failed")
503 })
504 },
505 |_| {
506 ServerConf::load_yaml_with_opt_override(opt)
508 },
509 )
510 } else {
511 ServerConf::new()
512 .ok_or_else(|| Error::explain(ErrorType::ReadError, "Conf generation failed"))
513 }?;
514
515 let bootstrap = Arc::new(Mutex::new(Bootstrap::new(
516 &opt,
517 &conf,
518 &execution_phase_watch,
519 )));
520
521 Ok(Server {
522 services: Default::default(),
523 shutdown_watch: tx,
524 shutdown_recv: rx,
525 execution_phase_watch,
526 configuration: Arc::new(conf),
527 options: opt,
528 dependencies: Arc::new(Mutex::new(DependencyGraph::new())),
529 bootstrap,
530 })
531 }
532
533 pub fn add_service(&mut self, service: impl ServiceWithDependents + 'static) -> ServiceHandle {
547 self.add_boxed_service(Box::new(service))
548 }
549
550 pub fn add_boxed_service(
564 &mut self,
565 service_box: Box<dyn ServiceWithDependents>,
566 ) -> ServiceHandle {
567 let name = service_box.name().to_string();
568
569 let (tx, rx) = watch::channel(false);
571
572 let id = self.dependencies.lock().add_node(name.clone(), rx.clone());
573
574 let service_handle = ServiceHandle::new(id, name, rx, &self.dependencies);
575
576 let wrapper = ServiceWrapper {
577 ready_notifier: Some(ServiceReadyNotifier::new(tx)),
578 service: service_box,
579 service_handle: service_handle.clone(),
580 };
581
582 self.services.insert(id, wrapper);
583
584 service_handle
585 }
586
587 pub fn add_services(
591 &mut self,
592 services: Vec<Box<dyn ServiceWithDependents>>,
593 ) -> Vec<ServiceHandle> {
594 services
595 .into_iter()
596 .map(|service| self.add_boxed_service(service))
597 .collect()
598 }
599
600 pub fn bootstrap(&mut self) {
605 self.bootstrap.lock().bootstrap();
606 }
607
608 pub fn bootstrap_as_a_service(&mut self) -> ServiceHandle {
613 let bootstrap_service =
614 background_service("Bootstrap Service", BootstrapService::new(&self.bootstrap));
615
616 self.add_service(bootstrap_service)
617 }
618
619 pub fn run_forever(self) -> ! {
627 self.run(RunArgs::default());
628
629 std::process::exit(0)
630 }
631
632 pub fn run(mut self, run_args: RunArgs) {
643 info!("Server starting");
644
645 let conf = self.configuration.as_ref();
646
647 #[cfg(unix)]
648 if conf.daemon {
649 info!("Daemonizing the server");
650 fast_timeout::pause_for_fork();
651 let daemonize_result = daemonize(&self.configuration);
652 fast_timeout::unpause();
653 if let Some(pid) = daemonize_result.notify_parent_pid {
656 self.bootstrap.lock().set_notify_parent_pid(pid);
657 }
658 }
659
660 #[cfg(windows)]
661 if conf.daemon {
662 panic!("Daemonizing under windows is not supported");
663 }
664
665 let blocking_opts = BlockingPoolOpts {
666 max_threads: conf.max_blocking_threads,
667 thread_keep_alive: conf.blocking_threads_ttl_seconds.map(Duration::from_secs),
668 };
669 let runtime_opts = conf.runtime_opts();
670 if conf.runtime_enable_alt_timer && !conf.work_stealing {
671 warn!("runtime_enable_alt_timer is ignored when work_stealing is disabled");
672 }
673 fast_timeout::set_fast_timeout_to_tokio_threshold(
676 conf.fast_timeout_to_tokio_threshold_seconds
677 .map(Duration::from_secs),
678 );
679
680 #[cfg(feature = "sentry")]
687 self.bootstrap.lock().start_sentry();
688
689 let mut runtimes: Vec<(Runtime, String)> = Vec::new();
691
692 #[cfg(unix)]
694 if let Some(expected) = self.collect_listen_addresses() {
695 self.bootstrap.lock().set_expected_listen_addrs(expected);
696 }
697
698 let startup_order = match self.dependencies.lock().topological_sort() {
700 Ok(order) => order,
701 Err(e) => {
702 error!("Failed to determine service startup order: {}", e);
703 std::process::exit(1);
704 }
705 };
706
707 let service_names: Vec<String> = startup_order
709 .iter()
710 .map(|(_, service)| service.name.clone())
711 .collect();
712 info!("Starting services in dependency order: {:?}", service_names);
713
714 for (service_id, service) in startup_order {
716 let mut wrapper = match self.services.remove(&service_id) {
717 Some(w) => w,
718 None => {
719 warn!(
720 "Service ID {:?}-{} in startup order but not found",
721 service_id, service.name
722 );
723 continue;
724 }
725 };
726
727 let threads = wrapper.service.threads().unwrap_or(conf.threads);
728 let name = wrapper.service.name().to_string();
729 let service_runtime_opts = wrapper
730 .service
731 .runtime_opts_override(&runtime_opts)
732 .unwrap_or_else(|| runtime_opts.clone());
733
734 let dependencies = self
736 .dependencies
737 .lock()
738 .get_dependencies(wrapper.service_handle.id);
739
740 let ready_notifier = wrapper
744 .ready_notifier
745 .take()
746 .expect("Service notifier should exist");
747
748 if !dependencies.is_empty() {
749 info!(
750 "Service '{name}' will wait for dependencies: {:?}",
751 dependencies.iter().map(|s| &s.name).collect::<Vec<_>>()
752 );
753 } else {
754 info!("Starting service: {}", name);
755 }
756
757 let dependency_watches = dependencies
758 .iter()
759 .map(|s| s.ready_watch.clone())
760 .collect::<Vec<_>>();
761
762 let runtime = Server::run_service(
763 wrapper.service,
764 #[cfg(unix)]
765 self.listen_fds(),
766 self.shutdown_recv.clone(),
767 threads,
768 conf.work_stealing,
769 self.configuration.listener_tasks_per_fd,
770 ready_notifier,
771 dependency_watches,
772 blocking_opts.clone(),
773 service_runtime_opts,
774 );
775 runtimes.push((runtime, name));
776 }
777
778 let server_runtime = Server::create_runtime(
781 "Server",
782 1,
783 true,
784 BlockingPoolOpts::default(),
785 RuntimeOpts::default(),
786 );
787 #[cfg(unix)]
788 let shutdown_type = server_runtime
789 .get_handle()
790 .block_on(self.main_loop(run_args));
791 #[cfg(windows)]
792 let shutdown_type = server_runtime
793 .get_handle()
794 .block_on(self.main_loop(run_args));
795
796 self.execution_phase_watch
797 .send(ExecutionPhase::ShutdownStarted)
798 .ok();
799
800 if matches!(shutdown_type, ShutdownType::Graceful) {
801 self.execution_phase_watch
802 .send(ExecutionPhase::ShutdownGracePeriod)
803 .ok();
804
805 let exit_timeout = self
806 .configuration
807 .as_ref()
808 .grace_period_seconds
809 .unwrap_or(EXIT_TIMEOUT);
810 info!("Graceful shutdown: grace period {}s starts", exit_timeout);
811 thread::sleep(Duration::from_secs(exit_timeout));
812 info!("Graceful shutdown: grace period ends");
813 }
814
815 let shutdown_timeout = match shutdown_type {
817 ShutdownType::Quick => Duration::from_secs(0),
818 ShutdownType::Graceful => Duration::from_secs(
819 self.configuration
820 .as_ref()
821 .graceful_shutdown_timeout_seconds
822 .unwrap_or(5),
823 ),
824 };
825
826 self.execution_phase_watch
827 .send(ExecutionPhase::ShutdownRuntimes)
828 .ok();
829
830 let shutdowns: Vec<_> = runtimes
831 .into_iter()
832 .map(|(rt, name)| {
833 info!("Waiting for runtimes to exit!");
834 let join = thread::spawn(move || {
835 let start = Instant::now();
836 rt.shutdown_timeout(shutdown_timeout);
837 start.elapsed()
838 });
839 (join, name)
840 })
841 .collect();
842 for (shutdown, name) in shutdowns {
843 info!("Waiting for service runtime {name} to exit");
844 match shutdown.join() {
845 Ok(elapsed) if !shutdown_timeout.is_zero() && elapsed >= shutdown_timeout => {
846 warn!("Service runtime {name} did not exit within {shutdown_timeout:?}")
847 }
848 Ok(elapsed) => info!("Service runtime {name} exited after {elapsed:?}"),
849 Err(e) => error!("Failed to shutdown service runtime {name}: {e:?}"),
850 }
851 }
852 info!("All runtimes exited, exiting now");
853
854 self.execution_phase_watch
855 .send(ExecutionPhase::Terminated)
856 .ok();
857 }
858
859 fn create_runtime(
860 name: &str,
861 threads: usize,
862 work_steal: bool,
863 blocking_opts: BlockingPoolOpts,
864 runtime_opts: RuntimeOpts,
865 ) -> Runtime {
866 RuntimeBuilder::new(threads, name)
867 .work_steal(work_steal)
868 .blocking_pool_opts(blocking_opts)
869 .runtime_opts(runtime_opts)
870 .build()
871 }
872}
873
874#[cfg(all(test, unix))]
875mod listen_address_tests {
876 use super::*;
877
878 struct UnknownService;
879
880 #[async_trait]
881 impl crate::services::Service for UnknownService {
882 fn name(&self) -> &str {
883 "unknown"
884 }
885 }
886
887 #[test]
888 fn unknown_service_disables_inherited_fd_cleanup() {
889 let mut server = Server::new_with_opt_and_conf(None, ServerConf::default());
890 server.bootstrap_as_a_service();
891 assert_eq!(server.collect_listen_addresses(), Some(Default::default()));
892
893 server.add_service(UnknownService);
894 assert_eq!(server.collect_listen_addresses(), None);
895 }
896}