Files
jcoffey-dev 966241070e
ci / fork-checks (pull_request) Skipped
ci / build (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 6m29s
Name Coffey Labs LLC as the copyright holder
Coffey Labs is now Coffey Labs LLC, an Arizona limited liability company. Copyright lines and SPDX-FileCopyrightText headers naming Coffey Labs or John Coffey now name Coffey Labs LLC. Upstream copyright notices are unchanged.
2026-10-05 23:33:00 -07:00

72 lines
2.4 KiB
Rust

/*
* SPDX-FileCopyrightText: 2026 Coffey Labs LLC
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
//! Ends a locked account's delegations at their `until` (AL-5), rather than
//! at the next daily sweep. Each node sleeps until the soonest `until`, wakes
//! early when a lock is written here, and checks at least hourly for locks
//! written on other nodes. One node re-applies each lock; the others find it
//! claimed.
use common::{BuildServer, Inner, KV_LOCK_TASK, Server};
use inbuxa_features::lock;
use std::{sync::Arc, time::Duration};
use store::write::now;
/// The longest the timer sleeps, so an `until` set on another node is seen.
const CEILING: u64 = 3600;
pub fn spawn_lock_expiry(inner: Arc<Inner>) {
tokio::spawn(async move {
// Since boot: anything that ended while the server was down
let mut checked = 0;
loop {
let server = inner.build_server();
let now = now();
let wait = match lock::all(server.store()).await {
Ok(locks) => {
for account_id in lock::ended_between(&locks, checked, now).collect::<Vec<_>>()
{
end_delegations(&server, account_id).await;
}
checked = now;
lock::next_until(&locks, now)
.map_or(CEILING, |until| (until - now).min(CEILING))
}
Err(err) => {
trc::error!(err.details("Failed to read account locks for their end dates"));
60
}
};
tokio::select! {
_ = tokio::time::sleep(Duration::from_secs(wait.max(1))) => {}
_ = lock::UNTIL_CHANGED.notified() => {}
}
}
});
}
async fn end_delegations(server: &Server, account_id: u32) {
let key = [b"lock-until:".as_slice(), &account_id.to_be_bytes()].concat();
match server
.in_memory_store()
.try_lock(KV_LOCK_TASK, &key, 60)
.await
{
Ok(true) => {
if let Err(err) = email::inbuxa_lock::reconcile(server, account_id).await {
trc::error!(
err.account_id(account_id)
.details("Failed to end a delegation at its date")
);
}
}
Ok(false) => {}
Err(err) => {
trc::error!(err.details("Failed to claim a delegation's end"));
}
}
}