@@ -125,7 +125,7 @@ impl KVStoreSync for FilesystemStore {
125125 }
126126
127127 fn remove (
128- & self , primary_namespace : & str , secondary_namespace : & str , key : & str ,
128+ & self , primary_namespace : & str , secondary_namespace : & str , key : & str , lazy : bool ,
129129 ) -> Result < ( ) , lightning:: io:: Error > {
130130 let path = self . inner . get_checked_dest_file_path (
131131 primary_namespace,
@@ -134,7 +134,7 @@ impl KVStoreSync for FilesystemStore {
134134 "remove" ,
135135 ) ?;
136136 let ( inner_lock_ref, version) = self . get_new_version_and_lock_ref ( path. clone ( ) ) ;
137- self . inner . remove_version ( inner_lock_ref, path, version)
137+ self . inner . remove_version ( inner_lock_ref, path, lazy , version)
138138 }
139139
140140 fn list (
@@ -334,76 +334,81 @@ impl FilesystemStoreInner {
334334 }
335335
336336 fn remove_version (
337- & self , inner_lock_ref : Arc < RwLock < u64 > > , dest_file_path : PathBuf , version : u64 ,
337+ & self , inner_lock_ref : Arc < RwLock < u64 > > , dest_file_path : PathBuf , lazy : bool , version : u64 ,
338338 ) -> lightning:: io:: Result < ( ) > {
339339 self . execute_locked_write ( inner_lock_ref, dest_file_path. clone ( ) , version, || {
340340 if !dest_file_path. is_file ( ) {
341341 return Ok ( ( ) ) ;
342342 }
343343
344- // We try our best to persist the updated metadata to ensure
345- // atomicity of this call.
346- #[ cfg( not( target_os = "windows" ) ) ]
347- {
344+ if lazy {
345+ // If we're lazy we just call remove and be done with it.
348346 fs:: remove_file ( & dest_file_path) ?;
347+ } else {
348+ // If we're not lazy we try our best to persist the updated metadata to ensure
349+ // atomicity of this call.
350+ #[ cfg( not( target_os = "windows" ) ) ]
351+ {
352+ fs:: remove_file ( & dest_file_path) ?;
349353
350- let parent_directory = dest_file_path. parent ( ) . ok_or_else ( || {
351- let msg = format ! (
352- "Could not retrieve parent directory of {}." ,
353- dest_file_path. display( )
354- ) ;
355- std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidInput , msg)
356- } ) ?;
357- let dir_file = fs:: OpenOptions :: new ( ) . read ( true ) . open ( parent_directory) ?;
358- // The above call to `fs::remove_file` corresponds to POSIX `unlink`, whose changes
359- // to the inode might get cached (and hence possibly lost on crash), depending on
360- // the target platform and file system.
361- //
362- // In order to assert we permanently removed the file in question we therefore
363- // call `fsync` on the parent directory on platforms that support it.
364- dir_file. sync_all ( ) ?;
365- }
354+ let parent_directory = dest_file_path. parent ( ) . ok_or_else ( || {
355+ let msg = format ! (
356+ "Could not retrieve parent directory of {}." ,
357+ dest_file_path. display( )
358+ ) ;
359+ std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidInput , msg)
360+ } ) ?;
361+ let dir_file = fs:: OpenOptions :: new ( ) . read ( true ) . open ( parent_directory) ?;
362+ // The above call to `fs::remove_file` corresponds to POSIX `unlink`, whose changes
363+ // to the inode might get cached (and hence possibly lost on crash), depending on
364+ // the target platform and file system.
365+ //
366+ // In order to assert we permanently removed the file in question we therefore
367+ // call `fsync` on the parent directory on platforms that support it.
368+ dir_file. sync_all ( ) ?;
369+ }
366370
367- #[ cfg( target_os = "windows" ) ]
368- {
369- // Since Windows `DeleteFile` API is not persisted until the last open file handle
370- // is dropped, and there seemingly is no reliable way to flush the directory
371- // metadata, we here fall back to use a 'recycling bin' model, i.e., first move the
372- // file to be deleted to a temporary trash file and remove the latter file
373- // afterwards.
374- //
375- // This should be marginally better, as, according to the documentation,
376- // `MoveFileExW` APIs should offer stronger persistence guarantees,
377- // at least if `MOVEFILE_WRITE_THROUGH`/`MOVEFILE_REPLACE_EXISTING` is set.
378- // However, all this is partially based on assumptions and local experiments, as
379- // Windows API is horribly underdocumented.
380- let mut trash_file_path = dest_file_path. clone ( ) ;
381- let trash_file_ext =
382- format ! ( "{}.trash" , self . tmp_file_counter. fetch_add( 1 , Ordering :: AcqRel ) ) ;
383- trash_file_path. set_extension ( trash_file_ext) ;
384-
385- call ! ( unsafe {
386- windows_sys:: Win32 :: Storage :: FileSystem :: MoveFileExW (
387- path_to_windows_str( & dest_file_path) . as_ptr( ) ,
388- path_to_windows_str( & trash_file_path) . as_ptr( ) ,
389- windows_sys:: Win32 :: Storage :: FileSystem :: MOVEFILE_WRITE_THROUGH
371+ #[ cfg( target_os = "windows" ) ]
372+ {
373+ // Since Windows `DeleteFile` API is not persisted until the last open file handle
374+ // is dropped, and there seemingly is no reliable way to flush the directory
375+ // metadata, we here fall back to use a 'recycling bin' model, i.e., first move the
376+ // file to be deleted to a temporary trash file and remove the latter file
377+ // afterwards.
378+ //
379+ // This should be marginally better, as, according to the documentation,
380+ // `MoveFileExW` APIs should offer stronger persistence guarantees,
381+ // at least if `MOVEFILE_WRITE_THROUGH`/`MOVEFILE_REPLACE_EXISTING` is set.
382+ // However, all this is partially based on assumptions and local experiments, as
383+ // Windows API is horribly underdocumented.
384+ let mut trash_file_path = dest_file_path. clone ( ) ;
385+ let trash_file_ext =
386+ format ! ( "{}.trash" , self . tmp_file_counter. fetch_add( 1 , Ordering :: AcqRel ) ) ;
387+ trash_file_path. set_extension ( trash_file_ext) ;
388+
389+ call ! ( unsafe {
390+ windows_sys:: Win32 :: Storage :: FileSystem :: MoveFileExW (
391+ path_to_windows_str( & dest_file_path) . as_ptr( ) ,
392+ path_to_windows_str( & trash_file_path) . as_ptr( ) ,
393+ windows_sys:: Win32 :: Storage :: FileSystem :: MOVEFILE_WRITE_THROUGH
390394 | windows_sys:: Win32 :: Storage :: FileSystem :: MOVEFILE_REPLACE_EXISTING ,
391- )
392- } ) ?;
395+ )
396+ } ) ?;
397+
398+ {
399+ // We fsync the trash file in hopes this will also flush the original's file
400+ // metadata to disk.
401+ let trash_file = fs:: OpenOptions :: new ( )
402+ . read ( true )
403+ . write ( true )
404+ . open ( & trash_file_path. clone ( ) ) ?;
405+ trash_file. sync_all ( ) ?;
406+ }
393407
394- {
395- // We fsync the trash file in hopes this will also flush the original's file
396- // metadata to disk.
397- let trash_file = fs:: OpenOptions :: new ( )
398- . read ( true )
399- . write ( true )
400- . open ( & trash_file_path. clone ( ) ) ?;
401- trash_file. sync_all ( ) ?;
408+ // We're fine if this remove would fail as the trash file will be cleaned up in
409+ // list eventually.
410+ fs:: remove_file ( trash_file_path) . ok ( ) ;
402411 }
403-
404- // We're fine if this remove would fail as the trash file will be cleaned up in
405- // list eventually.
406- fs:: remove_file ( trash_file_path) . ok ( ) ;
407412 }
408413
409414 Ok ( ( ) )
@@ -503,7 +508,7 @@ impl KVStore for FilesystemStore {
503508 }
504509
505510 fn remove (
506- & self , primary_namespace : & str , secondary_namespace : & str , key : & str ,
511+ & self , primary_namespace : & str , secondary_namespace : & str , key : & str , lazy : bool ,
507512 ) -> Pin < Box < dyn Future < Output = Result < ( ) , lightning:: io:: Error > > + ' static + Send > > {
508513 let this = Arc :: clone ( & self . inner ) ;
509514 let path = match this. get_checked_dest_file_path (
@@ -518,11 +523,11 @@ impl KVStore for FilesystemStore {
518523
519524 let ( inner_lock_ref, version) = self . get_new_version_and_lock_ref ( path. clone ( ) ) ;
520525 Box :: pin ( async move {
521- tokio:: task:: spawn_blocking ( move || this . remove_version ( inner_lock_ref , path , version ) )
522- . await
523- . unwrap_or_else ( |e| {
524- Err ( lightning :: io :: Error :: new ( lightning :: io :: ErrorKind :: Other , e ) )
525- } )
526+ tokio:: task:: spawn_blocking ( move || {
527+ this . remove_version ( inner_lock_ref , path , lazy , version )
528+ } )
529+ . await
530+ . unwrap_or_else ( |e| Err ( lightning :: io :: Error :: new ( lightning :: io :: ErrorKind :: Other , e ) ) )
526531 } )
527532 }
528533
@@ -767,7 +772,7 @@ mod tests {
767772 let fut1 = async_fs_store. write ( primary_namespace, secondary_namespace, key, data1) ;
768773 assert_eq ! ( fs_store. state_size( ) , 1 ) ;
769774
770- let fut2 = async_fs_store. remove ( primary_namespace, secondary_namespace, key) ;
775+ let fut2 = async_fs_store. remove ( primary_namespace, secondary_namespace, key, false ) ;
771776 assert_eq ! ( fs_store. state_size( ) , 1 ) ;
772777
773778 let fut3 = async_fs_store. write ( primary_namespace, secondary_namespace, key, data2. clone ( ) ) ;
@@ -794,7 +799,7 @@ mod tests {
794799 assert_eq ! ( data2, & * read_data) ;
795800
796801 // Test remove.
797- async_fs_store. remove ( primary_namespace, secondary_namespace, key) . await . unwrap ( ) ;
802+ async_fs_store. remove ( primary_namespace, secondary_namespace, key, false ) . await . unwrap ( ) ;
798803
799804 let listed_keys =
800805 async_fs_store. list ( primary_namespace, secondary_namespace) . await . unwrap ( ) ;
0 commit comments