Skip to main content

Module canary

Module canary 

Source
Expand description

Weighted canary / A-B traffic splitting middleware.

CanaryLayer implements Middleware and distributes incoming requests across a set of backends according to configurable weights, using the same smooth weighted round-robin (SWRR) algorithm nginx uses: each backend has a current_weight that accumulates its configured weight every selection and is decremented by the total weight whenever it’s picked. That spreads high-weight backends evenly through the sequence instead of bursting them consecutively — weights 5, 1, 1 select roughly A A B A C A A (repeating), never five As in a row, unlike a flat pre-expanded rotation list would.

Backends are contacted over plain HTTP/1.1, or over TLS when the backend URL uses an https://, h2s://, or grpcs:// scheme (requires the http-client or http2 feature — both pull in rustls). If a backend is unavailable the next one in the fallback order is tried; after exhausting all backends the middleware returns 502 Bad Gateway.

A group’s members can also come from a crate::service_discovery::BackendPool instead of a fixed URL — see WeightedPool / CanaryLayer::add_pool — so e.g. “10% of traffic to the canary group” keeps working as pods in that group come and go, without touching the weight itself.

Weights can be replaced at runtime with CanaryLayer::update — clone the layer before wrapping it to keep a handle for later:

use rust_web_server::app::App;
use rust_web_server::core::New;
use rust_web_server::canary::{CanaryLayer, WeightedBackend};
use rust_web_server::middleware::WithMiddleware;

// 75 % of traffic → stable, 25 % → canary
let layer = CanaryLayer::new(vec![
    WeightedBackend::new("http://stable:8080", 3),
    WeightedBackend::new("http://canary:8080", 1),
])
.path_prefix("/api");

let handle = layer.clone(); // keep this — `layer` moves into `.wrap(...)`
let app = WithMiddleware::new(App::new()).wrap(layer);

// Later — e.g. from an admin endpoint or a rollout script — shift more
// traffic to canary without restarting the process:
handle.update(
    vec![
        WeightedBackend::new("http://stable:8080", 1),
        WeightedBackend::new("http://canary:8080", 1),
    ],
    vec![],
);

Structs§

CanaryLayer
Weighted traffic-splitting proxy middleware.
WeightedBackend
A single fixed backend URL together with a relative traffic weight.
WeightedPool
A dynamically-discovered group of backends (a BackendPool) together with a relative traffic weight for the group as a whole. Which specific pool member answers a given request is a plain round-robin over pool.backends() at the time of selection — the weight only controls how often this group is picked relative to other groups/backends, not which member within it.