@@ -13,6 +13,7 @@ use tokio::{
1313 sync:: broadcast:: Receiver ,
1414 time:: { self , Duration , timeout} ,
1515} ;
16+ use tokio_stream:: wrappers:: ReadDirStream ;
1617use tracing:: instrument;
1718
1819#[ tokio:: test( flavor = "multi_thread" ) ]
@@ -1190,16 +1191,13 @@ async fn aux_db_files_are_deleted_on_close() {
11901191 test_utils:: init_log ( ) ;
11911192
11921193 let ( temp_dir, repo) = setup ( ) . await ;
1193-
11941194 repo. close ( ) . await . unwrap ( ) ;
11951195
1196- let mut read_dir = fs:: read_dir ( temp_dir. path ( ) ) . await . unwrap ( ) ;
1197- let mut entries = Vec :: new ( ) ;
1198-
1199- while let Some ( entry) = read_dir. next_entry ( ) . await . unwrap ( ) {
1200- entries. push ( entry. path ( ) ) ;
1201- }
1202-
1196+ let entries: Vec < _ > = ReadDirStream :: new ( fs:: read_dir ( temp_dir. path ( ) ) . await . unwrap ( ) )
1197+ . map_ok ( |entry| entry. path ( ) )
1198+ . try_collect ( )
1199+ . await
1200+ . unwrap ( ) ;
12031201 assert_eq ! ( entries, [ temp_dir. path( ) . join( DEFAULT_REPO_NAME ) ] ) ;
12041202}
12051203
@@ -1255,6 +1253,63 @@ async fn export() {
12551253 assert_eq ! ( dst_repo. access_mode( ) , AccessMode :: Read ) ;
12561254}
12571255
1256+ #[ tokio:: test]
1257+ async fn repository_size_decreases_after_delete ( ) {
1258+ let ( _base_dir, repo_path) = {
1259+ let ( base_dir, repo) = setup ( ) . await ;
1260+ let repo_path = base_dir. path ( ) . join ( DEFAULT_REPO_NAME ) ;
1261+ repo. close ( ) . await . unwrap ( ) ;
1262+ ( base_dir, repo_path)
1263+ } ;
1264+ let file_size: u64 = 1024 * 1024 ;
1265+
1266+ let repo_size_initial = fs:: metadata ( & repo_path) . await . unwrap ( ) . len ( ) ;
1267+ tracing:: info!( ?repo_size_initial) ;
1268+
1269+ {
1270+ let repo = Repository :: open ( & RepositoryParams :: new ( & repo_path) , None , AccessMode :: Write )
1271+ . await
1272+ . unwrap ( ) ;
1273+ let mut file = repo. create_file ( "data" ) . await . unwrap ( ) ;
1274+ write_random_data ( & mut file, file_size as usize ) . await ;
1275+ file. flush ( ) . await . unwrap ( ) ;
1276+ repo. close ( ) . await . unwrap ( ) ;
1277+ }
1278+
1279+ ensure_aux_db_files_are_deleted ( & repo_path) . await ;
1280+
1281+ let repo_size_after_create = fs:: metadata ( & repo_path) . await . unwrap ( ) . len ( ) ;
1282+ tracing:: info!( ?repo_size_after_create) ;
1283+ assert ! (
1284+ repo_size_after_create >= repo_size_initial + file_size,
1285+ "actual size: {}, expected min size: {}" ,
1286+ repo_size_after_create,
1287+ repo_size_initial + file_size
1288+ ) ;
1289+
1290+ {
1291+ let repo = Repository :: open ( & RepositoryParams :: new ( & repo_path) , None , AccessMode :: Write )
1292+ . await
1293+ . unwrap ( ) ;
1294+ repo. remove_entry ( "data" ) . await . unwrap ( ) ;
1295+
1296+ wait_for ( & repo, async || repo. count_blocks ( ) . await . unwrap ( ) <= 1 ) . await ;
1297+
1298+ repo. close ( ) . await . unwrap ( ) ;
1299+ }
1300+
1301+ ensure_aux_db_files_are_deleted ( & repo_path) . await ;
1302+
1303+ let repo_size_after_delete = fs:: metadata ( & repo_path) . await . unwrap ( ) . len ( ) ;
1304+ tracing:: info!( ?repo_size_after_delete) ;
1305+ assert ! (
1306+ repo_size_after_delete <= repo_size_after_create - file_size,
1307+ "actual size: {}, expected max size: {}" ,
1308+ repo_size_after_delete,
1309+ repo_size_after_create - file_size
1310+ ) ;
1311+ }
1312+
12581313const DEFAULT_REPO_NAME : & str = "repo.db" ;
12591314
12601315async fn setup ( ) -> ( TempDir , Repository ) {
@@ -1311,6 +1366,19 @@ fn random_bytes(size: usize) -> Vec<u8> {
13111366 buffer
13121367}
13131368
1369+ async fn write_random_data ( file : & mut File , size : usize ) {
1370+ let mut buffer = [ 0u8 ; 4 * 1024 ] ;
1371+ let mut remaining = size;
1372+ let mut rng = rand:: thread_rng ( ) ;
1373+
1374+ while remaining > 0 {
1375+ let chunk_size = buffer. len ( ) . min ( remaining) ;
1376+ rng. fill ( & mut buffer[ ..chunk_size] ) ;
1377+ file. write_all ( & buffer) . await . unwrap ( ) ;
1378+ remaining -= chunk_size;
1379+ }
1380+ }
1381+
13141382async fn wait_for_notification ( rx : & mut Receiver < Event > ) {
13151383 match timeout ( Duration :: from_secs ( 5 ) , rx. recv ( ) ) . await {
13161384 Ok ( Ok ( _) ) => ( ) ,
@@ -1341,3 +1409,31 @@ where
13411409 . await
13421410 . expect ( "timeout waiting for condition" )
13431411}
1412+
1413+ // HACK: Due to a [bug in sqlx][1], the db aux files are not always deleted after a repository is
1414+ // closed. As a workaround, this function reopens and closes the repository until the aux files are
1415+ // gone.
1416+ //
1417+ // [1]: https://github.com/launchbadge/sqlx/issues/3217
1418+ async fn ensure_aux_db_files_are_deleted ( repo_path : & Path ) {
1419+ let expected_files = [ repo_path. to_owned ( ) ] ;
1420+
1421+ loop {
1422+ if ReadDirStream :: new ( fs:: read_dir ( repo_path. parent ( ) . unwrap ( ) ) . await . unwrap ( ) )
1423+ . map_ok ( |entry| entry. path ( ) )
1424+ . try_collect :: < Vec < _ > > ( )
1425+ . await
1426+ . unwrap ( )
1427+ == expected_files
1428+ {
1429+ break ;
1430+ }
1431+
1432+ Repository :: open ( & RepositoryParams :: new ( repo_path) , None , AccessMode :: Blind )
1433+ . await
1434+ . unwrap ( )
1435+ . close ( )
1436+ . await
1437+ . unwrap ( ) ;
1438+ }
1439+ }
0 commit comments