@@ -26,7 +26,7 @@ use bytes::Bytes;
2626use futures:: executor:: block_on;
2727use futures:: task:: Poll ;
2828use futures:: { Future , FutureExt , poll} ;
29- use nativelink_config:: stores:: FilesystemSpec ;
29+ use nativelink_config:: stores:: { EvictionPolicy , FilesystemSpec } ;
3030use nativelink_error:: { Code , Error , ResultExt , make_err} ;
3131use nativelink_macro:: nativelink_test;
3232use nativelink_store:: filesystem_store:: {
@@ -41,7 +41,8 @@ use nativelink_util::{background_spawn, spawn};
4141use opentelemetry:: context:: { Context , FutureExt as OtelFutureExt } ;
4242use parking_lot:: Mutex ;
4343use pretty_assertions:: assert_eq;
44- use rand:: Rng ;
44+ use rand:: rngs:: SmallRng ;
45+ use rand:: { Rng , SeedableRng } ;
4546use sha2:: { Digest , Sha256 } ;
4647use tokio:: io:: { AsyncReadExt , AsyncSeekExt , AsyncWriteExt , Take } ;
4748use tokio:: sync:: { Barrier , Semaphore } ;
@@ -50,6 +51,15 @@ use tokio_stream::StreamExt;
5051use tokio_stream:: wrappers:: ReadDirStream ;
5152use tracing:: Instrument ;
5253
54+ const VALID_HASH : & str = "0123456789abcdef000000000000000000010000000000000123456789abcdef" ;
55+
56+ fn make_random_data ( sz : usize ) -> Vec < u8 > {
57+ let mut value = vec ! [ 0u8 ; sz] ;
58+ let mut rng = SmallRng :: seed_from_u64 ( 1 ) ;
59+ rng. fill ( & mut value[ ..] ) ;
60+ value
61+ }
62+
5363trait FileEntryHooks {
5464 fn on_make_and_open (
5565 _encoded_file_path : & EncodedFilePath ,
@@ -331,7 +341,7 @@ async fn temp_files_get_deleted_on_replace_test() -> Result<(), Error> {
331341 FilesystemStore :: < TestFileEntry < LocalHooks > > :: new ( & FilesystemSpec {
332342 content_path : content_path. clone ( ) ,
333343 temp_path : temp_path. clone ( ) ,
334- eviction_policy : Some ( nativelink_config :: stores :: EvictionPolicy {
344+ eviction_policy : Some ( EvictionPolicy {
335345 max_count : 3 ,
336346 ..Default :: default ( )
337347 } ) ,
@@ -404,7 +414,7 @@ async fn file_continues_to_stream_on_content_replace_test() -> Result<(), Error>
404414 FilesystemStore :: < TestFileEntry < LocalHooks > > :: new ( & FilesystemSpec {
405415 content_path : content_path. clone ( ) ,
406416 temp_path : temp_path. clone ( ) ,
407- eviction_policy : Some ( nativelink_config :: stores :: EvictionPolicy {
417+ eviction_policy : Some ( EvictionPolicy {
408418 max_count : 3 ,
409419 ..Default :: default ( )
410420 } ) ,
@@ -512,7 +522,7 @@ async fn file_gets_cleans_up_on_cache_eviction() -> Result<(), Error> {
512522 FilesystemStore :: < TestFileEntry < LocalHooks > > :: new ( & FilesystemSpec {
513523 content_path : content_path. clone ( ) ,
514524 temp_path : temp_path. clone ( ) ,
515- eviction_policy : Some ( nativelink_config :: stores :: EvictionPolicy {
525+ eviction_policy : Some ( EvictionPolicy {
516526 max_count : 1 ,
517527 ..Default :: default ( )
518528 } ) ,
@@ -658,7 +668,7 @@ async fn eviction_on_insert_calls_unref_once() -> Result<(), Error> {
658668 FilesystemStore :: < TestFileEntry < LocalHooks > > :: new ( & FilesystemSpec {
659669 content_path : make_temp_path ( "content_path" ) ,
660670 temp_path : make_temp_path ( "temp_path" ) ,
661- eviction_policy : Some ( nativelink_config :: stores :: EvictionPolicy {
671+ eviction_policy : Some ( EvictionPolicy {
662672 max_bytes : 5 ,
663673 ..Default :: default ( )
664674 } ) ,
@@ -1345,3 +1355,56 @@ async fn file_slot_taken_when_ready() -> Result<(), Error> {
13451355 . map_err ( |_| make_err ! ( Code :: Internal , "Deadlock detected" ) ) ?;
13461356 res_1. merge ( res_2) . merge ( res_3) . merge ( res_4)
13471357}
1358+
1359+ // If we insert a file larger than the max_bytes eviction policy, it should be safely
1360+ // evicted, without deadlocking.
1361+ #[ nativelink_test]
1362+ async fn safe_small_safe_eviction ( ) -> Result < ( ) , Error > {
1363+ let store_spec = FilesystemSpec {
1364+ content_path : "/tmp/nativelink/safe_fs" . into ( ) ,
1365+ temp_path : "/tmp/nativelink/safe_fs_temp" . into ( ) ,
1366+ eviction_policy : Some ( EvictionPolicy {
1367+ max_bytes : 1 ,
1368+ ..Default :: default ( )
1369+ } ) ,
1370+ ..Default :: default ( )
1371+ } ;
1372+ let store = Store :: new ( <FilesystemStore >:: new ( & store_spec) . await ?) ;
1373+
1374+ // > than the max_bytes
1375+ let bytes = 2 ;
1376+
1377+ let data = make_random_data ( bytes) ;
1378+ let digest = DigestInfo :: try_new ( VALID_HASH , data. len ( ) ) . unwrap ( ) ;
1379+
1380+ assert_eq ! (
1381+ store. has( digest) . await ,
1382+ Ok ( None ) ,
1383+ "Expected data to not exist in store"
1384+ ) ;
1385+
1386+ store. update_oneshot ( digest, data. clone ( ) . into ( ) ) . await ?;
1387+
1388+ assert_eq ! (
1389+ store. has( digest) . await ,
1390+ Ok ( None ) ,
1391+ "Expected data to not exist in store, because eviction"
1392+ ) ;
1393+
1394+ let ( tx, mut rx) = make_buf_channel_pair ( ) ;
1395+
1396+ assert_eq ! (
1397+ store. get( digest, tx) . await ,
1398+ Err ( Error {
1399+ code: Code :: NotFound ,
1400+ messages: vec![ format!(
1401+ "{VALID_HASH}-{bytes} not found in filesystem store here"
1402+ ) ] ,
1403+ } ) ,
1404+ "Expected data to not exist in store, because eviction"
1405+ ) ;
1406+
1407+ assert ! ( rx. recv( ) . await . is_err( ) ) ;
1408+
1409+ Ok ( ( ) )
1410+ }
0 commit comments