gam_runtime/warm_start/
session.rs1use crate::warm_start::ConfiguredWarmStartStore;
7use crate::warm_start::key::Fingerprint;
8use crate::warm_start::store::{EntryKind, WarmStartEntry, WarmStartStore};
9use std::sync::Mutex;
10use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
11
12const MIN_CHECKPOINT_INTERVAL: Duration = Duration::from_secs(2);
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum LoadSource {
20 Exact,
21 Preloaded,
22}
23
24#[derive(Debug, Clone)]
25pub struct LoadedEntry {
26 pub entry: WarmStartEntry,
27 pub source: LoadSource,
28}
29
30#[derive(Debug)]
31pub struct Session {
32 store: WarmStartStore,
33 configured_store: Option<ConfiguredWarmStartStore>,
34 key: Fingerprint,
35 run_id: String,
36 inner: Mutex<Inner>,
37 preloaded: Mutex<Option<WarmStartEntry>>,
45}
46
47#[derive(Debug)]
48struct Inner {
49 last_write: Option<Instant>,
50 best_seen: Option<f64>,
51}
52
53impl Session {
54 pub fn open(store: WarmStartStore, key: Fingerprint) -> Self {
55 let nanos = SystemTime::now()
56 .duration_since(UNIX_EPOCH)
57 .map(|d| d.as_nanos())
58 .unwrap_or(0);
59 let pid = std::process::id();
60 let run_id = format!("ckpt-r{pid:x}-{nanos:x}");
61 Self {
62 store,
63 configured_store: None,
64 key,
65 run_id,
66 inner: Mutex::new(Inner {
67 last_write: None,
68 best_seen: None,
69 }),
70 preloaded: Mutex::new(None),
71 }
72 }
73
74 pub(super) fn open_configured(
75 store: WarmStartStore,
76 key: Fingerprint,
77 configured_store: ConfiguredWarmStartStore,
78 ) -> Self {
79 let mut session = Self::open(store, key);
80 session.configured_store = Some(configured_store);
81 session
82 }
83
84 fn configured_store_is_available(&self) -> bool {
85 self.configured_store
86 .as_ref()
87 .is_none_or(ConfiguredWarmStartStore::is_available)
88 }
89
90 fn record_store_error(&self, operation: &str, error: &crate::warm_start::StoreError) {
91 if let Some(configured_store) = &self.configured_store {
92 configured_store.record_store_error(operation, error);
93 }
94 }
95
96 pub fn key(&self) -> &Fingerprint {
97 &self.key
98 }
99
100 pub fn try_load_with_source(&self) -> Option<LoadedEntry> {
107 if let Ok(mut slot) = self.preloaded.lock()
108 && let Some(entry) = slot.take()
109 {
110 return Some(LoadedEntry {
111 entry,
112 source: LoadSource::Preloaded,
113 });
114 }
115 if !self.configured_store_is_available() {
116 return None;
117 }
118 match self.store.lookup(&self.key) {
119 Ok(Some(entry)) => Some(LoadedEntry {
120 entry,
121 source: LoadSource::Exact,
122 }),
123 Ok(None) => None,
124 Err(error) => {
125 self.record_store_error("load outer-iterate session", &error);
126 None
127 }
128 }
129 }
130
131 pub fn peek_load_with_source(&self) -> Option<LoadedEntry> {
134 if let Ok(slot) = self.preloaded.lock()
135 && let Some(entry) = slot.as_ref()
136 {
137 return Some(LoadedEntry {
138 entry: entry.clone(),
139 source: LoadSource::Preloaded,
140 });
141 }
142 if !self.configured_store_is_available() {
143 return None;
144 }
145 match self.store.lookup(&self.key) {
146 Ok(Some(entry)) => Some(LoadedEntry {
147 entry,
148 source: LoadSource::Exact,
149 }),
150 Ok(None) => None,
151 Err(error) => {
152 self.record_store_error("peek outer-iterate session", &error);
153 None
154 }
155 }
156 }
157
158 pub fn checkpoint(
162 &self,
163 payload: &[u8],
164 objective: Option<f64>,
165 iteration: Option<u64>,
166 ) -> bool {
167 if !self.configured_store_is_available() {
168 return false;
169 }
170 let now = Instant::now();
171 let mut guard = match self.inner.lock() {
172 Ok(g) => g,
173 Err(p) => p.into_inner(),
174 };
175 let improves = match (objective, guard.best_seen) {
176 (Some(o), Some(b)) => o < b - 1e-12,
177 (Some(_), None) => true,
178 _ => false,
179 };
180 if !improves
181 && let Some(last) = guard.last_write
182 && now.duration_since(last) < MIN_CHECKPOINT_INTERVAL
183 {
184 return false;
185 }
186 match self.store.save_overwrite(
187 &self.key,
188 &self.run_id,
189 payload,
190 objective,
191 iteration,
192 EntryKind::Checkpoint,
193 ) {
194 Ok(()) => {
195 guard.last_write = Some(now);
196 if let Some(o) = objective {
197 guard.best_seen = Some(match guard.best_seen {
198 Some(b) => b.min(o),
199 None => o,
200 });
201 }
202 true
203 }
204 Err(error) => {
205 self.record_store_error("checkpoint outer-iterate session", &error);
206 false
207 }
208 }
209 }
210
211 pub fn finalize(&self, payload: &[u8], objective: Option<f64>, iteration: Option<u64>) -> bool {
214 if !self.configured_store_is_available() {
215 return false;
216 }
217 match self.store.save_overwrite(
218 &self.key,
219 &self.run_id,
220 payload,
221 objective,
222 iteration,
223 EntryKind::Final,
224 ) {
225 Ok(()) => true,
226 Err(error) => {
227 self.record_store_error("finalize outer-iterate session", &error);
228 false
229 }
230 }
231 }
232}
233