内存优化

This commit is contained in:
xiuting.xu 2026-05-12 16:45:56 +08:00
parent 4c6b441753
commit ff711a2fe1
15 changed files with 152 additions and 96 deletions

View File

@ -4,6 +4,10 @@
# SSH example: 10.0.0.12:22 # SSH example: 10.0.0.12:22
RPKI_RTR_SERVER_ADDR=rpki-rtr-tcp:323 RPKI_RTR_SERVER_ADDR=rpki-rtr-tcp:323
# RTR protocol version used as client command second argument (supported: 0,1,2)
RPKI_RTR_PROTOCOL_VERSION=2
# TLS server name used by --server-name in TLS mode # TLS server name used by --server-name in TLS mode
# Must match server certificate SAN dNSName. # Must match server certificate SAN dNSName.
RPKI_RTR_TLS_SERVER_NAME=localhost RPKI_RTR_TLS_SERVER_NAME=localhost

View File

@ -3,7 +3,7 @@ version: "3.9"
services: services:
rtr-client-1: rtr-client-1:
image: rpki-rtr-debug-client:latest image: rpki-rtr-debug-client:latest
command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "2", "reset", "--keep-after-error", "--summary-only"] command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "${RPKI_RTR_PROTOCOL_VERSION:-2}", "reset", "--keep-after-error", "--summary-only"]
volumes: volumes:
- ../../logs/client:/app/logs - ../../logs/client:/app/logs
restart: no restart: no
@ -12,7 +12,7 @@ services:
rtr-client-2: rtr-client-2:
image: rpki-rtr-debug-client:latest image: rpki-rtr-debug-client:latest
command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "2", "reset", "--keep-after-error", "--summary-only"] command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "${RPKI_RTR_PROTOCOL_VERSION:-2}", "reset", "--keep-after-error", "--summary-only"]
volumes: volumes:
- ../../logs/client:/app/logs - ../../logs/client:/app/logs
restart: no restart: no
@ -21,7 +21,7 @@ services:
rtr-client-3: rtr-client-3:
image: rpki-rtr-debug-client:latest image: rpki-rtr-debug-client:latest
command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "2", "reset", "--keep-after-error", "--summary-only"] command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "${RPKI_RTR_PROTOCOL_VERSION:-2}", "reset", "--keep-after-error", "--summary-only"]
volumes: volumes:
- ../../logs/client:/app/logs - ../../logs/client:/app/logs
restart: no restart: no
@ -30,7 +30,7 @@ services:
rtr-client-4: rtr-client-4:
image: rpki-rtr-debug-client:latest image: rpki-rtr-debug-client:latest
command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "2", "reset", "--keep-after-error", "--summary-only"] command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "${RPKI_RTR_PROTOCOL_VERSION:-2}", "reset", "--keep-after-error", "--summary-only"]
volumes: volumes:
- ../../logs/client:/app/logs - ../../logs/client:/app/logs
restart: no restart: no
@ -39,7 +39,7 @@ services:
rtr-client-5: rtr-client-5:
image: rpki-rtr-debug-client:latest image: rpki-rtr-debug-client:latest
command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "2", "reset", "--keep-after-error", "--summary-only"] command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "${RPKI_RTR_PROTOCOL_VERSION:-2}", "reset", "--keep-after-error", "--summary-only"]
volumes: volumes:
- ../../logs/client:/app/logs - ../../logs/client:/app/logs
restart: no restart: no

View File

