v0.24.5
This commit is contained in:
commit
5e23e9af02
240 files changed
+99957
No files matched your search
Vendored
+257
@@ -0,0 +1,257 @@
|
||||
struct FillFlight {
|
||||
id: u64,
|
||||
scope: String,
|
||||
scheme: String,
|
||||
authority: String,
|
||||
target: String,
|
||||
done: tokio::sync::watch::Sender<bool>,
|
||||
}
|
||||
|
||||
pub enum FillClaim {
|
||||
Disabled,
|
||||
Leader(FillLeaderGuard),
|
||||
Waited,
|
||||
Timeout,
|
||||
}
|
||||
|
||||
pub struct FillLeaderGuard {
|
||||
cache: std::sync::Weak<Cache>,
|
||||
key: Key,
|
||||
id: u64,
|
||||
done: tokio::sync::watch::Sender<bool>,
|
||||
}
|
||||
|
||||
struct CollapseWaiterGuard {
|
||||
cache: std::sync::Weak<Cache>,
|
||||
}
|
||||
|
||||
impl Drop for CollapseWaiterGuard {
|
||||
fn drop(&mut self) {
|
||||
if let Some(cache) = self.cache.upgrade() {
|
||||
cache.collapse_waiters.fetch_sub(1, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for FillLeaderGuard {
|
||||
fn drop(&mut self) {
|
||||
if let Some(cache) = self.cache.upgrade() {
|
||||
let mut flights = cache
|
||||
.fill_flights
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner());
|
||||
let current = flights
|
||||
.get(&self.key)
|
||||
.is_some_and(|flight| flight.id == self.id);
|
||||
if current {
|
||||
flights.remove(&self.key);
|
||||
}
|
||||
}
|
||||
// Publish completion even when a purge already removed this flight from the map.
|
||||
self.done.send_replace(true);
|
||||
}
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
pub async fn claim_fill(
|
||||
self: &Arc<Self>,
|
||||
ticket: &Ticket,
|
||||
settings: &Settings,
|
||||
) -> FillClaim {
|
||||
if !settings.collapse_enabled {
|
||||
return FillClaim::Disabled;
|
||||
}
|
||||
let existing = {
|
||||
let mut flights = self
|
||||
.fill_flights
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner());
|
||||
if let Some(flight) = flights.get(&ticket.key) {
|
||||
Some(flight.clone())
|
||||
} else {
|
||||
let id = self
|
||||
.fill_flight_sequence
|
||||
.fetch_add(1, Ordering::Relaxed)
|
||||
.wrapping_add(1);
|
||||
let (done, _) = tokio::sync::watch::channel(false);
|
||||
flights.insert(
|
||||
ticket.key,
|
||||
Arc::new(FillFlight {
|
||||
id,
|
||||
scope: ticket.scope.clone(),
|
||||
scheme: ticket.scheme.clone(),
|
||||
authority: ticket.authority.clone(),
|
||||
target: ticket.target.clone(),
|
||||
done: done.clone(),
|
||||
}),
|
||||
);
|
||||
return FillClaim::Leader(FillLeaderGuard {
|
||||
cache: Arc::downgrade(self),
|
||||
key: ticket.key,
|
||||
id,
|
||||
done,
|
||||
});
|
||||
}
|
||||
};
|
||||
let Some(flight) = existing else {
|
||||
unreachable!("existing fill resolved above")
|
||||
};
|
||||
// A watch channel retains the completed state, so a leader finishing between
|
||||
// map lookup and follower polling cannot lose the wakeup.
|
||||
let mut done = flight.done.subscribe();
|
||||
self.collapse_waiters.fetch_add(1, Ordering::Relaxed);
|
||||
let _waiter = CollapseWaiterGuard {
|
||||
cache: Arc::downgrade(self),
|
||||
};
|
||||
let waited = if *done.borrow() {
|
||||
Ok(())
|
||||
} else {
|
||||
tokio::time::timeout(
|
||||
Duration::from_millis(settings.collapse_wait_timeout_ms),
|
||||
done.changed(),
|
||||
)
|
||||
.await
|
||||
.map(|_| ())
|
||||
};
|
||||
match waited {
|
||||
Ok(()) => {
|
||||
self.counters
|
||||
.collapsed_fills
|
||||
.fetch_add(1, Ordering::Relaxed);
|
||||
FillClaim::Waited
|
||||
}
|
||||
Err(_) => {
|
||||
self.counters
|
||||
.collapse_timeouts
|
||||
.fetch_add(1, Ordering::Relaxed);
|
||||
FillClaim::Timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cancel_all_fill_flights(&self) {
|
||||
let flights = {
|
||||
let mut flights = self
|
||||
.fill_flights
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner());
|
||||
flights.drain().map(|(_, flight)| flight).collect::<Vec<_>>()
|
||||
};
|
||||
for flight in flights {
|
||||
flight.done.send_replace(true);
|
||||
}
|
||||
}
|
||||
|
||||
fn cancel_scope_fill_flights(&self, scope: &str) {
|
||||
let removed = {
|
||||
let mut flights = self
|
||||
.fill_flights
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner());
|
||||
let keys = flights
|
||||
.iter()
|
||||
.filter(|(_, flight)| flight.scope == scope)
|
||||
.map(|(key, _)| *key)
|
||||
.collect::<Vec<_>>();
|
||||
keys.into_iter()
|
||||
.filter_map(|key| flights.remove(&key))
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
for flight in removed {
|
||||
flight.done.send_replace(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProactiveScheduleGuard {
|
||||
cache: std::sync::Weak<Cache>,
|
||||
key: Key,
|
||||
}
|
||||
impl Drop for ProactiveScheduleGuard {
|
||||
fn drop(&mut self) {
|
||||
if let Some(cache) = self.cache.upgrade() {
|
||||
cache
|
||||
.proactive_scheduled_keys
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
.remove(&self.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProactiveInflightGuard {
|
||||
cache: std::sync::Weak<Cache>,
|
||||
}
|
||||
impl Drop for ProactiveInflightGuard {
|
||||
fn drop(&mut self) {
|
||||
if let Some(cache) = self.cache.upgrade() {
|
||||
cache.proactive_inflight.fetch_sub(1, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
pub fn begin_proactive_schedule(
|
||||
self: &Arc<Self>,
|
||||
ticket: &Ticket,
|
||||
settings: &Settings,
|
||||
) -> Option<ProactiveScheduleGuard> {
|
||||
if !settings.proactive_refresh_enabled || settings.proactive_refresh_percent == 0 {
|
||||
return None;
|
||||
}
|
||||
let sample = u16::from_be_bytes([ticket.key[0], ticket.key[1]]) % 100;
|
||||
if sample >= settings.proactive_refresh_percent as u16 {
|
||||
self.counters
|
||||
.proactive_skipped
|
||||
.fetch_add(1, Ordering::Relaxed);
|
||||
return None;
|
||||
}
|
||||
let mut scheduled = self
|
||||
.proactive_scheduled_keys
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner());
|
||||
if !scheduled.insert(ticket.key) {
|
||||
return None;
|
||||
}
|
||||
self.counters
|
||||
.proactive_scheduled
|
||||
.fetch_add(1, Ordering::Relaxed);
|
||||
Some(ProactiveScheduleGuard {
|
||||
cache: Arc::downgrade(self),
|
||||
key: ticket.key,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn try_begin_proactive_refresh(
|
||||
self: &Arc<Self>,
|
||||
settings: &Settings,
|
||||
) -> Option<ProactiveInflightGuard> {
|
||||
let limit = settings.proactive_refresh_max_inflight as u64;
|
||||
loop {
|
||||
let current = self.proactive_inflight.load(Ordering::Relaxed);
|
||||
if current >= limit {
|
||||
self.counters
|
||||
.proactive_skipped
|
||||
.fetch_add(1, Ordering::Relaxed);
|
||||
return None;
|
||||
}
|
||||
if self
|
||||
.proactive_inflight
|
||||
.compare_exchange_weak(
|
||||
current,
|
||||
current + 1,
|
||||
Ordering::AcqRel,
|
||||
Ordering::Relaxed,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
self.counters
|
||||
.proactive_started
|
||||
.fetch_add(1, Ordering::Relaxed);
|
||||
return Some(ProactiveInflightGuard {
|
||||
cache: Arc::downgrade(self),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user