route caching

This commit is contained in:
John Smith 2022-12-17 22:09:08 -05:00
parent bf9cfd5b94
commit 26311e96c5
2 changed files with 98 additions and 17 deletions

View File

@ -65,14 +65,14 @@ deps-linux:
# Code + Linux deps
code-linux:
FROM +deps-linux
COPY --dir .cargo external files scripts veilid-cli veilid-core veilid-server veilid-flutter veilid-wasm Cargo.lock Cargo.toml /veilid
COPY --dir .cargo external files scripts veilid-cli veilid-core veilid-server veilid-tools veilid-flutter veilid-wasm Cargo.lock Cargo.toml /veilid
RUN cat /veilid/scripts/earthly/cargo-linux/config.toml >> /veilid/.cargo/config.tml
WORKDIR /veilid
# Code + Linux + Android deps
code-android:
FROM +deps-android
COPY --dir .cargo external files scripts veilid-cli veilid-core veilid-server veilid-flutter veilid-wasm Cargo.lock Cargo.toml /veilid
COPY --dir .cargo external files scripts veilid-cli veilid-core veilid-server veilid-tools veilid-flutter veilid-wasm Cargo.lock Cargo.toml /veilid
RUN cat /veilid/scripts/earthly/cargo-linux/config.toml >> /veilid/.cargo/config.tml
RUN cat /veilid/scripts/earthly/cargo-android/config.toml >> /veilid/.cargo/config.tml
WORKDIR /veilid

View File

