redrop

Keeping Configuration Out of Your Code

redrop::set_concurrency_limit(8) is a perfectly normal function call, and you could just write it directly in main. For a lot of small programs, that's genuinely fine — don't over-engineer this if you don't need to.

But the concurrency limit is exactly the kind of value that tends to need retuning without a code change: what's right for your laptop during development is probably wrong for a production server, and what's right today might be wrong once traffic grows. Recompiling and redeploying just to change one number is friction you don't need. So the showcase crate keeps it in a small external file instead, and loads it at startup.

redrop.toml

Sitting next to showcase/src/main.rs, there's a redrop.toml:

[collector]
concurrency_limit = 8

That's the entire tuning surface, in one place, in a format anyone on the team can edit without reading Rust.

Loading it

We describe the shape we expect with a couple of small serde structs:

use serde::Deserialize;

#[derive(Deserialize)]
struct RedropConfig {
    collector: CollectorConfig,
}

#[derive(Deserialize)]
struct CollectorConfig {
    concurrency_limit: usize,
}

and read it once, at the top of main:

fn load_config(path: &std::path::Path) -> RedropConfig {
    let text = std::fs::read_to_string(path)
        .unwrap_or_else(|err| panic!("couldn't read {}: {err}", path.display()));
    toml::from_str(&text)
        .unwrap_or_else(|err| panic!("couldn't parse {}: {err}", path.display()))
}

We go one small step further and let the path itself be overridden by an environment variable, falling back to the file shipped next to the crate:

fn config_path() -> std::path::PathBuf {
    std::env::var_os("REDROP_CONFIG")
        .map(std::path::PathBuf::from)
        .unwrap_or_else(|| {
            std::path::Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/redrop.toml")).to_owned()
        })
}

That's what makes this genuinely "configuration elsewhere" rather than just "a constant in a different file": the same compiled binary can pick up different tuning per environment — a redrop.toml baked into a container image, a path mounted from a config map, whatever your deployment looks like — without anyone touching main.rs.

Then, applying it is one line, right where we saw it in chapter 4:

let config = load_config(&config_path());
redrop::set_concurrency_limit(config.collector.concurrency_limit);

Try changing it

REDROP_CONFIG=/tmp/my-redrop.toml cargo run -p redrop-showcase

Drop the concurrency_limit to 1 in a copy of the file and re-run — you'll see the flushes in the output stop overlapping and run strictly one at a time. Nothing about LogShipper or its async_drop changed; only the number in the file did.

← Handling Load: Concurrency and the Background Daemon Shutting Down Cleanly →