mod common;
use std::collections::BTreeMap;
use std::time::Duration;
use crabka_replicator::config::{
ClusterConfig, Delivery, FlowConfig, NamingPolicy, PolicyConfig, ReplicatorConfig, Residency,
Selectors,
};
use crabka_replicator::supervisor::FlowSupervisor;
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn selective_replication_naming_and_residency() {
let source = common::start_broker().await;
let target = common::start_broker().await;
common::create_topic(&source.bootstrap, "orders", 1).await;
common::create_topic(&source.bootstrap, "payments", 1).await;
common::create_topic(&source.bootstrap, "audit.internal", 1).await;
common::create_topic(&source.bootstrap, "customers", 1).await;
common::produce(&source.bootstrap, "orders", b"o1", b"v1").await;
common::produce(&source.bootstrap, "orders", b"o2", b"v2").await;
common::produce(&source.bootstrap, "orders", b"o3", b"v3").await;
common::produce(&source.bootstrap, "payments", b"p1", b"v1").await;
common::produce(&source.bootstrap, "payments", b"p2", b"v2").await;
common::produce(&source.bootstrap, "audit.internal", b"a1", b"v1").await;
common::produce(&source.bootstrap, "audit.internal", b"a2", b"v2").await;
common::produce(&source.bootstrap, "customers", b"c1", b"v1").await;
common::produce(&source.bootstrap, "customers", b"c2", b"v2").await;
let mut clusters = BTreeMap::new();
clusters.insert(
"us-east".to_string(),
ClusterConfig {
bootstrap: source.bootstrap.clone(),
region: "us".to_string(),
zones: vec!["us".to_string()],
},
);
clusters.insert(
"eu-west".to_string(),
ClusterConfig {
bootstrap: target.bootstrap.clone(),
region: "eu".to_string(),
zones: vec!["eu".to_string()],
},
);
let config = ReplicatorConfig {
clusters,
flows: vec![FlowConfig {
from: "us-east".to_string(),
to: "eu-west".to_string(),
topics: Selectors {
include: vec![
"orders".to_string(),
"payments".to_string(),
"customers".to_string(),
],
exclude: vec!["*.internal".to_string()],
},
groups: Selectors::default(),
naming: NamingPolicy::Default,
delivery: Delivery::AtLeastOnce,
}],
policies: vec![PolicyConfig {
name: "keep-pii-in-eu".to_string(),
topics: vec!["customers".to_string()],
residency: Some(Residency {
allow_zones: vec!["gdpr".to_string()],
deny_zones: vec![],
}),
}],
};
let sup = FlowSupervisor::run(config).await.unwrap();
common::await_count(
&target.bootstrap,
"us-east.orders",
3,
Duration::from_secs(20),
)
.await;
common::await_count(
&target.bootstrap,
"us-east.payments",
2,
Duration::from_secs(20),
)
.await;
tokio::time::sleep(Duration::from_secs(2)).await;
assert_eq!(
common::count(&target.bootstrap, "us-east.orders").await,
3,
"orders: expected 3 replicated records"
);
assert_eq!(
common::count(&target.bootstrap, "us-east.payments").await,
2,
"payments: expected 2 replicated records"
);
assert_eq!(
common::count(&target.bootstrap, "us-east.audit.internal").await,
0,
"audit.internal: should be excluded by *.internal selector"
);
assert_eq!(
common::count(&target.bootstrap, "us-east.customers").await,
0,
"customers: should be blocked by residency policy (no gdpr zone on target)"
);
sup.shutdown().await;
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn active_active_loop_prevention() {
let a = common::start_broker().await;
let b = common::start_broker().await;
common::create_topic(&a.bootstrap, "orders", 1).await;
common::produce(&a.bootstrap, "orders", b"k1", b"v1").await;
common::produce(&a.bootstrap, "orders", b"k2", b"v2").await;
common::produce(&a.bootstrap, "orders", b"k3", b"v3").await;
common::create_topic(&b.bootstrap, "pings", 1).await;
common::produce(&b.bootstrap, "pings", b"p1", b"v1").await;
let mut clusters = BTreeMap::new();
clusters.insert(
"ca".to_string(),
ClusterConfig {
bootstrap: a.bootstrap.clone(),
region: "us".to_string(),
zones: vec!["us".to_string()],
},
);
clusters.insert(
"cb".to_string(),
ClusterConfig {
bootstrap: b.bootstrap.clone(),
region: "eu".to_string(),
zones: vec!["eu".to_string()],
},
);
let wildcard_sel = Selectors {
include: vec!["*".to_string()],
exclude: vec![],
};
let config = ReplicatorConfig {
clusters,
flows: vec![
FlowConfig {
from: "ca".to_string(),
to: "cb".to_string(),
topics: wildcard_sel.clone(),
groups: Selectors::default(),
naming: NamingPolicy::Default,
delivery: Delivery::AtLeastOnce,
},
FlowConfig {
from: "cb".to_string(),
to: "ca".to_string(),
topics: wildcard_sel,
groups: Selectors::default(),
naming: NamingPolicy::Default,
delivery: Delivery::AtLeastOnce,
},
],
policies: vec![],
};
let sup = FlowSupervisor::run(config).await.unwrap();
common::await_count(&b.bootstrap, "ca.orders", 3, Duration::from_secs(20)).await;
common::await_count(&a.bootstrap, "cb.pings", 1, Duration::from_secs(20)).await;
tokio::time::sleep(Duration::from_secs(3)).await;
assert_eq!(
common::count(&a.bootstrap, "cb.ca.orders").await,
0,
"loop prevention: cb.ca.orders must not exist on cluster ca"
);
assert_eq!(
common::count(&a.bootstrap, "orders").await,
3,
"original orders on ca must remain at 3"
);
sup.shutdown().await;
}