statsig_rust/interned_values/interned_store/
mmap_artifact.rs1use std::{fs::Metadata, path::Path, time::UNIX_EPOCH};
2
3use crate::StatsigErr;
4
5use super::{
6 legacy_mmap_v1_path_for_sdk_key,
7 mmap_manifest::{inspect_mmap_v2_publication, MmapV2Publication},
8 mmap_manifest_path_for_sdk_key, mmap_v2_path_for_sdk_key, InternedStore,
9 LEGACY_MMAP_FORMAT_VERSION,
10};
11
12#[non_exhaustive]
18#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub enum MmapArtifactState {
20 Missing,
22 LegacyV1,
24 FallbackV1,
27 CommittedV2,
29 IncompleteV2,
32 Invalid,
34}
35
36impl MmapArtifactState {
37 pub const fn as_str(self) -> &'static str {
39 match self {
40 Self::Missing => "missing",
41 Self::LegacyV1 => "legacy_v1",
42 Self::FallbackV1 => "fallback_v1",
43 Self::CommittedV2 => "committed_v2",
44 Self::IncompleteV2 => "incomplete_v2",
45 Self::Invalid => "invalid",
46 }
47 }
48}
49
50#[non_exhaustive]
61#[derive(Clone, Debug, Eq, PartialEq)]
62pub struct MmapArtifactSnapshot {
63 pub state: MmapArtifactState,
64 pub format_version: Option<u32>,
67 pub v1_bytes: Option<u64>,
68 pub v2_bytes: Option<u64>,
69 pub manifest_bytes: Option<u64>,
70 pub total_linked_bytes: u64,
71 pub linked_file_count: u64,
72 pub newest_linked_modified_unix_seconds: Option<u64>,
74 pub filesystem_capacity_bytes: Option<u64>,
75 pub filesystem_available_bytes: Option<u64>,
76}
77
78impl InternedStore {
79 pub fn inspect_mmap_artifact(sdk_key: &str) -> Result<MmapArtifactSnapshot, StatsigErr> {
86 let v1_path = legacy_mmap_v1_path_for_sdk_key(sdk_key);
87 let v2_path = mmap_v2_path_for_sdk_key(sdk_key);
88 let manifest_path = mmap_manifest_path_for_sdk_key(sdk_key);
89
90 let publication = inspect_mmap_v2_publication(&manifest_path, &v1_path, &v2_path)?;
91 let v1_metadata = metadata_if_linked(&v1_path)?;
92 let manifest_metadata = metadata_if_linked(&manifest_path)?;
93
94 let (state, format_version, committed_v2_metadata) = match publication {
95 MmapV2Publication::Absent if v1_metadata.is_some() => (
96 MmapArtifactState::LegacyV1,
97 Some(LEGACY_MMAP_FORMAT_VERSION),
98 None,
99 ),
100 MmapV2Publication::Absent => (MmapArtifactState::Missing, None, None),
101 MmapV2Publication::Committed(file) => {
102 let metadata = file
103 .metadata()
104 .map_err(|error| StatsigErr::FileError(error.to_string()))?;
105 (
106 MmapArtifactState::CommittedV2,
107 Some(crate::interned_values::INTERNED_MMAP_FORMAT_VERSION),
108 Some(metadata),
109 )
110 }
111 MmapV2Publication::Incomplete => (MmapArtifactState::IncompleteV2, None, None),
112 MmapV2Publication::Invalid(_) if v1_metadata.is_some() => (
113 MmapArtifactState::FallbackV1,
114 Some(LEGACY_MMAP_FORMAT_VERSION),
115 None,
116 ),
117 MmapV2Publication::Invalid(_) => (MmapArtifactState::Invalid, None, None),
118 };
119
120 let v2_metadata = match committed_v2_metadata {
121 Some(metadata) => Some(metadata),
122 None => metadata_if_linked(&v2_path)?,
123 };
124 let v1_bytes = v1_metadata.as_ref().map(Metadata::len);
125 let v2_bytes = v2_metadata.as_ref().map(Metadata::len);
126 let manifest_bytes = manifest_metadata.as_ref().map(Metadata::len);
127 let total_linked_bytes = [v1_bytes, v2_bytes, manifest_bytes]
128 .into_iter()
129 .flatten()
130 .fold(0_u64, u64::saturating_add);
131 let linked_file_count = [v1_bytes, v2_bytes, manifest_bytes]
132 .into_iter()
133 .filter(Option::is_some)
134 .count() as u64;
135 let newest_linked_modified_unix_seconds = [
136 v1_metadata.as_ref(),
137 v2_metadata.as_ref(),
138 manifest_metadata.as_ref(),
139 ]
140 .into_iter()
141 .flatten()
142 .filter_map(modified_unix_seconds)
143 .max();
144 let (filesystem_capacity_bytes, filesystem_available_bytes) = manifest_path
145 .parent()
146 .map(filesystem_space)
147 .unwrap_or((None, None));
148
149 Ok(MmapArtifactSnapshot {
150 state,
151 format_version,
152 v1_bytes,
153 v2_bytes,
154 manifest_bytes,
155 total_linked_bytes,
156 linked_file_count,
157 newest_linked_modified_unix_seconds,
158 filesystem_capacity_bytes,
159 filesystem_available_bytes,
160 })
161 }
162}
163
164fn metadata_if_linked(path: &Path) -> Result<Option<Metadata>, StatsigErr> {
165 match path.metadata() {
166 Ok(metadata) => Ok(Some(metadata)),
167 Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None),
168 Err(error) => Err(StatsigErr::FileError(error.to_string())),
169 }
170}
171
172fn modified_unix_seconds(metadata: &Metadata) -> Option<u64> {
173 metadata
174 .modified()
175 .ok()?
176 .duration_since(UNIX_EPOCH)
177 .ok()
178 .map(|duration| duration.as_secs())
179}
180
181#[cfg(any(unix, windows))]
182fn filesystem_space(path: &Path) -> (Option<u64>, Option<u64>) {
183 fs4::statvfs(path)
184 .map(|stats| (Some(stats.total_space()), Some(stats.available_space())))
185 .unwrap_or((None, None))
186}
187
188#[cfg(not(any(unix, windows)))]
189fn filesystem_space(_path: &Path) -> (Option<u64>, Option<u64>) {
190 (None, None)
191}