Skip to content
68 linesCodeBlameRaw
1//! g1t-wide pauses (billing's `platform_pause`; "Spend guardrails" in
2//! docs.g1t.sh/guides/deploy-to-cloudflare/),
3//! read cheaply: one call to billing per isolate every 30 seconds at most,
4//! never a database read per request.
5//!
6//! When billing cannot say, nothing is paused: a pause is a brake staff
7//! (or billing's usage watcher) pull on purpose, and a billing outage must
8//! not stop schedules and indexing everywhere. The failure is kept for the
9//! same 30 seconds, so an outage is not asked about on every request.
10
11use std::cell::RefCell;
12
13use g1t_contracts::billing::{PauseLevel, PlatformPause};
14use worker::Fetcher;
15
16/// How long an answer is kept in the isolate.
17pub const KEEP_MS: u64 = 30_000;
18
19thread_local! {
20 static KEPT: RefCell<Option<(PlatformPause, u64)>> = const { RefCell::new(None) };
21}
22
23/// A kept answer, while it is fresh.
24fn fresh(kept: Option<(PlatformPause, u64)>, now: u64) -> Option<PlatformPause> {
25 kept.filter(|(_, until)| *until > now).map(|(pause, _)| pause)
26}
27
28/// Whether `level` is paused, through the billing service's binding.
29pub async fn paused(billing: &Fetcher, level: PauseLevel) -> bool {
30 current(billing).await.is(level)
31}
32
33/// Every level, kept for `KEEP_MS`. Nothing paused when billing cannot say.
34pub async fn current(billing: &Fetcher) -> PlatformPause {
35 let now = crate::now_ms();
36 if let Some(pause) = KEPT.with(|kept| fresh(*kept.borrow(), now)) {
37 return pause;
38 }
39 let pause = match crate::call::<_, PlatformPause>(billing, "platform_pause", &serde_json::json!({})).await {
40 Ok(pause) => pause,
41 Err(error) => {
42 worker::console_error!("platform pause unreadable, so nothing is paused: {error}");
43 PlatformPause::default()
44 }
45 };
46 KEPT.with(|kept| *kept.borrow_mut() = Some((pause, now + KEEP_MS)));
47 pause
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 #[test]
55 fn an_answer_is_kept_thirty_seconds() {
56 let paused = PlatformPause { schedules: true, ..PlatformPause::default() };
57 assert_eq!(fresh(Some((paused, 1_000 + KEEP_MS)), 1_000), Some(paused));
58 assert_eq!(fresh(Some((paused, 1_000 + KEEP_MS)), 1_000 + KEEP_MS), None);
59 assert_eq!(fresh(None, 0), None);
60 }
61
62 #[test]
63 fn an_unread_pause_pauses_nothing() {
64 let none = PlatformPause::default();
65 assert!(PauseLevel::ALL.into_iter().all(|level| !none.is(level)));
66 assert!(!none.any());
67 }
68}