pingora_load_balancing/
background.rs

1// Copyright 2025 Cloudflare, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Implement [BackgroundService] for [LoadBalancer]
16
17use std::time::{Duration, Instant};
18
19use super::{BackendIter, BackendSelection, LoadBalancer};
20use async_trait::async_trait;
21use pingora_core::services::background::BackgroundService;
22
23#[async_trait]
24impl<S: Send + Sync + BackendSelection + 'static> BackgroundService for LoadBalancer<S>
25where
26    S::Iter: BackendIter,
27{
28    async fn start(&self, shutdown: pingora_core::server::ShutdownWatch) -> () {
29        // 136 years
30        const NEVER: Duration = Duration::from_secs(u32::MAX as u64);
31        let mut now = Instant::now();
32        // run update and health check once
33        let mut next_update = now;
34        let mut next_health_check = now;
35        loop {
36            if *shutdown.borrow() {
37                return;
38            }
39
40            if next_update <= now {
41                // TODO: log err
42                let _ = self.update().await;
43                next_update = now + self.update_frequency.unwrap_or(NEVER);
44            }
45
46            if next_health_check <= now {
47                self.backends
48                    .run_health_check(self.parallel_health_check)
49                    .await;
50                next_health_check = now + self.health_check_frequency.unwrap_or(NEVER);
51            }
52
53            if self.update_frequency.is_none() && self.health_check_frequency.is_none() {
54                return;
55            }
56            let to_wake = std::cmp::min(next_update, next_health_check);
57            tokio::time::sleep_until(to_wake.into()).await;
58            now = Instant::now();
59        }
60    }
61}