1use std::path::{Path, PathBuf};
4
5use crate::{platform, workspace::canonical_target};
6
7mod blocked;
8pub mod definitions;
9mod executions;
10pub(crate) mod history;
11mod human;
12pub use human::HumanClaim;
13mod catalog;
14pub(crate) mod connection;
15pub mod prune;
16pub(crate) mod receipts;
17mod result;
18mod runs;
19mod snapshots;
20mod unreadable;
21mod validation;
22pub use unreadable::{Unreadable, read_unreadable};
23mod lifecycle;
24pub(crate) mod migrations;
25pub(crate) mod run_control;
26pub use lifecycle::{ExecutionState, GreenResult, PendingRequest, RequestState, RunState};
27pub use result::{ExecutionResult, Field as ResultField, RuntimeResult};
28pub use snapshots::{
29 ArtifactValidation, Blocker, ChildIdentity, EvalValidation, HumanDefinition, Validation,
30 ValidationSnapshot,
31};
32mod wait_timeout;
33pub use catalog::{CatalogRun, Signoff, read_catalog};
34pub use runs::{RunSummary, read_runs, read_scoped_runs};
35pub(crate) mod reader;
36mod requests;
37pub(crate) use executions::read_profiled_executions;
38pub use executions::{
39 Capacity, Claim, Execution, ExecutionOptions, Origin, Producer, Provenance,
40 read_keyed_executions, read_latest_cached,
41};
42#[cfg(any(test, feature = "test-support"))]
43pub use receipts::OwnedReceipts;
44pub(crate) use receipts::regular_files as check_probe_files;
45pub use receipts::{
46 DATABASE, EARLIER_STATE, LastRequest, QueueReason, Receipts, Request, Run, RunView,
47 StoppedBackend, read_latest_requests, read_run, read_state_id, schema_error, state_schema,
48};
49pub use requests::read_request_repo;
50pub use requests::{
51 RequestView, read_original, read_request, read_request_batch, read_requests, read_requests_in,
52 read_run_requests, read_session_request, read_waiting, read_waiting_originals,
53};
54
55pub(crate) const SQLITE_BUSY_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
58
59const TRANSIENT_ACCESS_RETRIES: u32 = 5;
62const TRANSIENT_ACCESS_BACKOFF: std::time::Duration = std::time::Duration::from_millis(20);
63
64fn retry<T, E>(
68 mut attempt: impl FnMut() -> Result<T, E>,
69 transient: impl Fn(&E) -> bool,
70 mut before_retry: impl FnMut(),
71) -> Result<T, E> {
72 for remaining in (0..TRANSIENT_ACCESS_RETRIES).rev() {
73 match attempt() {
74 Ok(value) => return Ok(value),
75 Err(error) if remaining > 0 && transient(&error) => {
76 before_retry();
77 }
78 Err(error) => return Err(error),
79 }
80 }
81 unreachable!("the loop above always returns by its last attempt")
82}
83
84pub(crate) fn retry_transient_access<T>(
90 attempt: impl FnMut() -> std::io::Result<T>,
91 before_retry: impl FnMut(),
92) -> std::io::Result<T> {
93 retry(attempt, platform::transient_file_access, before_retry)
94}
95
96#[cfg(test)]
97mod retry_tests {
98 use std::{cell::Cell, io};
99
100 #[test]
101 fn file_probe_returns_success_and_permanent_errors_without_backoff() {
102 let calls = Cell::new(0);
103 let result = super::retry_transient_access(
104 || {
105 calls.set(calls.get() + 1);
106 Ok(7)
107 },
108 || panic!("successful probes need no backoff"),
109 );
110 assert_eq!(result.unwrap(), 7);
111 assert_eq!(calls.get(), 1);
112
113 let calls = Cell::new(0);
114 let result = super::retry_transient_access(
115 || {
116 calls.set(calls.get() + 1);
117 Err::<(), _>(io::Error::other("permanent probe failure"))
118 },
119 || panic!("permanent errors cannot be retried"),
120 );
121 assert_eq!(result.unwrap_err().to_string(), "permanent probe failure");
122 assert_eq!(calls.get(), 1);
123 }
124}
125
126pub fn state_dir(explicit: Option<&Path>) -> Result<PathBuf, String> {
128 let path = match explicit {
129 Some(path) => path.to_path_buf(),
130 None => state_home().map_err(|e| e.to_string())?,
131 };
132 canonical_target(&path).map_err(|e| e.to_string())
133}
134
135pub const STATE_SCHEMA_VERSION: u32 = 8;
138
139#[derive(Debug, thiserror::Error)]
140#[error("cannot resolve state home: set {}", state_home_variables())]
141pub struct StateHomeError;
142
143fn state_home_variables() -> String {
145 let names: Vec<&str> = std::iter::once("ARTIFACTIZE_STATE_HOME")
146 .chain(platform::STATE_VARIABLES.iter().copied())
147 .chain([platform::HOME_VARIABLE])
148 .collect();
149 let (last, rest) = names.split_last().expect("constant names");
150 format!("{}, or {last}", rest.join(", "))
151}
152
153pub fn state_home() -> Result<PathBuf, StateHomeError> {
156 platform::state_directory().ok_or(StateHomeError)
157}
158
159#[cfg(test)]
160#[path = "store/file_tests.rs"]
161mod file_tests;