cloudflare / cloudflare/pingora

Create an example of constructing a load balancer with extensions included

Open
#545 3 comments 2 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
27.4k
Forks
1.7k
Avg merge
6h 22m
Merged PRs (30d)
3

Description

## What is the problem your feature solves, or the need it fulfills?
The current examples appear to all user `from_iter` which bypasses the much more complicated example of constructing a load balancer from static backends with some useful information in the backend `ext` included

## Describe the solution you'd like
Create an example somewhat like the one I pasted below that took me 8 hours to get working.

This will hopefully save others time.

## Describe alternatives you've considered

You could just keep this issue around as the documentation.

## Additional context

Working code:
```
use pingora::lb::{LoadBalancer};
use pingora::lb::selection::weighted::Weighted;
use http::Extensions;
use std::collections::BTreeSet;
use pingora::lb::Backend;
use pingora::lb::discovery::ServiceDiscovery;
use pingora::lb::Backends;
use pingora::lb::discovery::Static;

// this is what we get from the configuration
pub enum LoadBalancerType {
RoundRobin,
}

pub struct UpstreamConfig {
pub uuid: String,
pub socket_addr: Option, // Socket address as string
pub disabled: Option, // Disabled flag, Option to handle absence
pub weight: Option, // Weight for load balancing
}
pub struct UpstreamPoolConfig {
pub uuid: String, // Unique identifier for the pool
pub upstreams: Option>, // List of upstream configurations
pub health_check: Option, // enable health checks - XXX TBD TCP or more complicated HTTP?
pub lb_algo: Option, // Load balancer algorithm
}

pub fn create_static_round_robin_load_balancer(config: &UpstreamPoolConfig) -> Option> {
// Build a BTreeSet of backends from the config
let mut backend_set = BTreeSet::new();
if let Some(upstreams) = &config.upstreams {
for upstream in upstreams {
if let Some(addr_str) = &upstream.socket_addr {
let weight = upstream.weight.unwrap_or(1) as usize;
match Backend::new_with_weight(addr_str, weight) {
Ok(mut backend) => {
let mut extensions = Extensions::new();
extensions.insert(upstream.uuid.clone());
backend.ext = extensions;
backend_set.insert(backend);
}
Err(e) => {
eprintln!("Error creating backend for {}: {}", addr_str, e);
}
}
}
}
}

if backend_set.is_empty() {
return None;
}

// Create the Backends struct
let discovery = Static::new(backend_set);
let backends = Backends::new(discovery as Box);

// Create the load balancer using the Backends struct
let load_balancer = LoadBalancer::from_backends(backends);
use futures::FutureExt;
load_balancer.update()
.now_or_never()
.expect("static should not block")
.expect("static should not error");
Some(load_balancer)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_create_static_round_robin_load_balancer() {
let config = UpstreamPoolConfig {
uuid: "pool1".to_string(),
upstreams: Some(vec![
UpstreamConfig {
uuid: "upstream1".to_string(),
socket_addr: Some("127.0.0.1:8080".to_string()),
disabled: Some(false),
weight: Some(1),
},
UpstreamConfig {
uuid: "upstream2".to_string(),
socket_addr: Some("127.0.0.1:8081".to_string()),
disabled: Some(false),
weight: Some(2),
},
]),
health_check: Some(true),
lb_algo: Some("round_robin".to_string()),
};

let load_balancer = create_static_round_robin_load_balancer(&config);
assert!(load_balancer.is_some());

let load_balancer = load_balancer.unwrap();
let backends = load_balancer.backends().get_backend();

assert_eq!(backends.len(), 2);

let backend1 = backends.iter().find(|b| b.addr.to_string() == "127.0.0.1:8080").unwrap();
assert_eq!(backend1.weight, 1);
assert_eq!(backend1.ext.get::().unwrap(), "upstream1");

let backend2 = backends.iter().find(|b| b.addr.to_string() == "127.0.0.1:8081").unwrap();
assert_eq!(backend2.weight, 2);
assert_eq!(backend2.ext.get::().unwrap(), "upstream2");
}
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.