Skip to main content

dynamo_runtime/storage/kv/
file.rs

1// SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::cmp;
5use std::collections::HashSet;
6use std::ffi::OsString;
7use std::fmt;
8use std::fs;
9use std::fs::OpenOptions;
10use std::io::{ErrorKind, Write};
11use std::os::unix::ffi::OsStrExt;
12use std::path::{Path, PathBuf};
13use std::sync::Arc;
14use std::thread;
15use std::time::Duration;
16use std::time::SystemTime;
17use std::{collections::HashMap, pin::Pin};
18
19use anyhow::Context as _;
20use async_trait::async_trait;
21use futures::StreamExt;
22use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher, event};
23use parking_lot::Mutex;
24use tokio_util::sync::CancellationToken;
25
26use super::{Bucket, Key, KeyValue, Store, StoreError, StoreOutcome, WatchEvent};
27
28/// How long until a key expires. We keep the keys alive by touching the files.
29/// 10s is the same as our etcd lease expiry.
30const DEFAULT_TTL: Duration = Duration::from_secs(10);
31
32/// Don't do keep-alive any more often than this. Limits the disk write load.
33const MIN_KEEP_ALIVE: Duration = Duration::from_secs(1);
34
35/// Prefix for temporary files used in atomic writes.
36/// Files with this prefix are ignored by the watcher.
37const TEMP_FILE_PREFIX: &str = ".tmp_";
38const TEMP_FILE_CREATE_ATTEMPTS: usize = 16;
39
40/// Treat as a singleton
41#[derive(Clone)]
42pub struct FileStore {
43    cancel_token: CancellationToken,
44    root: PathBuf,
45    connection_id: u64,
46    /// Directories we may have created files in, for shutdown cleanup and keep-alive.
47    /// Arc so that we only ever have one map here after clone.
48    active_dirs: Arc<Mutex<HashMap<PathBuf, Directory>>>,
49}
50
51impl FileStore {
52    pub(super) fn new<P: Into<PathBuf>>(cancel_token: CancellationToken, root_dir: P) -> Self {
53        let fs = FileStore {
54            cancel_token,
55            root: root_dir.into(),
56            connection_id: rand::random::<u64>(),
57            active_dirs: Arc::new(Mutex::new(HashMap::new())),
58        };
59        let c = fs.clone();
60        thread::spawn(move || c.expiry_thread());
61        fs
62    }
63
64    /// Keep our files alive and delete expired keys.
65    ///
66    /// Does not return until cancellation token cancelled. On shutdown the process will
67    /// often exit before we detect cancellation. That's fine.
68    /// We run this in a real thread so it doesn't get delayed by tokio runtime under heavy load.
69    fn expiry_thread(&self) {
70        loop {
71            let ttl = self.shortest_ttl();
72            let keep_alive_interval = cmp::max(ttl / 3, MIN_KEEP_ALIVE);
73
74            // Check before and after the sleep
75            if self.cancel_token.is_cancelled() {
76                break;
77            }
78
79            thread::sleep(keep_alive_interval);
80
81            if self.cancel_token.is_cancelled() {
82                break;
83            }
84
85            self.keep_alive();
86            if let Err(err) = self.delete_expired_files() {
87                tracing::error!(error = %err, "FileStore delete_expired_files");
88            }
89        }
90    }
91
92    /// The shortest TTL of any directory we are using.
93    fn shortest_ttl(&self) -> Duration {
94        let mut ttl = DEFAULT_TTL;
95        let active_dirs = self.active_dirs.lock().clone();
96        for (_, dir) in active_dirs {
97            ttl = cmp::min(ttl, dir.ttl);
98        }
99        tracing::trace!("FileStore expiry shortest ttl {ttl:?}");
100        ttl
101    }
102
103    fn keep_alive(&self) {
104        let active_dirs = self.active_dirs.lock().clone();
105        for (_, dir) in active_dirs {
106            dir.keep_alive();
107        }
108    }
109
110    fn delete_expired_files(&self) -> anyhow::Result<()> {
111        let active_dirs = self.active_dirs.lock().clone();
112        for (path, dir) in active_dirs {
113            dir.delete_expired_files()
114                .with_context(|| path.display().to_string())?;
115        }
116        Ok(())
117    }
118}
119
120#[async_trait]
121impl Store for FileStore {
122    type Bucket = Directory;
123
124    /// A "bucket" is a directory
125    async fn get_or_create_bucket(
126        &self,
127        bucket_name: &str,
128        ttl: Option<Duration>,
129    ) -> Result<Self::Bucket, StoreError> {
130        let p = self.root.join(bucket_name);
131        if let Some(dir) = self.active_dirs.lock().get(&p) {
132            return Ok(dir.clone());
133        };
134
135        if p.exists() {
136            // Get
137            if !p.is_dir() {
138                return Err(StoreError::FilesystemError(
139                    "Bucket name is not a directory".to_string(),
140                ));
141            }
142        } else {
143            // Create
144            fs::create_dir_all(&p).map_err(to_fs_err)?;
145        }
146        let dir = Directory::new(self.root.clone(), p.clone(), ttl.unwrap_or(DEFAULT_TTL));
147        self.active_dirs.lock().insert(p, dir.clone());
148        Ok(dir)
149    }
150
151    /// A "bucket" is a directory
152    async fn get_bucket(&self, bucket_name: &str) -> Result<Option<Self::Bucket>, StoreError> {
153        let p = self.root.join(bucket_name);
154        if let Some(dir) = self.active_dirs.lock().get(&p) {
155            return Ok(Some(dir.clone()));
156        };
157
158        if !p.exists() {
159            return Ok(None);
160        }
161        if !p.is_dir() {
162            return Err(StoreError::FilesystemError(
163                "Bucket name is not a directory".to_string(),
164            ));
165        }
166        // The filesystem itself doesn't store the TTL so for now default it
167        let dir = Directory::new(self.root.clone(), p.clone(), DEFAULT_TTL);
168        self.active_dirs.lock().insert(p, dir.clone());
169        Ok(Some(dir))
170    }
171
172    fn connection_id(&self) -> u64 {
173        self.connection_id
174    }
175
176    // This cannot be a Drop imp because DistributedRuntime is cloned various places including
177    // Python. Drop doesn't get called.
178    fn shutdown(&self) {
179        for (_, mut dir) in self.active_dirs.lock().drain() {
180            if let Err(err) = dir.delete_owned_files() {
181                tracing::error!(error = %err, %dir, "Failed shutdown delete of owned files");
182            }
183        }
184    }
185}
186
187#[derive(Clone)]
188pub struct Directory {
189    root: PathBuf,
190    p: PathBuf,
191    ttl: Duration,
192    /// These are the files we created and hence must delete on shutdown
193    owned_files: Arc<Mutex<HashSet<PathBuf>>>,
194}
195
196impl Directory {
197    fn new(root: PathBuf, p: PathBuf, ttl: Duration) -> Self {
198        // Canonicalize root to handle symlinks (e.g., /var -> /private/var on macOS)
199        let canonical_root = root.canonicalize().unwrap_or_else(|_| root.clone());
200        if ttl < MIN_KEEP_ALIVE {
201            let h_ttl = humantime::format_duration(ttl);
202            tracing::warn!(path = %p.display(), ttl = %h_ttl, "ttl is too short, increasing to {}", humantime::format_duration(MIN_KEEP_ALIVE));
203        }
204        let ttl = cmp::max(ttl, MIN_KEEP_ALIVE);
205        Directory {
206            root: canonical_root,
207            p,
208            ttl,
209            owned_files: Arc::new(Mutex::new(HashSet::new())),
210        }
211    }
212
213    /// touch the files we own so they don't get deleted by a different FileStore
214    fn keep_alive(&self) {
215        let owned_files = self.owned_files.lock().clone();
216        for path in owned_files {
217            let file = match OpenOptions::new().write(true).open(&path) {
218                Ok(f) => f,
219                Err(err) => {
220                    tracing::error!(path = %path.display(), error = %err, "FileStore::keep_alive failed opening owned file");
221                    continue;
222                }
223            };
224            if let Err(err) = file.set_modified(SystemTime::now()) {
225                tracing::error!(path = %path.display(), error = %err, "FileStore::keep_alive failed set_modified on owned file");
226                continue;
227            }
228            tracing::trace!("FileStore keep_alive set {}", path.display());
229        }
230    }
231
232    /// Remove any files not touched for longer than TTL.
233    /// This looks at all files in the directory to catch orphaned files from processes that didn't stop cleanly.
234    /// Returns an error if we cannot open the directory. Errors inside the directory are logged
235    /// but non-fatal.
236    fn delete_expired_files(&self) -> anyhow::Result<()> {
237        let deadline = SystemTime::now() - self.ttl;
238        let dirname = self.p.display().to_string();
239        for entry in fs::read_dir(&self.p).with_context(|| dirname.clone())? {
240            let entry = match entry {
241                Ok(p) => p,
242                Err(err) => {
243                    tracing::warn!(dir = dirname, error = %err, "File store could read directory contents");
244                    continue;
245                }
246            };
247            if !entry.file_type().map(|f| f.is_file()).unwrap_or(false) {
248                tracing::warn!(dir = dirname, entry = %entry.path().display(), "File store directory should only contain files");
249                continue;
250            }
251            let ctx = entry.path().display().to_string();
252            let metadata = match entry.metadata() {
253                Ok(m) => m,
254                Err(err) => {
255                    tracing::warn!(path = %ctx, error = %err, "Failed fetching metadata");
256                    continue;
257                }
258            };
259            let last_modified = match metadata.modified() {
260                Ok(lm) => lm,
261                Err(err) => {
262                    // We should only get an error on platforms with no mtime, which we don't
263                    // support anyway.
264                    tracing::warn!(path = %ctx, error = %err, "Failed reading mtime");
265                    continue;
266                }
267            };
268            if last_modified < deadline {
269                tracing::info!(path = ctx, ?last_modified, "Expired");
270                if let Err(err) = fs::remove_file(entry.path()) {
271                    tracing::warn!(path = %ctx, error = %err, "Failed removing");
272                }
273            }
274        }
275        Ok(())
276    }
277
278    fn delete_owned_files(&mut self) -> anyhow::Result<()> {
279        let mut errs = Vec::new();
280        for p in self.owned_files.lock().drain() {
281            if let Err(err) = fs::remove_file(&p) {
282                errs.push(format!("{}: {err}", p.display()));
283            }
284        }
285        if !errs.is_empty() {
286            anyhow::bail!(errs.join(", "));
287        }
288        Ok(())
289    }
290
291    fn write_temp_file(&self, value: &[u8]) -> Result<PathBuf, StoreError> {
292        for _ in 0..TEMP_FILE_CREATE_ATTEMPTS {
293            let temp_name = format!("{TEMP_FILE_PREFIX}{:016x}", rand::random::<u64>());
294            let temp_path = self.p.join(&temp_name);
295            if write_temp_file_at(&temp_path, value)? {
296                return Ok(temp_path);
297            }
298        }
299
300        Err(StoreError::FilesystemError(format!(
301            "failed to create unique FileStore temp file in {} after {TEMP_FILE_CREATE_ATTEMPTS} attempts",
302            self.p.display()
303        )))
304    }
305}
306
307impl fmt::Display for Directory {
308    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
309        write!(f, "{}", self.p.display())
310    }
311}
312
313#[async_trait]
314impl Bucket for Directory {
315    /// Write a file to the directory by publishing a completed temp file.
316    /// This ensures watchers never see a partially written file.
317    /// Revision-zero inserts provide create-if-absent publication for this
318    /// FileStore path, but not leases, fencing, crash durability, or strict
319    /// runtime-wide cardinality guarantees.
320    async fn insert(
321        &self,
322        key: &Key,
323        value: bytes::Bytes,
324        revision: u64,
325    ) -> Result<StoreOutcome, StoreError> {
326        let safe_key = key.url_safe();
327        let full_path = self.p.join(safe_key.as_ref());
328        let str_path = full_path.display().to_string();
329
330        let temp_path = self.write_temp_file(&value)?;
331
332        if revision == 0 {
333            // No-clobber publish for revision-zero inserts: the link fails if another
334            // writer already created the key, and readers never see a partial target file.
335            match fs::hard_link(&temp_path, &full_path) {
336                Ok(()) => {
337                    if let Err(err) = fs::remove_file(&temp_path) {
338                        tracing::warn!(
339                            path = %temp_path.display(),
340                            error = %err,
341                            "Failed to remove FileStore temp file after create-if-absent publish"
342                        );
343                    }
344                    self.owned_files.lock().insert(full_path.clone());
345                    return Ok(StoreOutcome::Created(0));
346                }
347                Err(err) if err.kind() == ErrorKind::AlreadyExists => {
348                    if let Err(remove_err) = fs::remove_file(&temp_path) {
349                        tracing::warn!(
350                            path = %temp_path.display(),
351                            error = %remove_err,
352                            "Failed to remove unused FileStore temp file after create-if-absent conflict"
353                        );
354                    }
355                    return Ok(StoreOutcome::Exists(0));
356                }
357                Err(err) => {
358                    if let Err(remove_err) = fs::remove_file(&temp_path) {
359                        tracing::warn!(
360                            path = %temp_path.display(),
361                            error = %remove_err,
362                            "Failed to remove unused FileStore temp file after create-if-absent error"
363                        );
364                    }
365                    return Err(to_fs_err(err));
366                }
367            }
368        }
369
370        // Atomic rename to target path
371        fs::rename(&temp_path, &full_path)
372            .with_context(|| format!("renaming {} to {}", temp_path.display(), str_path))
373            .map_err(a_to_fs_err)?;
374
375        self.owned_files.lock().insert(full_path.clone());
376        Ok(StoreOutcome::Created(revision))
377    }
378
379    /// Read a file from the directory
380    async fn get(&self, key: &Key) -> Result<Option<bytes::Bytes>, StoreError> {
381        let safe_key = key.url_safe();
382        let full_path = self.p.join(safe_key.as_ref());
383        if !full_path.exists() {
384            return Ok(None);
385        }
386        let str_path = full_path.display().to_string();
387        let data: bytes::Bytes = fs::read(&full_path)
388            .context(str_path)
389            .map_err(a_to_fs_err)?
390            .into();
391        Ok(Some(data))
392    }
393
394    /// Delete a file from the directory
395    async fn delete(&self, key: &Key) -> Result<(), StoreError> {
396        let safe_key = key.url_safe();
397        let full_path = self.p.join(safe_key.as_ref());
398        let str_path = full_path.display().to_string();
399        if !full_path.exists() {
400            return Err(StoreError::MissingKey(str_path));
401        }
402
403        self.owned_files.lock().remove(&full_path);
404
405        fs::remove_file(&full_path)
406            .context(str_path)
407            .map_err(a_to_fs_err)
408    }
409
410    async fn watch(
411        &self,
412    ) -> Result<Pin<Box<dyn futures::Stream<Item = WatchEvent> + Send + 'life0>>, StoreError> {
413        let (tx, mut rx) = tokio::sync::mpsc::channel(128);
414
415        let mut watcher = RecommendedWatcher::new(
416            move |res: Result<Event, notify::Error>| {
417                if let Err(err) = tx.blocking_send(res) {
418                    tracing::error!(error = %err, "Failed to send file watch event");
419                }
420            },
421            Config::default(),
422        )
423        .map_err(to_fs_err)?;
424
425        watcher
426            .watch(&self.p, RecursiveMode::NonRecursive)
427            .map_err(to_fs_err)?;
428
429        let dir = self.p.clone();
430        let root = self.root.clone();
431
432        Ok(Box::pin(async_stream::stream! {
433            // Keep watcher alive for the duration of the stream
434            let _watcher = watcher;
435
436            while let Some(event_result) = rx.recv().await {
437                let event = match event_result {
438                    Ok(event) => event,
439                    Err(err) => {
440                        tracing::error!(error = %err, "Failed receiving file watch event");
441                        continue;
442                    }
443                };
444
445                for item_path in event.paths {
446                    // Skip if the event is for the directory itself
447                    if item_path == dir {
448                        tracing::warn!("Unexpected event on the directory itself");
449                        continue;
450                    }
451
452                    // Canonicalize paths to handle symlinks (e.g., /var -> /private/var on macOS)
453                    // The unwrap_or_else path is for Remove case.
454                    let canonical_item_path = item_path.canonicalize().unwrap_or_else(|_| item_path.clone());
455
456                    let key = match canonical_item_path.strip_prefix(&root) {
457                        Ok(stripped) => Key::from_url_safe(&stripped.display().to_string()),
458                        Err(err) => {
459                            // Possibly this should be a panic.
460                            // A key cannot be outside the file store root.
461                            tracing::error!(
462                                error = %err,
463                                item_path = %canonical_item_path.display(),
464                                root = %root.display(),
465                                "Item in file store is not prefixed with file store root. Should be impossible. Ignoring invalid key.");
466                            continue;
467                        }
468                    };
469
470                    // Skip temp files used for atomic writes
471                    if item_path.file_name()
472                        .map(|n| n.to_string_lossy().starts_with(TEMP_FILE_PREFIX))
473                        .unwrap_or(false)
474                    {
475                        continue;
476                    }
477
478                    match event.kind {
479                        // Handle file creation, modification, and rename-to (from atomic writes)
480                        EventKind::Create(event::CreateKind::File)
481                        | EventKind::Modify(event::ModifyKind::Data(event::DataChange::Content))
482                        | EventKind::Modify(event::ModifyKind::Name(event::RenameMode::To)) => {
483                            let data: bytes::Bytes = match fs::read(&item_path) {
484                                Ok(data) => data.into(),
485                                Err(err) => {
486                                    tracing::warn!(error = %err, item = %item_path.display(), "Failed reading event item. Skipping.");
487                                    continue;
488                                }
489                            };
490                            let item = KeyValue::new(key, data);
491                            yield WatchEvent::Put(item);
492                        }
493                        EventKind::Remove(event::RemoveKind::File) => {
494                            yield WatchEvent::Delete(key);
495                        }
496                        _ => {
497                            // These happen every time the keep-alive updates last modified time
498                            continue;
499                        }
500                    }
501                }
502            }
503        }))
504    }
505
506    async fn entries(&self) -> Result<HashMap<Key, bytes::Bytes>, StoreError> {
507        let contents = fs::read_dir(&self.p)
508            .with_context(|| self.p.display().to_string())
509            .map_err(a_to_fs_err)?;
510        let mut out = HashMap::new();
511        for entry in contents {
512            let entry = entry.map_err(to_fs_err)?;
513            if !entry.path().is_file() {
514                tracing::warn!(
515                    path = %entry.path().display(),
516                    "Unexpected entry, directory should only contain files."
517                );
518                continue;
519            }
520
521            // Skip temp files used for atomic writes
522            if entry
523                .file_name()
524                .to_string_lossy()
525                .starts_with(TEMP_FILE_PREFIX)
526            {
527                continue;
528            }
529
530            // Canonicalize paths to handle symlinks (e.g., /var -> /private/var on macOS)
531            let canonical_entry_path = match entry.path().canonicalize() {
532                Ok(p) => p,
533                Err(err) => {
534                    tracing::warn!(error = %err, path = %entry.path().display(), "Failed to canonicalize path. Using original path.");
535                    entry.path()
536                }
537            };
538
539            let key = match canonical_entry_path.strip_prefix(&self.root) {
540                Ok(p) => Key::from_url_safe(&p.to_string_lossy()),
541                Err(err) => {
542                    tracing::error!(
543                        error = %err,
544                        path = %canonical_entry_path.display(),
545                        root = %self.root.display(),
546                        "FileStore path not in root. Should be impossible. Skipping entry."
547                    );
548                    continue;
549                }
550            };
551            let data: bytes::Bytes = fs::read(entry.path())
552                .with_context(|| self.p.display().to_string())
553                .map_err(a_to_fs_err)?
554                .into();
555            out.insert(key, data);
556        }
557        Ok(out)
558    }
559}
560
561fn write_temp_file_at(temp_path: &Path, value: &[u8]) -> Result<bool, StoreError> {
562    let mut file = match OpenOptions::new()
563        .write(true)
564        .create_new(true)
565        .open(temp_path)
566    {
567        Ok(file) => file,
568        Err(err) if err.kind() == ErrorKind::AlreadyExists => return Ok(false),
569        Err(err) => {
570            let err = anyhow::Error::new(err)
571                .context(format!("creating temp file {}", temp_path.display()));
572            return Err(a_to_fs_err(err));
573        }
574    };
575
576    if let Err(err) = file.write_all(value) {
577        if let Err(remove_err) = fs::remove_file(temp_path) {
578            tracing::warn!(
579                path = %temp_path.display(),
580                error = %remove_err,
581                "Failed to remove FileStore temp file after write error"
582            );
583        }
584        let err =
585            anyhow::Error::new(err).context(format!("writing temp file {}", temp_path.display()));
586        return Err(a_to_fs_err(err));
587    }
588
589    Ok(true)
590}
591
592// For anyhow preserve the context
593fn a_to_fs_err(err: anyhow::Error) -> StoreError {
594    StoreError::FilesystemError(format!("{err:#}"))
595}
596
597fn to_fs_err<E: std::error::Error>(err: E) -> StoreError {
598    StoreError::FilesystemError(err.to_string())
599}
600
601#[cfg(test)]
602mod tests {
603    use std::collections::HashSet;
604    use std::fs;
605
606    use tokio_util::sync::CancellationToken;
607
608    use crate::storage::kv::{Bucket as _, FileStore, Key, Store as _, StoreOutcome};
609
610    #[tokio::test]
611    async fn test_entries_full_path() {
612        let t = tempfile::tempdir().unwrap();
613
614        let cancel_token = CancellationToken::new();
615        let m = FileStore::new(cancel_token.clone(), t.path());
616        let bucket = m.get_or_create_bucket("v1/tests", None).await.unwrap();
617        let _ = bucket
618            .insert(&Key::new("key1/multi/part".to_string()), "value1".into(), 0)
619            .await
620            .unwrap();
621        let _ = bucket
622            .insert(&Key::new("key2".to_string()), "value2".into(), 0)
623            .await
624            .unwrap();
625        let entries = bucket.entries().await.unwrap();
626        let keys: HashSet<Key> = entries.into_keys().collect();
627        cancel_token.cancel(); // stop the background thread
628
629        assert!(keys.contains(&Key::new("v1/tests/key1/multi/part".to_string())));
630        assert!(keys.contains(&Key::new("v1/tests/key2".to_string())));
631    }
632
633    #[test]
634    fn test_temp_file_creation_does_not_overwrite_existing_path() {
635        let t = tempfile::tempdir().unwrap();
636        let temp_path = t.path().join(".tmp_existing");
637
638        fs::write(&temp_path, b"sentinel").unwrap();
639        let created = super::write_temp_file_at(&temp_path, b"new").unwrap();
640
641        assert!(!created);
642        assert_eq!(fs::read(&temp_path).unwrap(), b"sentinel");
643    }
644
645    #[tokio::test]
646    async fn test_insert_revision_zero_is_create_if_absent() {
647        let t = tempfile::tempdir().unwrap();
648
649        let cancel_token = CancellationToken::new();
650        let m = FileStore::new(cancel_token.clone(), t.path());
651        let bucket = m.get_or_create_bucket("v1/tests", None).await.unwrap();
652        let key = Key::new("singleton".to_string());
653
654        let first = bucket.insert(&key, "winner".into(), 0).await.unwrap();
655        let second = bucket.insert(&key, "loser".into(), 0).await.unwrap();
656        let value = bucket.get(&key).await.unwrap().unwrap();
657        cancel_token.cancel();
658
659        assert_eq!(first, StoreOutcome::Created(0));
660        assert_eq!(second, StoreOutcome::Exists(0));
661        assert_eq!(value.as_ref(), b"winner");
662    }
663
664    #[tokio::test]
665    async fn test_insert_nonzero_revision_overwrites() {
666        let t = tempfile::tempdir().unwrap();
667
668        let cancel_token = CancellationToken::new();
669        let m = FileStore::new(cancel_token.clone(), t.path());
670        let bucket = m.get_or_create_bucket("v1/tests", None).await.unwrap();
671        let key = Key::new("existing".to_string());
672
673        bucket.insert(&key, "old".into(), 0).await.unwrap();
674        let outcome = bucket.insert(&key, "new".into(), 1).await.unwrap();
675        let value = bucket.get(&key).await.unwrap().unwrap();
676        cancel_token.cancel();
677
678        assert_eq!(outcome, StoreOutcome::Created(1));
679        assert_eq!(value.as_ref(), b"new");
680    }
681
682    #[tokio::test]
683    async fn test_concurrent_insert_revision_zero_has_one_winner() {
684        let t = tempfile::tempdir().unwrap();
685        let root = t.path().to_path_buf();
686        let key = Key::new("singleton".to_string());
687
688        let mut tasks = Vec::new();
689        for index in 0..16 {
690            let root = root.clone();
691            let key = key.clone();
692            tasks.push(tokio::spawn(async move {
693                let cancel_token = CancellationToken::new();
694                let store = FileStore::new(cancel_token.clone(), root);
695                let bucket = store.get_or_create_bucket("v1/claims", None).await.unwrap();
696                let value = format!("value-{index}");
697                let outcome = bucket.insert(&key, value.clone().into(), 0).await.unwrap();
698                let stored = bucket.get(&key).await.unwrap().unwrap();
699                cancel_token.cancel();
700                (outcome, String::from_utf8(stored.to_vec()).unwrap(), value)
701            }));
702        }
703
704        let mut created_values = Vec::new();
705        let mut observed_values = HashSet::new();
706        for task in tasks {
707            let (outcome, stored, attempted) = task.await.unwrap();
708            observed_values.insert(stored);
709            if outcome == StoreOutcome::Created(0) {
710                created_values.push(attempted);
711            } else {
712                assert_eq!(outcome, StoreOutcome::Exists(0));
713            }
714        }
715
716        assert_eq!(created_values.len(), 1);
717        assert_eq!(observed_values.len(), 1);
718        assert_eq!(
719            observed_values.into_iter().next().unwrap(),
720            created_values.pop().unwrap()
721        );
722    }
723}