33
44use std:: {
55 borrow:: Cow ,
6- fs,
7- fs:: OpenOptions ,
8- io,
6+ fs, io,
97 io:: Write ,
108 ops:: Deref ,
119 path:: { Path , PathBuf } ,
@@ -25,6 +23,7 @@ use rkyv::{
2523 ser:: allocator:: ArenaHandle ,
2624 util:: AlignedVec ,
2725} ;
26+ use tempfile:: NamedTempFile ;
2827use threadpool:: ThreadPool ;
2928
3029use crate :: {
@@ -38,8 +37,6 @@ use crate::{
3837
3938// static CACHE_OBJS_MEM_SIZE: AtomicUsize = AtomicUsize::new(0);
4039
41- const MAX_TEMP_ATTEMPTS : u32 = 64 ;
42-
4340/// file load&store trait
4441pub trait FileLoadStore : Sized {
4542 fn f_load ( path : & Path ) -> Result < Self , io:: Error > ;
@@ -50,54 +47,18 @@ fn write_bytes_atomically(path: &Path, data: &[u8]) -> Result<(), io::Error> {
5047 if path. exists ( ) {
5148 return Ok ( ( ) ) ;
5249 }
53- let mut attempt = 0u32 ;
54-
55- loop {
56- if path. exists ( ) {
57- return Ok ( ( ) ) ;
58- }
50+ let dir = path
51+ . parent ( )
52+ . filter ( |parent| !parent. as_os_str ( ) . is_empty ( ) )
53+ . unwrap_or_else ( || Path :: new ( "." ) ) ;
5954
60- let temp_path = if attempt == 0 {
61- path. with_extension ( "temp" )
62- } else {
63- path. with_extension ( format ! ( "temp.{attempt}" ) )
64- } ;
55+ let mut temp_file = NamedTempFile :: new_in ( dir) ?;
56+ temp_file. write_all ( data) ?;
6557
66- match OpenOptions :: new ( )
67- . write ( true )
68- . create_new ( true )
69- . open ( & temp_path)
70- {
71- Ok ( mut file) => {
72- file. write_all ( data) ?;
73- drop ( file) ;
74-
75- match fs:: rename ( & temp_path, path) {
76- Ok ( ( ) ) => return Ok ( ( ) ) ,
77- Err ( err) if err. kind ( ) == io:: ErrorKind :: AlreadyExists && path. exists ( ) => {
78- let _ = fs:: remove_file ( & temp_path) ;
79- return Ok ( ( ) ) ;
80- }
81- Err ( err) => {
82- let _ = fs:: remove_file ( & temp_path) ;
83- return Err ( err) ;
84- }
85- }
86- }
87- Err ( err) if err. kind ( ) == io:: ErrorKind :: AlreadyExists && path. exists ( ) => {
88- return Ok ( ( ) ) ;
89- }
90- Err ( err) if err. kind ( ) == io:: ErrorKind :: AlreadyExists => {
91- attempt = attempt. saturating_add ( 1 ) ;
92- if attempt >= MAX_TEMP_ATTEMPTS {
93- return Err ( io:: Error :: new (
94- io:: ErrorKind :: AlreadyExists ,
95- format ! ( "exhausted temp file attempts for {}" , path. display( ) ) ,
96- ) ) ;
97- }
98- }
99- Err ( err) => return Err ( err) ,
100- }
58+ match temp_file. persist_noclobber ( path) {
59+ Ok ( _persisted) => Ok ( ( ) ) ,
60+ Err ( err) if err. error . kind ( ) == io:: ErrorKind :: AlreadyExists && path. exists ( ) => Ok ( ( ) ) ,
61+ Err ( err) => Err ( err. error ) ,
10162 }
10263}
10364
@@ -699,7 +660,7 @@ mod test {
699660 }
700661
701662 #[ test]
702- fn test_write_bytes_atomically_retries_when_temp_exists ( ) {
663+ fn test_write_bytes_atomically_ignores_stale_temp_file ( ) {
703664 let dir = tempdir ( ) . unwrap ( ) ;
704665 let path = dir. path ( ) . join ( "object" ) ;
705666
0 commit comments