@@ -5,7 +5,7 @@ use crate::uuid_utils::decode_unix_timestamp;
5
5
use crate :: wal:: WalFileReader ;
6
6
use anyhow:: { anyhow, bail} ;
7
7
use arc_swap:: ArcSwapOption ;
8
- use async_compression:: tokio:: write:: GzipEncoder ;
8
+ use async_compression:: tokio:: write:: { GzipEncoder , XzEncoder } ;
9
9
use aws_sdk_s3:: config:: { Credentials , Region } ;
10
10
use aws_sdk_s3:: error:: SdkError ;
11
11
use aws_sdk_s3:: operation:: get_object:: builders:: GetObjectFluentBuilder ;
@@ -653,7 +653,7 @@ impl Replicator {
653
653
CompressionKind :: None => Ok ( ByteStream :: from_path ( db_path) . await ?) ,
654
654
CompressionKind :: Gzip => {
655
655
let mut reader = File :: open ( db_path) . await ?;
656
- let gzip_path = Self :: db_gzip_path ( db_path) ;
656
+ let gzip_path = Self :: db_compressed_path ( db_path, "gz" ) ;
657
657
let compressed_file = OpenOptions :: new ( )
658
658
. create ( true )
659
659
. write ( true )
@@ -671,13 +671,34 @@ impl Replicator {
671
671
) ;
672
672
Ok ( ByteStream :: from_path ( gzip_path) . await ?)
673
673
}
674
+ CompressionKind :: Xz => {
675
+ let mut reader = File :: open ( db_path) . await ?;
676
+ let gzip_path = Self :: db_compressed_path ( db_path, "xz" ) ;
677
+ let compressed_file = OpenOptions :: new ( )
678
+ . create ( true )
679
+ . write ( true )
680
+ . read ( true )
681
+ . truncate ( true )
682
+ . open ( & gzip_path)
683
+ . await ?;
684
+ let mut writer =
685
+ XzEncoder :: with_quality ( compressed_file, async_compression:: Level :: Best ) ;
686
+ let size = tokio:: io:: copy ( & mut reader, & mut writer) . await ?;
687
+ writer. shutdown ( ) . await ?;
688
+ tracing:: debug!(
689
+ "Compressed database file ({} bytes) into `{}`" ,
690
+ size,
691
+ gzip_path. display( )
692
+ ) ;
693
+ Ok ( ByteStream :: from_path ( gzip_path) . await ?)
694
+ }
674
695
}
675
696
}
676
697
677
- fn db_gzip_path ( db_path : & Path ) -> PathBuf {
678
- let mut gzip_path = db_path. to_path_buf ( ) ;
679
- gzip_path . pop ( ) ;
680
- gzip_path . join ( "db.gz" )
698
+ fn db_compressed_path ( db_path : & Path , suffix : & ' static str ) -> PathBuf {
699
+ let mut compressed_path : PathBuf = db_path. to_path_buf ( ) ;
700
+ compressed_path . pop ( ) ;
701
+ compressed_path . join ( format ! ( "db.{suffix}" ) )
681
702
}
682
703
683
704
fn restore_db_path ( & self ) -> PathBuf {
@@ -816,9 +837,10 @@ impl Replicator {
816
837
let _ = snapshot_notifier. send ( Ok ( Some ( generation) ) ) ;
817
838
let elapsed = Instant :: now ( ) - start;
818
839
tracing:: debug!( "Snapshot upload finished (took {:?})" , elapsed) ;
819
- // cleanup gzip database snapshot if exists
820
- let gzip_path = Self :: db_gzip_path ( & db_path) ;
821
- let _ = tokio:: fs:: remove_file ( gzip_path) . await ;
840
+ // cleanup gzip/xz database snapshot if exists
841
+ for suffix in & [ "gz" , "xz" ] {
842
+ let _ = tokio:: fs:: remove_file ( Self :: db_compressed_path ( & db_path, suffix) ) . await ;
843
+ }
822
844
} ) ;
823
845
let elapsed = Instant :: now ( ) - start_ts;
824
846
tracing:: debug!( "Scheduled DB snapshot {} (took {:?})" , generation, elapsed) ;
@@ -1163,6 +1185,7 @@ impl Replicator {
1163
1185
let main_db_path = match self . use_compression {
1164
1186
CompressionKind :: None => format ! ( "{}-{}/db.db" , self . db_name, generation) ,
1165
1187
CompressionKind :: Gzip => format ! ( "{}-{}/db.gz" , self . db_name, generation) ,
1188
+ CompressionKind :: Xz => format ! ( "{}-{}/db.xz" , self . db_name, generation) ,
1166
1189
} ;
1167
1190
1168
1191
if let Ok ( db_file) = self . get_object ( main_db_path) . send ( ) . await {
@@ -1175,6 +1198,12 @@ impl Replicator {
1175
1198
) ;
1176
1199
tokio:: io:: copy ( & mut decompress_reader, db) . await ?
1177
1200
}
1201
+ CompressionKind :: Xz => {
1202
+ let mut decompress_reader = async_compression:: tokio:: bufread:: XzDecoder :: new (
1203
+ tokio:: io:: BufReader :: new ( body_reader) ,
1204
+ ) ;
1205
+ tokio:: io:: copy ( & mut decompress_reader, db) . await ?
1206
+ }
1178
1207
} ;
1179
1208
db. flush ( ) . await ?;
1180
1209
@@ -1235,6 +1264,7 @@ impl Replicator {
1235
1264
Some ( result) => result,
1236
1265
None => {
1237
1266
if !key. ends_with ( ".gz" )
1267
+ && !key. ends_with ( ".xz" )
1238
1268
&& !key. ends_with ( ".db" )
1239
1269
&& !key. ends_with ( ".meta" )
1240
1270
&& !key. ends_with ( ".dep" )
@@ -1423,6 +1453,7 @@ impl Replicator {
1423
1453
let str = fpath. to_str ( ) ?;
1424
1454
if str. ends_with ( ".db" )
1425
1455
| str. ends_with ( ".gz" )
1456
+ | str. ends_with ( ".xz" )
1426
1457
| str. ends_with ( ".raw" )
1427
1458
| str. ends_with ( ".meta" )
1428
1459
| str. ends_with ( ".dep" )
@@ -1670,13 +1701,15 @@ pub enum CompressionKind {
1670
1701
#[ default]
1671
1702
None ,
1672
1703
Gzip ,
1704
+ Xz ,
1673
1705
}
1674
1706
1675
1707
impl CompressionKind {
1676
1708
pub fn parse ( kind : & str ) -> std:: result:: Result < Self , & str > {
1677
1709
match kind {
1678
1710
"gz" | "gzip" => Ok ( CompressionKind :: Gzip ) ,
1679
1711
"raw" | "" => Ok ( CompressionKind :: None ) ,
1712
+ "xz" => Ok ( CompressionKind :: Xz ) ,
1680
1713
other => Err ( other) ,
1681
1714
}
1682
1715
}
@@ -1687,6 +1720,7 @@ impl std::fmt::Display for CompressionKind {
1687
1720
match self {
1688
1721
CompressionKind :: None => write ! ( f, "raw" ) ,
1689
1722
CompressionKind :: Gzip => write ! ( f, "gz" ) ,
1723
+ CompressionKind :: Xz => write ! ( f, "xz" ) ,
1690
1724
}
1691
1725
}
1692
1726
}
0 commit comments