use super::*;
#[derive(Default)]
struct Stages {
retire: Weak<Timer>,
reap: Weak<Timer>,
}
pub(super) struct Leases {
inner: Weak<AuthStateInner>,
wheel: TimingWheel,
now: u64,
stages: HashMap<KeyId, Stages>,
}
impl Leases {
pub(super) fn restored(inner: &Arc<AuthStateInner>, now: u64) -> Self {
let mut leases = Self {
inner: Arc::downgrade(inner),
wheel: new_wheel(),
now,
stages: HashMap::new(),
};
let mut live = Vec::new();
let mut dead = Vec::new();
for (index, slot) in inner.slots().iter().enumerate() {
let key_id = KeyId::new(slot.generation, SlotIndex::from_index(index));
match slot.state {
SlotState::Active => live.extend(slot.lease.upgrade().map(|l| (key_id, l))),
SlotState::Expired | SlotState::Revoked => dead.push(key_id),
SlotState::Free => {}
}
}
dead.extend(
inner
.high()
.iter()
.filter(|entry| entry.state != SlotState::Active)
.map(|entry| entry.key_id),
);
for (key_id, lease) in live {
leases.watch(key_id, lease);
}
for key_id in dead {
let tombstoned_at = inner
.cold()
.get(&key_id)
.map(|cold| cold.tombstoned_at)
.filter(|at| *at != 0)
.unwrap_or(now);
leases.schedule_reap(key_id, retention_ends(tombstoned_at));
}
leases
}
pub(super) fn issue(&mut self, lease: &Arc<AuthLease>, issued_at: u64, label: Option<String>) {
let Some(inner) = self.inner.upgrade() else {
return;
};
inner.cold_mut().insert(
lease.key_id(),
ColdMetadata {
issued_at,
label,
tombstoned_at: 0,
},
);
self.watch(lease.key_id(), lease.clone());
}
pub(super) fn adopt(&mut self, lease: &Arc<AuthLease>) {
self.watch(lease.key_id(), lease.clone());
}
pub(super) fn renew(&mut self, key_id: KeyId, expires_at: u64) -> bool {
let Some(stages) = self.stages.get(&key_id) else {
return false;
};
let (Some(retire), Some(reap)) = (stages.retire.upgrade(), stages.reap.upgrade()) else {
return false;
};
self.schedule_at(expires_at, retire);
self.schedule_at(retention_ends(expires_at), reap);
true
}
pub(super) fn retire_now(&mut self, key_id: KeyId) {
if let Some(retire) = self.stage(key_id, |stages| &stages.retire) {
retire.fire();
}
}
pub(super) fn end(&mut self, key_id: KeyId) {
self.retire_now(key_id);
if let Some(reap) = self.stage(key_id, |stages| &stages.reap) {
reap.fire();
}
self.stages.remove(&key_id);
}
pub(super) fn tick(&mut self, now: u64) {
if now.saturating_sub(self.now) > self.wheel.max_delay() {
self.drop_schedule(now);
return;
}
while self.now < now {
self.now += 1;
self.wheel.tick();
}
}
pub(super) fn wipe(&mut self, now: u64) {
if let Some(inner) = self.inner.upgrade() {
for lease in inner.slots().iter().filter_map(|slot| slot.lease.upgrade()) {
lease.cancel_rotated();
}
}
self.drop_schedule(now);
}
pub(super) fn collect_garbage(&mut self, now: u64) -> u64 {
let Some(inner) = self.inner.upgrade() else {
return 0;
};
let mut due = inner
.slots()
.iter()
.enumerate()
.filter(|(_, slot)| slot.is_collectable(now))
.map(|(index, slot)| KeyId::new(slot.generation, SlotIndex::from_index(index)))
.collect::<Vec<_>>();
due.extend(
inner
.high()
.iter()
.filter(|entry| entry.state != SlotState::Active || entry.expires_at <= now)
.map(|entry| entry.key_id),
);
for key_id in &due {
self.end(*key_id);
}
due.len() as u64
}
fn drop_schedule(&mut self, now: u64) {
self.stages.clear();
self.now = now;
self.wheel = new_wheel();
}
fn schedule_at(&mut self, deadline: u64, timer: Arc<Timer>) {
self.wheel
.schedule(deadline.saturating_sub(self.now), timer);
}
fn stage(
&mut self,
key_id: KeyId,
which: impl Fn(&Stages) -> &Weak<Timer>,
) -> Option<Arc<Timer>> {
let stages = self.stages.get(&key_id)?;
let timer = which(stages).upgrade();
if stages.retire.strong_count() == 0 && stages.reap.strong_count() == 0 {
self.stages.remove(&key_id);
}
timer
}
fn watch(&mut self, key_id: KeyId, lease: Arc<AuthLease>) {
let inner = self.inner.clone();
let expires_at = lease.expires_at();
let retire_lease = lease.clone();
let retire = Timer::new(move || {
if let Some(inner) = inner.upgrade() {
retire(&inner, key_id, &retire_lease);
}
});
let reap = self.reap_timer(key_id, Some(lease));
self.stages.insert(
key_id,
Stages {
retire: Arc::downgrade(&retire),
reap: Arc::downgrade(&reap),
},
);
self.schedule_at(expires_at, retire);
self.schedule_at(retention_ends(expires_at), reap);
}
fn schedule_reap(&mut self, key_id: KeyId, deadline: u64) {
let reap = self.reap_timer(key_id, None);
self.stages.insert(
key_id,
Stages {
reap: Arc::downgrade(&reap),
..Stages::default()
},
);
self.schedule_at(deadline, reap);
}
fn reap_timer(&self, key_id: KeyId, lease: Option<Arc<AuthLease>>) -> Arc<Timer> {
let inner = self.inner.clone();
Timer::new(move || {
drop(lease);
if let Some(inner) = inner.upgrade() {
reap(&inner, key_id);
}
})
}
}
fn retention_ends(tombstoned_at: u64) -> u64 {
tombstoned_at.saturating_add(TOMBSTONE_RETENTION.as_secs())
}
fn retire(inner: &Arc<AuthStateInner>, key_id: KeyId, lease: &Arc<AuthLease>) {
lease.cancel_expired();
let mut slots = inner.slots_mut();
let Some(slot) = slots.get_mut(key_id.slot().as_index()) else {
return;
};
if !slot.holds(key_id) || slot.state != SlotState::Active {
return;
}
slot.state = SlotState::Expired;
let tombstoned_at = slot.expires_at;
drop(slots);
inner
.cold_mut()
.entry(key_id)
.and_modify(|cold| cold.tombstoned_at = tombstoned_at);
tracing::info!(
event = "temporary_key_expired",
auth_stage = "expiry",
key_id = key_id.as_u64(),
expires_at = tombstoned_at,
"temporary key expired and active work was cancelled"
);
}
fn reap(inner: &Arc<AuthStateInner>, key_id: KeyId) {
let mut slots = inner.slots_mut();
match slots.get_mut(key_id.slot().as_index()) {
Some(slot) if slot.holds(key_id) => {
slot.retire();
drop(slots);
}
Some(_) => return,
None => {
drop(slots);
inner.high_mut().retain(|entry| entry.key_id != key_id);
}
}
inner.cold_mut().remove(&key_id);
}
fn new_wheel() -> TimingWheel {
TimingWheel::new(MAX_SCHEDULABLE_DELAY.as_secs(), 64)
}