rs-zero 0.2.6

Rust-first microservice framework inspired by go-zero engineering practices
Documentation
#![cfg(feature = "cache-redis")]

use rs_zero::cache::CacheKey;
use rs_zero::cache_redis::{
    RedisNodeConfig, RedisShardChoice, RedisShardPicker, RedisShardedCacheConfig,
    RedisShardedCacheStore,
};

#[test]
fn sharded_config_rejects_missing_nodes() {
    let error = RedisShardedCacheConfig::default()
        .validate()
        .expect_err("missing nodes");
    assert!(error.to_string().contains("at least one redis shard"));
}

#[test]
fn sharded_store_selects_stable_node_for_same_key() {
    let store = RedisShardedCacheStore::new(RedisShardedCacheConfig {
        nodes: vec![
            RedisNodeConfig::new("redis-a", "redis://127.0.0.1:6379"),
            RedisNodeConfig::new("redis-b", "redis://127.0.0.1:6380"),
        ],
        ..RedisShardedCacheConfig::default()
    })
    .expect("store");
    let key = CacheKey::new(store.namespace(), ["users", "42"]);

    let first = store.shard_name_for_key(&key).expect("first shard");
    let second = store.shard_name_for_key(&key).expect("second shard");
    assert_eq!(first, second);
}

#[test]
fn weighted_picker_prefers_heavier_node_over_many_keys() {
    let picker = RedisShardPicker::new(vec![
        RedisShardChoice {
            name: "light".to_string(),
            weight: 1,
        },
        RedisShardChoice {
            name: "heavy".to_string(),
            weight: 8,
        },
    ])
    .expect("picker");

    let mut heavy = 0;
    let mut light = 0;
    for index in 0..512 {
        match picker
            .select_name(&format!("cache:key:{index}"))
            .expect("node")
        {
            "heavy" => heavy += 1,
            "light" => light += 1,
            other => panic!("unexpected shard {other}"),
        }
    }

    assert!(heavy > light, "heavy={heavy} light={light}");
}