1use std::sync::atomic::{AtomicBool, Ordering};
4use std::sync::{Arc, OnceLock};
5use std::time::Duration;
6
7use bytes::Bytes;
8use rustc_hash::FxHashMap;
9use tokio_util::sync::CancellationToken;
10
11use crate::auth::Auth;
12use crate::config::LimitsConfig;
13use crate::info::TaskInfoCache;
14use crate::remote::{OutputCache, WorkerRegistry};
15
16pub struct Shared {
18 pub auth: Auth,
20 pub limits: LimitsConfig,
22 pub loopback_hosts_only: bool,
25 pub ready: Arc<AtomicBool>,
27 pub app: OnceLock<Arc<AppState>>,
29 pub stop: CancellationToken,
31}
32
33impl Shared {
34 pub fn app(&self) -> Option<&Arc<AppState>> {
36 if self.stop.is_cancelled() || !self.ready.load(Ordering::SeqCst) {
37 return None;
38 }
39 self.app.get()
40 }
41
42 pub fn is_standby(&self) -> bool {
44 !self.stop.is_cancelled() && !self.ready.load(Ordering::SeqCst)
45 }
46
47 pub fn app_or_unavailable(&self) -> Result<&Arc<AppState>, tonic::Status> {
49 self.app()
50 .ok_or_else(|| tonic::Status::unavailable("not the leader (standby or shutting down)"))
51 }
52}
53
54#[derive(Clone, Debug)]
56pub struct PoolInfo {
57 pub slot_cap: u32,
59 pub lease_ttl: Duration,
61 pub max_attempts: u32,
63}
64
65pub struct AppState {
67 pub sched: cronoq::Scheduler<Bytes, Bytes>,
69 pub registry: Arc<WorkerRegistry>,
71 pub outputs: Arc<OutputCache>,
73 pub info: Arc<TaskInfoCache>,
75 pub pools: FxHashMap<Arc<str>, PoolInfo>,
77 pub limits: LimitsConfig,
79 pub queries: Option<Arc<dyn cronoq::StoreQueries<Bytes>>>,
81 pub shards: Option<Arc<crate::shard::ShardMap>>,
83 pub peers: Option<Arc<crate::peer::Peers>>,
85}
86
87impl AppState {
88 pub async fn output(&self, id: cronoq::TaskId) -> Option<Bytes> {
90 if let Some(o) = self.outputs.get(id) {
91 return Some(o);
92 }
93 let q = self.queries.as_ref()?;
94 q.get_task(&cronoq::TaskRef::Id(id))
95 .await
96 .ok()
97 .flatten()
98 .and_then(|r| r.output)
99 }
100
101 pub async fn pool_of(&self, id: cronoq::TaskId) -> Option<Arc<str>> {
103 if let Some(m) = self.info.get(id.0) {
104 return Some(m.pool);
105 }
106 let q = self.queries.as_ref()?;
107 q.get_task(&cronoq::TaskRef::Id(id))
108 .await
109 .ok()
110 .flatten()
111 .map(|r| Arc::from(r.pool.as_str()))
112 }
113
114 pub fn pool(&self, name: &str) -> Option<&PoolInfo> {
116 self.pools.get(name)
117 }
118}