Save the provenance accumulator for a session. Best-effort: failures are logged but never fatal.
(&self, session_id: &str, acc: &ProvenanceAccumulator)
| 136 | /// |
| 137 | /// Best-effort: failures are logged but never fatal. |
| 138 | pub(crate) fn save_accumulator(&self, session_id: &str, acc: &ProvenanceAccumulator) { |
| 139 | // We can't reuse with_accumulator here because we already have |
| 140 | // the accumulator in hand. Acquire the lock, write, release. |
| 141 | use fs2::FileExt; |
| 142 | |
| 143 | let dir = self.session_graph_dir(session_id); |
| 144 | let _ = std::fs::create_dir_all(&dir); |
| 145 | let lock_path = dir.join(LOCK_FILENAME); |
| 146 | |
| 147 | let lock_file = match std::fs::OpenOptions::new() |
| 148 | .create(true) |
| 149 | .write(true) |
| 150 | .truncate(false) |
| 151 | .open(&lock_path) |
| 152 | { |
| 153 | Ok(f) => f, |
| 154 | Err(e) => { |
| 155 | log::warn!("Failed to open lock file for session {}: {}", session_id, e); |
| 156 | return; |
| 157 | } |
| 158 | }; |
| 159 | |
| 160 | if let Err(e) = lock_file.try_lock_exclusive() { |
| 161 | if e.kind() == std::io::ErrorKind::WouldBlock { |
| 162 | log::warn!( |
| 163 | "Provenance accumulator for session {} is already locked; skipping save", |
| 164 | session_id, |
| 165 | ); |
| 166 | } else { |
| 167 | log::warn!("Failed to acquire lock for session {}: {}", session_id, e); |
| 168 | } |
| 169 | return; |
| 170 | } |
| 171 | |
| 172 | if let Err(e) = acc.save(&dir) { |
| 173 | log::warn!( |
| 174 | "Failed to save provenance accumulator for {}: {}", |
| 175 | session_id, |
| 176 | e, |
| 177 | ); |
| 178 | } |
| 179 | |
| 180 | let _ = lock_file.unlock(); |
| 181 | } |
| 182 | |
| 183 | /// Inject reasoning/thinking blocks from the stop event into the |
| 184 | /// ProvenanceAccumulator as Decision nodes. |