@@ -38,13 +38,13 @@ impl BackgroundJob for DumpDb {
38
38
directory. populate ( & database_url) ?;
39
39
40
40
info ! ( path = ?directory. export_dir, "Creating tarball" ) ;
41
- DumpTarball :: create ( & directory. export_dir )
41
+ create_tarball ( & directory. export_dir )
42
42
} )
43
43
. await ?;
44
44
45
45
info ! ( "Uploading tarball" ) ;
46
46
env. storage
47
- . upload_db_dump ( target_name, & tarball. tarball_path )
47
+ . upload_db_dump ( target_name, tarball. path ( ) )
48
48
. await ?;
49
49
info ! ( "Database dump tarball uploaded" ) ;
50
50
@@ -71,15 +71,23 @@ impl BackgroundJob for DumpDb {
71
71
/// make sure it gets deleted again even in the case of an error.
72
72
#[ derive( Debug ) ]
73
73
pub struct DumpDirectory {
74
+ /// The temporary directory that contains the export directory. This is
75
+ /// allowing `dead_code` since we're only relying on the `Drop`
76
+ /// implementation to clean up the directory.
77
+ #[ allow( dead_code) ]
78
+ tempdir : tempfile:: TempDir ,
79
+
74
80
pub timestamp : chrono:: DateTime < chrono:: Utc > ,
75
81
pub export_dir : PathBuf ,
76
82
}
77
83
78
84
impl DumpDirectory {
79
85
pub fn create ( ) -> anyhow:: Result < Self > {
86
+ let tempdir = tempfile:: tempdir ( ) ?;
87
+
80
88
let timestamp = chrono:: Utc :: now ( ) ;
81
89
let timestamp_str = timestamp. format ( "%Y-%m-%d-%H%M%S" ) . to_string ( ) ;
82
- let export_dir = std :: env :: temp_dir ( ) . join ( "dump-db" ) . join ( timestamp_str) ;
90
+ let export_dir = tempdir . path ( ) . join ( timestamp_str) ;
83
91
84
92
debug ! ( ?export_dir, "Creating database dump folder…" ) ;
85
93
fs:: create_dir_all ( & export_dir) . with_context ( || {
@@ -90,6 +98,7 @@ impl DumpDirectory {
90
98
} ) ?;
91
99
92
100
Ok ( Self {
101
+ tempdir,
93
102
timestamp,
94
103
export_dir,
95
104
} )
@@ -179,12 +188,6 @@ impl DumpDirectory {
179
188
}
180
189
}
181
190
182
- impl Drop for DumpDirectory {
183
- fn drop ( & mut self ) {
184
- std:: fs:: remove_dir_all ( & self . export_dir ) . unwrap ( ) ;
185
- }
186
- }
187
-
188
191
pub fn run_psql ( script : & Path , database_url : & str ) -> anyhow:: Result < ( ) > {
189
192
debug ! ( ?script, "Running psql script…" ) ;
190
193
let psql_script =
@@ -213,73 +216,56 @@ pub fn run_psql(script: &Path, database_url: &str) -> anyhow::Result<()> {
213
216
Ok ( ( ) )
214
217
}
215
218
216
- /// Manage the tarball of the database dump.
217
- ///
218
- /// Create the tarball, upload it to S3, and make sure it gets deleted.
219
- struct DumpTarball {
220
- tarball_path : PathBuf ,
221
- }
219
+ fn create_tarball ( export_dir : & Path ) -> anyhow:: Result < tempfile:: NamedTempFile > {
220
+ debug ! ( "Creating tarball file" ) ;
221
+ let tempfile = tempfile:: NamedTempFile :: new ( ) ?;
222
+ let encoder = flate2:: write:: GzEncoder :: new ( tempfile. as_file ( ) , flate2:: Compression :: default ( ) ) ;
222
223
223
- impl DumpTarball {
224
- fn create ( export_dir : & Path ) -> anyhow:: Result < Self > {
225
- let tarball_path = export_dir. with_extension ( "tar.gz" ) ;
224
+ let mut archive = tar:: Builder :: new ( encoder) ;
226
225
227
- debug ! ( path = ?tarball_path, "Creating tarball file" ) ;
228
- let tarfile = File :: create ( & tarball_path) ?;
226
+ let tar_top_dir = PathBuf :: from ( export_dir. file_name ( ) . unwrap ( ) ) ;
227
+ debug ! ( path = ?tar_top_dir, "Appending directory to tarball" ) ;
228
+ archive. append_dir ( & tar_top_dir, export_dir) ?;
229
229
230
- let result = Self { tarball_path } ;
231
- let encoder = flate2:: write:: GzEncoder :: new ( tarfile, flate2:: Compression :: default ( ) ) ;
230
+ // Append readme, metadata, schemas.
231
+ let mut paths = Vec :: new ( ) ;
232
+ for entry in fs:: read_dir ( export_dir) ? {
233
+ let entry = entry?;
234
+ let file_type = entry. file_type ( ) ?;
235
+ if file_type. is_file ( ) {
236
+ paths. push ( ( entry. path ( ) , entry. file_name ( ) ) ) ;
237
+ }
238
+ }
239
+ // Sort paths to make the tarball deterministic.
240
+ paths. sort ( ) ;
241
+ for ( path, file_name) in paths {
242
+ let name_in_tar = tar_top_dir. join ( file_name) ;
243
+ debug ! ( name = ?name_in_tar, "Appending file to tarball" ) ;
244
+ archive. append_path_with_name ( path, name_in_tar) ?;
245
+ }
232
246
233
- let mut archive = tar:: Builder :: new ( encoder) ;
247
+ // Append topologically sorted tables to make it possible to pipeline
248
+ // importing with gz extraction.
234
249
235
- let tar_top_dir = PathBuf :: from ( export_dir . file_name ( ) . unwrap ( ) ) ;
236
- debug ! ( path = ?tar_top_dir , "Appending directory to tarball" ) ;
237
- archive . append_dir ( & tar_top_dir , export_dir ) ? ;
250
+ debug ! ( "Sorting database tables" ) ;
251
+ let visibility_config = VisibilityConfig :: get ( ) ;
252
+ let sorted_tables = visibility_config . topological_sort ( ) ;
238
253
239
- // Append readme, metadata, schemas.
240
- let mut paths = Vec :: new ( ) ;
241
- for entry in fs:: read_dir ( export_dir) ? {
242
- let entry = entry?;
243
- let file_type = entry. file_type ( ) ?;
244
- if file_type. is_file ( ) {
245
- paths. push ( ( entry. path ( ) , entry. file_name ( ) ) ) ;
246
- }
247
- }
248
- // Sort paths to make the tarball deterministic.
249
- paths. sort ( ) ;
250
- for ( path, file_name) in paths {
251
- let name_in_tar = tar_top_dir. join ( file_name) ;
254
+ let path = tar_top_dir. join ( "data" ) ;
255
+ debug ! ( ?path, "Appending directory to tarball" ) ;
256
+ archive. append_dir ( path, export_dir. join ( "data" ) ) ?;
257
+ for table in sorted_tables {
258
+ let csv_path = export_dir. join ( "data" ) . join ( table) . with_extension ( "csv" ) ;
259
+ if csv_path. exists ( ) {
260
+ let name_in_tar = tar_top_dir. join ( "data" ) . join ( table) . with_extension ( "csv" ) ;
252
261
debug ! ( name = ?name_in_tar, "Appending file to tarball" ) ;
253
- archive. append_path_with_name ( path, name_in_tar) ?;
254
- }
255
-
256
- // Append topologically sorted tables to make it possible to pipeline
257
- // importing with gz extraction.
258
-
259
- debug ! ( "Sorting database tables" ) ;
260
- let visibility_config = VisibilityConfig :: get ( ) ;
261
- let sorted_tables = visibility_config. topological_sort ( ) ;
262
-
263
- let path = tar_top_dir. join ( "data" ) ;
264
- debug ! ( ?path, "Appending directory to tarball" ) ;
265
- archive. append_dir ( path, export_dir. join ( "data" ) ) ?;
266
- for table in sorted_tables {
267
- let csv_path = export_dir. join ( "data" ) . join ( table) . with_extension ( "csv" ) ;
268
- if csv_path. exists ( ) {
269
- let name_in_tar = tar_top_dir. join ( "data" ) . join ( table) . with_extension ( "csv" ) ;
270
- debug ! ( name = ?name_in_tar, "Appending file to tarball" ) ;
271
- archive. append_path_with_name ( csv_path, name_in_tar) ?;
272
- }
262
+ archive. append_path_with_name ( csv_path, name_in_tar) ?;
273
263
}
274
-
275
- Ok ( result)
276
264
}
277
- }
278
265
279
- impl Drop for DumpTarball {
280
- fn drop ( & mut self ) {
281
- std:: fs:: remove_file ( & self . tarball_path ) . unwrap ( ) ;
282
- }
266
+ drop ( archive) ;
267
+
268
+ Ok ( tempfile)
283
269
}
284
270
285
271
mod configuration;
@@ -307,8 +293,8 @@ mod tests {
307
293
fs:: write ( p. join ( "data" ) . join ( "crate_owners.csv" ) , "" ) . unwrap ( ) ;
308
294
fs:: write ( p. join ( "data" ) . join ( "users.csv" ) , "" ) . unwrap ( ) ;
309
295
310
- let tarball = DumpTarball :: create ( & p) . unwrap ( ) ;
311
- let gz = GzDecoder :: new ( File :: open ( & * tarball. tarball_path ) . unwrap ( ) ) ;
296
+ let tarball = create_tarball ( & p) . unwrap ( ) ;
297
+ let gz = GzDecoder :: new ( File :: open ( tarball. path ( ) ) . unwrap ( ) ) ;
312
298
let mut tar = Archive :: new ( gz) ;
313
299
314
300
let entries = tar. entries ( ) . unwrap ( ) ;
0 commit comments