@ -10,6 +10,16 @@ const REMOTE_PRIVATE_ROUTE_CACHE_SIZE: usize = 1024;
const REMOTE_PRIVATE_ROUTE_CACHE_EXPIRY: TimestampDuration = TimestampDuration::new(300_000_000u64);
/// Amount of time a route can remain idle before it gets tested
const ROUTE_MIN_IDLE_TIME_MS: u32 = 30_000;
/// The size of the compiled route cache
const COMPILED_ROUTE_CACHE_SIZE: usize = 256;
// Compiled route key for caching
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
struct CompiledRouteCacheKey {
sr_pubkey: DHTKey,
pr_pubkey: DHTKey,
}
/// Compiled route (safety route + private route)
#[derive(Clone, Debug)]
@ -239,6 +249,8 @@ pub struct RouteSpecStoreCache {
hop_cache: HashSet<Vec<u8>>,
/// Has a remote private route responded to a question and when
remote_private_route_cache: LruCache<DHTKey, RemotePrivateRouteInfo>,
/// Compiled route cache
compiled_route_cache: LruCache<CompiledRouteCacheKey, SafetyRoute>,
/// List of dead allocated routes
dead_routes: Vec<DHTKey>,
/// List of dead remote routes
@ -252,6 +264,7 @@ impl Default for RouteSpecStoreCache {
used_end_nodes: Default::default(),
hop_cache: Default::default(),
remote_private_route_cache: LruCache::new(REMOTE_PRIVATE_ROUTE_CACHE_SIZE),
compiled_route_cache: LruCache::new(COMPILED_ROUTE_CACHE_SIZE),
dead_routes: Default::default(),
dead_remote_routes: Default::default(),
}
@ -1106,11 +1119,18 @@ impl RouteSpecStore {
/// Release an allocated or remote route that is no longer in use
#[instrument(level = "trace", skip(self), ret)]
pub fn release_route(&self, key: &DHTKey) -> bool {
let is_remote = {
let inner = &mut *self.inner.lock();
// Release from compiled route cache if it's used there
self.invalidate_compiled_route_cache(inner, key);
// Check to see if this is a remote route
let cur_ts = get_aligned_timestamp();
Self::with_peek_remote_private_route(inner, cur_ts, key, |_| {}).is_some()
};
if is_remote {
self.release_remote_private_route(key)
} else {
@ -1222,6 +1242,41 @@ impl RouteSpecStore {
//////////////////////////////////////////////////////////////////////
// Route cache
fn add_to_compiled_route_cache(&self, inner: &mut RouteSpecStoreInner, pr_pubkey: DHTKey, safety_route: SafetyRoute)
{
let key = CompiledRouteCacheKey {
sr_pubkey: safety_route.public_key,
pr_pubkey,
};
if let Some(v) = inner.cache.compiled_route_cache.insert(key, safety_route) {
log_rtab!(error "route cache already contained key: sr_pubkey={:?}, pr_pubkey={:?}", v.public_key, pr_pubkey);
}
}
fn lookup_compiled_route_cache(&self, inner: &mut RouteSpecStoreInner, sr_pubkey: DHTKey, pr_pubkey: DHTKey) -> Option<SafetyRoute> {
let key = CompiledRouteCacheKey {
sr_pubkey,
pr_pubkey,
};
inner.cache.compiled_route_cache.get(&key).cloned()
}
fn invalidate_compiled_route_cache(&self, inner: &mut RouteSpecStoreInner, dead_key: &DHTKey) {
let mut dead_entries = Vec::new();
for (k, _v) in inner.cache.compiled_route_cache.iter() {
if k.sr_pubkey == *dead_key || k.pr_pubkey == *dead_key {
dead_entries.push(k.clone());
}
}
for d in dead_entries {
inner.cache.compiled_route_cache.remove(&d);
}
}
/// Compiles a safety route to the private route, with caching
/// Returns an Err() if the parameters are wrong
/// Returns Ok(None) if no allocation could happen at this time (not an error)
@ -1234,6 +1289,7 @@ impl RouteSpecStore {
let routing_table = self.unlocked_inner.routing_table.clone();
let rti = &mut *routing_table.inner.write();
let pr_pubkey = private_route.public_key;
let pr_hopcount = private_route.hop_count as usize;
let max_route_hop_count = self.unlocked_inner.max_route_hop_count;
// Check private route hop count isn't larger than the max route hop count plus one for the 'first hop' header
@ -1293,12 +1349,45 @@ impl RouteSpecStore {
};
sr_pubkey
};
let safety_rsd = Self::detail_mut(inner, &sr_pubkey).unwrap();
// Look up a few things from the safety route detail we want for the compiled route and don't borrow inner
let (optimize, first_hop, secret) = {
let safety_rsd = Self::detail(inner, &sr_pubkey).ok_or_else(|| eyre!("route missing"))?;
// We can optimize the peer info in this safety route if it has been successfully
// communicated over either via an outbound test, or used as a private route inbound
// and we are replying over the same route as our safety route outbound
let optimize = safety_rsd.stats.last_tested_ts.is_some() || safety_rsd.stats.last_received_ts.is_some();
// xxx implement caching here!
// Get the first hop noderef of the safety route
let mut first_hop = safety_rsd.hop_node_refs.first().unwrap().clone();
// Ensure sequencing requirement is set on first hop
first_hop.set_sequencing(safety_spec.sequencing);
// Get the safety route secret key
let secret = safety_rsd.secret_key;
(optimize, first_hop, secret)
};
// See if we have a cached route we can use
if optimize {
if let Some(safety_route) = self.lookup_compiled_route_cache(inner, sr_pubkey, pr_pubkey) {
// Build compiled route
let compiled_route = CompiledRoute {
safety_route,
secret,
first_hop,
};
// Return compiled route
return Ok(Some(compiled_route));
}
}
// Create hops
let hops = {
let safety_rsd = Self::detail(inner, &sr_pubkey).ok_or_else(|| eyre!("route missing"))?;
// start last blob-to-encrypt data off as private route
let mut blob_data = {
let mut pr_message = ::capnp::message::Builder::new_default();
@ -1311,12 +1400,6 @@ impl RouteSpecStore {
blob_data
};
// We can optimize the peer info in this safety route if it has been successfully
// communicated over either via an outbound test, or used as a private route inbound
// and we are replying over the same route as our safety route outbound
let optimize = safety_rsd.stats.last_tested_ts.is_some()
|| safety_rsd.stats.last_received_ts.is_some();
// Encode each hop from inside to outside
// skips the outermost hop since that's entering the
// safety route and does not include the dialInfo
@ -1402,20 +1485,18 @@ impl RouteSpecStore {
hops,
};
let mut first_hop = safety_rsd.hop_node_refs.first().unwrap().clone();
// Ensure sequencing requirement is set on first hop
first_hop.set_sequencing(safety_spec.sequencing);
// Add to cache but only if we have an optimized route
if optimize {
self.add_to_compiled_route_cache(inner, pr_pubkey, safety_route.clone());
}
// Build compiled route
let compiled_route = CompiledRoute {
safety_route,
secret: safety_rsd.secret_key,
secret,
first_hop,
};
// xxx: add cache here
// Return compiled route
Ok(Some(compiled_route))
}