@ -9,7 +9,7 @@ services:
command: command:
[ [
"${RPKI_RTR_SERVER_ADDR:-rpki-rtr-ssh:22}", "${RPKI_RTR_SERVER_ADDR:-rpki-rtr-ssh:22}",
"2", "${RPKI_RTR_PROTOCOL_VERSION:-2}",
"reset", "reset",
"--ssh", "--ssh",
"--ssh-user", "--ssh-user",

View File

@ -9,7 +9,7 @@ services:
command: command:
[ [
"${RPKI_RTR_SERVER_ADDR:-rpki-rtr-ssh:22}", "${RPKI_RTR_SERVER_ADDR:-rpki-rtr-ssh:22}",
"2", "${RPKI_RTR_PROTOCOL_VERSION:-2}",
"reset", "reset",
"--ssh", "--ssh",
"--ssh-user", "--ssh-user",

View File

@ -4,7 +4,7 @@ services:
context: ../.. context: ../..
dockerfile: deploy/client/Dockerfile dockerfile: deploy/client/Dockerfile
image: rpki-rtr-debug-client:latest image: rpki-rtr-debug-client:latest
command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "2", "reset", "--keep-after-error", "--summary-only"] command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "${RPKI_RTR_PROTOCOL_VERSION:-2}", "reset", "--keep-after-error", "--summary-only"]
volumes: volumes:
- ../../logs/client:/app/logs - ../../logs/client:/app/logs
restart: no restart: no

View File

@ -9,7 +9,7 @@ services:
command: command:
[ [
"${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tls:324}", "${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tls:324}",
"2", "${RPKI_RTR_PROTOCOL_VERSION:-2}",
"reset", "reset",
"--tls", "--tls",
"--ca-cert", "--ca-cert",

View File

@ -4,7 +4,7 @@ services:
context: ../.. context: ../..
dockerfile: deploy/client/Dockerfile dockerfile: deploy/client/Dockerfile
image: rpki-rtr-debug-client:latest image: rpki-rtr-debug-client:latest
command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "2", "reset", "--keep-after-error", "--summary-only"] command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "${RPKI_RTR_PROTOCOL_VERSION:-2}", "reset", "--keep-after-error", "--summary-only"]
volumes: volumes:
- ../../logs/client:/app/logs - ../../logs/client:/app/logs
restart: no restart: no

8
deploy/server/.env Normal file
View File

@ -0,0 +1,8 @@
# Host directory containing CCR files to mount into the server container.
RPKI_RTR_CCR_HOST_DIR=../../data
# In-container directory used by rpki_rtr as CCR input directory.
RPKI_RTR_CCR_DIR=/app/data
# Max retained delta count in RTR cache.
RPKI_RTR_MAX_DELTA=10

View File

@ -25,14 +25,15 @@ services:
# Optional: enable password authentication in addition to publickey # Optional: enable password authentication in addition to publickey
# RPKI_RTR_SSH_PASSWORD: "test-password" # RPKI_RTR_SSH_PASSWORD: "test-password"
RPKI_RTR_DB_PATH: "/app/rtr-db" RPKI_RTR_DB_PATH: "/app/rtr-db"
RPKI_RTR_CCR_DIR: "/app/data" RPKI_RTR_CCR_DIR: "${RPKI_RTR_CCR_DIR:-/app/data}"
RPKI_RTR_SLURM_DIR: "/app/slurm" RPKI_RTR_SLURM_DIR: "/app/slurm"
RPKI_RTR_STRICT_CCR_VALIDATION: "false" RPKI_RTR_STRICT_CCR_VALIDATION: "false"
RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS: "300" RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS: "300"
RPKI_RTR_MAX_DELTA: "${RPKI_RTR_MAX_DELTA:-10}"
RPKI_RTR_MAX_CONCURRENT_HANDSHAKES: "128" RPKI_RTR_MAX_CONCURRENT_HANDSHAKES: "128"
RUST_LOG: "info" RUST_LOG: "info"
volumes: volumes:
- ../../data:/app/data:ro - ${RPKI_RTR_CCR_HOST_DIR:-../../data}:${RPKI_RTR_CCR_DIR:-/app/data}:ro
- ../../rtr-db:/app/rtr-db - ../../rtr-db:/app/rtr-db
- ../../data:/app/slurm:ro - ../../data:/app/slurm:ro
- ${RPKI_RTR_SSH_KEYS_VOLUME:-/etc/ssh:/host-ssh:ro} - ${RPKI_RTR_SSH_KEYS_VOLUME:-/etc/ssh:/host-ssh:ro}

View File

@ -15,15 +15,16 @@ services:
RPKI_RTR_ENABLE_SSH: "false" RPKI_RTR_ENABLE_SSH: "false"
RPKI_RTR_TCP_ADDR: "0.0.0.0:323" RPKI_RTR_TCP_ADDR: "0.0.0.0:323"
RPKI_RTR_DB_PATH: "/app/rtr-db" RPKI_RTR_DB_PATH: "/app/rtr-db"
RPKI_RTR_CCR_DIR: "/app/data" RPKI_RTR_CCR_DIR: "${RPKI_RTR_CCR_DIR:-/app/data}"
RPKI_RTR_SLURM_DIR: "/app/slurm" RPKI_RTR_SLURM_DIR: "/app/slurm"
RPKI_RTR_STRICT_CCR_VALIDATION: "false" RPKI_RTR_STRICT_CCR_VALIDATION: "false"
RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS: "60" RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS: "60"
RPKI_RTR_MAX_DELTA: "${RPKI_RTR_MAX_DELTA:-10}"
RPKI_RTR_MAX_CONNECTIONS: "100000" RPKI_RTR_MAX_CONNECTIONS: "100000"
RPKI_RTR_MAX_CONCURRENT_HANDSHAKES: "128" RPKI_RTR_MAX_CONCURRENT_HANDSHAKES: "128"
RUST_LOG: "info" RUST_LOG: "info"
volumes: volumes:
- ../../data:/app/data:ro - ${RPKI_RTR_CCR_HOST_DIR:-../../data}:${RPKI_RTR_CCR_DIR:-/app/data}:ro
- ../../rtr-db:/app/rtr-db - ../../rtr-db:/app/rtr-db
- ../../data:/app/slurm:ro - ../../data:/app/slurm:ro
- ../../logs/server:/app/logs - ../../logs/server:/app/logs

View File

@ -21,14 +21,15 @@ services:
RPKI_RTR_TLS_CLIENT_CA_PATH: "/app/certs/client-ca.crt" RPKI_RTR_TLS_CLIENT_CA_PATH: "/app/certs/client-ca.crt"
RPKI_RTR_ENFORCE_TLS_CLIENT_SAN_IP_MATCH: "false" RPKI_RTR_ENFORCE_TLS_CLIENT_SAN_IP_MATCH: "false"
RPKI_RTR_DB_PATH: "/app/rtr-db" RPKI_RTR_DB_PATH: "/app/rtr-db"
RPKI_RTR_CCR_DIR: "/app/data" RPKI_RTR_CCR_DIR: "${RPKI_RTR_CCR_DIR:-/app/data}"
RPKI_RTR_SLURM_DIR: "/app/slurm" RPKI_RTR_SLURM_DIR: "/app/slurm"
RPKI_RTR_STRICT_CCR_VALIDATION: "false" RPKI_RTR_STRICT_CCR_VALIDATION: "false"
RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS: "300" RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS: "300"
RPKI_RTR_MAX_DELTA: "${RPKI_RTR_MAX_DELTA:-10}"
RPKI_RTR_MAX_CONCURRENT_HANDSHAKES: "128" RPKI_RTR_MAX_CONCURRENT_HANDSHAKES: "128"
RUST_LOG: "info" RUST_LOG: "info"
volumes: volumes:
- ../../data:/app/data:ro - ${RPKI_RTR_CCR_HOST_DIR:-../../data}:${RPKI_RTR_CCR_DIR:-/app/data}:ro
- ../../rtr-db:/app/rtr-db - ../../rtr-db:/app/rtr-db
- ../../data:/app/slurm:ro - ../../data:/app/slurm:ro
- ../../tests/fixtures/tls:/app/certs:ro - ../../tests/fixtures/tls:/app/certs:ro

View File

@ -18,10 +18,11 @@ services:
RPKI_RTR_TCP_ADDR: "0.0.0.0:323" RPKI_RTR_TCP_ADDR: "0.0.0.0:323"
RPKI_RTR_TLS_ADDR: "0.0.0.0:324" RPKI_RTR_TLS_ADDR: "0.0.0.0:324"
RPKI_RTR_DB_PATH: "/app/rtr-db" RPKI_RTR_DB_PATH: "/app/rtr-db"
RPKI_RTR_CCR_DIR: "/app/data" RPKI_RTR_CCR_DIR: "${RPKI_RTR_CCR_DIR:-/app/data}"
RPKI_RTR_SLURM_DIR: "/app/slurm" RPKI_RTR_SLURM_DIR: "/app/slurm"
RPKI_RTR_STRICT_CCR_VALIDATION: "false" RPKI_RTR_STRICT_CCR_VALIDATION: "false"
RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS: "300" RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS: "300"
RPKI_RTR_MAX_DELTA: "${RPKI_RTR_MAX_DELTA:-10}"
RPKI_RTR_MAX_CONCURRENT_HANDSHAKES: "128" RPKI_RTR_MAX_CONCURRENT_HANDSHAKES: "128"
RUST_LOG: "info" RUST_LOG: "info"
# SSH mode example: # SSH mode example:
@ -35,7 +36,7 @@ services:
# Optional: enable password auth in addition to publickey # Optional: enable password auth in addition to publickey
# RPKI_RTR_SSH_PASSWORD: "test-password" # RPKI_RTR_SSH_PASSWORD: "test-password"
volumes: volumes:
- ../../data:/app/data:ro - ${RPKI_RTR_CCR_HOST_DIR:-../../data}:${RPKI_RTR_CCR_DIR:-/app/data}:ro
- ../../rtr-db:/app/rtr-db - ../../rtr-db:/app/rtr-db
- ../../data:/app/slurm:ro - ../../data:/app/slurm:ro
- ../../logs/server:/app/logs - ../../logs/server:/app/logs

28
src/rtr/cache/core.rs vendored
View File

@ -578,33 +578,7 @@ fn merge_deltas_minimally(current_serial: u32, deltas: &[Arc<Delta>]) -> Delta {
} }
fn project_snapshot_for_version(snapshot: &Snapshot, version: u8) -> Snapshot { fn project_snapshot_for_version(snapshot: &Snapshot, version: u8) -> Snapshot {
let mut payloads = Vec::new(); snapshot.project_for_version(version)
for payload in snapshot.payloads() {
if let Some(projected) = project_payload_for_version(&payload, version) {
payloads.push(projected);
}
}
Snapshot::from_payloads(payloads)
}
fn project_payload_for_version(payload: &Payload, version: u8) -> Option<Payload> {
match payload {
Payload::RouteOrigin(origin) => Some(Payload::RouteOrigin(origin.clone())),
Payload::RouterKey(key) => {
if version >= 1 {
Some(Payload::RouterKey(key.clone()))
} else {
None
}
}
Payload::Aspa(aspa) => {
if version >= 2 {
Some(Payload::Aspa(aspa.clone()))
} else {
None
}
}
}
} }
fn estimate_snapshot_payload_wire_size(snapshot: &Snapshot) -> usize { fn estimate_snapshot_payload_wire_size(snapshot: &Snapshot) -> usize {

157
src/rtr/cache/model.rs vendored
View File

@ -1,4 +1,4 @@
use std::collections::{BTreeMap, BTreeSet}; use std::collections::BTreeMap;
use std::sync::{Arc, OnceLock}; use std::sync::{Arc, OnceLock};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -69,9 +69,9 @@ impl<'de> Deserialize<'de> for DualTime {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Snapshot { pub struct Snapshot {
origins: BTreeSet<RouteOrigin>, origins: Arc<Vec<RouteOrigin>>,
router_keys: BTreeSet<RouterKey>, router_keys: Arc<Vec<RouterKey>>,
aspas: BTreeSet<Aspa>, aspas: Arc<Vec<Aspa>>,
created_at: DualTime, created_at: DualTime,
origins_hash: [u8; 32], origins_hash: [u8; 32],
router_keys_hash: [u8; 32], router_keys_hash: [u8; 32],
@ -83,14 +83,26 @@ pub struct Snapshot {
impl Snapshot { impl Snapshot {
pub fn new( pub fn new(
origins: BTreeSet<RouteOrigin>, origins: Vec<RouteOrigin>,
router_keys: BTreeSet<RouterKey>, router_keys: Vec<RouterKey>,
aspas: BTreeSet<Aspa>, aspas: Vec<Aspa>,
) -> Self {
Self::from_shared_parts(
Arc::new(sorted_dedup(origins)),
Arc::new(sorted_dedup(router_keys)),
Arc::new(normalize_aspas(aspas)),
)
}
fn from_shared_parts(
origins: Arc<Vec<RouteOrigin>>,
router_keys: Arc<Vec<RouterKey>>,
aspas: Arc<Vec<Aspa>>,
) -> Self { ) -> Self {
let mut snapshot = Snapshot { let mut snapshot = Snapshot {
origins, origins,
router_keys, router_keys,
aspas: normalize_aspas(aspas), aspas,
created_at: DualTime::now(), created_at: DualTime::now(),
origins_hash: [0u8; 32], origins_hash: [0u8; 32],
router_keys_hash: [0u8; 32], router_keys_hash: [0u8; 32],
@ -105,21 +117,21 @@ impl Snapshot {
} }
pub fn empty() -> Self { pub fn empty() -> Self {
Self::new(BTreeSet::new(), BTreeSet::new(), BTreeSet::new()) Self::new(Vec::new(), Vec::new(), Vec::new())
} }
pub fn from_payloads(payloads: Vec<Payload>) -> Self { pub fn from_payloads(payloads: Vec<Payload>) -> Self {
let mut origins = BTreeSet::new(); let mut origins = Vec::new();
let mut router_keys = BTreeSet::new(); let mut router_keys = Vec::new();
let mut aspas = Vec::new(); let mut aspas = Vec::new();
for p in payloads { for p in payloads {
match p { match p {
Payload::RouteOrigin(o) => { Payload::RouteOrigin(o) => {
origins.insert(o); origins.push(o);
} }
Payload::RouterKey(k) => { Payload::RouterKey(k) => {
router_keys.insert(k); router_keys.push(k);
} }
Payload::Aspa(a) => { Payload::Aspa(a) => {
aspas.push(a); aspas.push(a);
@ -130,6 +142,21 @@ impl Snapshot {
Snapshot::new(origins, router_keys, normalize_aspas(aspas)) Snapshot::new(origins, router_keys, normalize_aspas(aspas))
} }
pub fn project_for_version(&self, version: u8) -> Self {
let router_keys = if version >= 1 {
self.router_keys.clone()
} else {
Arc::new(Vec::new())
};
let aspas = if version >= 2 {
self.aspas.clone()
} else {
Arc::new(Vec::new())
};
Self::from_shared_parts(self.origins.clone(), router_keys, aspas)
}
pub fn recompute_hashes(&mut self) { pub fn recompute_hashes(&mut self) {
self.origins_hash = self.compute_origins_hash(); self.origins_hash = self.compute_origins_hash();
self.router_keys_hash = self.compute_router_keys_hash(); self.router_keys_hash = self.compute_router_keys_hash();
@ -182,21 +209,23 @@ impl Snapshot {
let mut withdrawn = Vec::new(); let mut withdrawn = Vec::new();
if !self.same_origins(new_snapshot) { if !self.same_origins(new_snapshot) {
for origin in new_snapshot.origins.difference(&self.origins) { diff_sorted(
announced.push(Payload::RouteOrigin(origin.clone())); self.origins.as_slice(),
} new_snapshot.origins.as_slice(),
for origin in self.origins.difference(&new_snapshot.origins) { &mut announced,
withdrawn.push(Payload::RouteOrigin(origin.clone())); &mut withdrawn,
} Payload::RouteOrigin,
);
} }
if !self.same_router_keys(new_snapshot) { if !self.same_router_keys(new_snapshot) {
for key in new_snapshot.router_keys.difference(&self.router_keys) { diff_sorted(
announced.push(Payload::RouterKey(key.clone())); self.router_keys.as_slice(),
} new_snapshot.router_keys.as_slice(),
for key in self.router_keys.difference(&new_snapshot.router_keys) { &mut announced,
withdrawn.push(Payload::RouterKey(key.clone())); &mut withdrawn,
} Payload::RouterKey,
);
} }
if !self.same_aspas(new_snapshot) { if !self.same_aspas(new_snapshot) {
@ -268,16 +297,16 @@ impl Snapshot {
self.snapshot_hash == other.snapshot_hash self.snapshot_hash == other.snapshot_hash
} }
pub fn origins(&self) -> &BTreeSet<RouteOrigin> { pub fn origins(&self) -> &[RouteOrigin] {
&self.origins self.origins.as_ref()
} }
pub fn router_keys(&self) -> &BTreeSet<RouterKey> { pub fn router_keys(&self) -> &[RouterKey] {
&self.router_keys self.router_keys.as_ref()
} }
pub fn aspas(&self) -> &BTreeSet<Aspa> { pub fn aspas(&self) -> &[Aspa] {
&self.aspas self.aspas.as_ref()
} }
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
@ -369,15 +398,15 @@ fn build_snapshot_payloads_for_rtr(snapshot: &Snapshot) -> Vec<Payload> {
} }
fn dedup_payloads(payloads: &mut Vec<Payload>) { fn dedup_payloads(payloads: &mut Vec<Payload>) {
let mut seen = BTreeSet::new(); payloads.sort();
payloads.retain(|p| seen.insert(p.clone())); payloads.dedup();
} }
fn normalize_aspas<I>(aspas: I) -> BTreeSet<Aspa> fn normalize_aspas<I>(aspas: I) -> Vec<Aspa>
where where
I: IntoIterator<Item = Aspa>, I: IntoIterator<Item = Aspa>,
{ {
let mut by_customer = BTreeMap::<u32, BTreeSet<_>>::new(); let mut by_customer = BTreeMap::<u32, Vec<_>>::new();
for aspa in aspas { for aspa in aspas {
let providers = by_customer let providers = by_customer
@ -386,17 +415,19 @@ where
providers.extend(aspa.provider_asns().iter().copied()); providers.extend(aspa.provider_asns().iter().copied());
} }
by_customer let mut normalized = by_customer
.into_iter() .into_iter()
.map(|(customer_asn, providers)| { .map(|(customer_asn, providers)| {
Aspa::new(customer_asn.into(), providers.into_iter().collect()) Aspa::new(customer_asn.into(), providers)
}) })
.collect() .collect::<Vec<_>>();
normalized.sort();
normalized
} }
fn diff_aspas( fn diff_aspas(
current: &BTreeSet<Aspa>, current: &[Aspa],
next: &BTreeSet<Aspa>, next: &[Aspa],
announced: &mut Vec<Payload>, announced: &mut Vec<Payload>,
withdrawn: &mut Vec<Payload>, withdrawn: &mut Vec<Payload>,
) { ) {
@ -413,7 +444,7 @@ fn diff_aspas(
.keys() .keys()
.chain(next.keys()) .chain(next.keys())
.copied() .copied()
.collect::<BTreeSet<_>>(); .collect::<std::collections::BTreeSet<_>>();
for customer in customers { for customer in customers {
match (current.get(&customer), next.get(&customer)) { match (current.get(&customer), next.get(&customer)) {
@ -426,3 +457,47 @@ fn diff_aspas(
} }
} }
} }
fn sorted_dedup<T: Ord>(mut items: Vec<T>) -> Vec<T> {
items.sort();
items.dedup();
items
}
fn diff_sorted<T, F>(
current: &[T],
next: &[T],
announced: &mut Vec<Payload>,
withdrawn: &mut Vec<Payload>,
wrap: F,
) where
T: Ord + Clone,
F: Fn(T) -> Payload,
{
let mut i = 0usize;
let mut j = 0usize;
while i < current.len() && j < next.len() {
match current[i].cmp(&next[j]) {
std::cmp::Ordering::Less => {
withdrawn.push(wrap(current[i].clone()));
i += 1;
}
std::cmp::Ordering::Greater => {
announced.push(wrap(next[j].clone()));
j += 1;
}
std::cmp::Ordering::Equal => {
i += 1;
j += 1;
}
}
}
while i < current.len() {
withdrawn.push(wrap(current[i].clone()));
i += 1;
}
while j < next.len() {
announced.push(wrap(next[j].clone()));
j += 1;
}
}

View File

@ -213,14 +213,5 @@ fn persist_update_job(job: StoreSyncJob) {
} }
fn project_snapshot_for_version(snapshot: &Snapshot, version: u8) -> Snapshot { fn project_snapshot_for_version(snapshot: &Snapshot, version: u8) -> Snapshot {
let mut payloads = Vec::new(); snapshot.project_for_version(version)
for payload in snapshot.payloads() {
match payload {
Payload::RouteOrigin(_) => payloads.push(payload),
Payload::RouterKey(_) if version >= 1 => payloads.push(payload),
Payload::Aspa(_) if version >= 2 => payloads.push(payload),
_ => {}
}
}
Snapshot::from_payloads(payloads)
} }