55#![ allow( clippy:: cast_possible_wrap) ]
66
77use crate :: generator;
8+ use crate :: generator:: common:: { RateSpec , ThrottleConversionError , ThrottleMode } ;
89use fuser:: {
910 BackgroundSession , FileAttr , Filesystem , MountOption , ReplyAttr , ReplyData , ReplyDirectory ,
1011 ReplyEntry , Request , spawn_mount2,
@@ -55,41 +56,74 @@ pub struct Config {
5556 maximum_block_size : byte_unit:: Byte ,
5657 /// The mount-point for this filesystem
5758 mount_point : PathBuf ,
58- /// The load profile, controlling bytes per second as a function of time.
59+ /// The load profile, controlling bytes or blocks per second as a function of time.
5960 load_profile : LoadProfile ,
6061}
6162
6263/// Profile for load in this filesystem.
6364#[ derive( Debug , Serialize , Deserialize , Clone , Copy , PartialEq ) ]
6465#[ serde( rename_all = "snake_case" ) ]
6566pub enum LoadProfile {
66- /// Constant bytes per second
67- Constant ( byte_unit :: Byte ) ,
68- /// Linear growth of bytes per second
67+ /// Constant rate ( bytes or blocks per second).
68+ Constant ( RateSpec ) ,
69+ /// Linear growth of rate ( bytes or blocks per second).
6970 Linear {
70- /// Starting point for bytes per second
71- initial_bytes_per_second : byte_unit:: Byte ,
72- /// Amount to increase per second
73- rate : byte_unit:: Byte ,
71+ /// Starting point for the rate.
72+ #[ serde( alias = "initial_bytes_per_second" ) ]
73+ initial : RateSpec ,
74+ /// Amount to increase per second.
75+ rate : RateSpec ,
7476 } ,
7577}
7678
7779impl LoadProfile {
78- fn to_model ( self ) -> model:: LoadProfile {
80+ fn to_model ( self ) -> Result < model:: LoadProfile , ThrottleConversionError > {
7981 // For now, one tick is one second.
8082 match self {
81- LoadProfile :: Constant ( bpt) => model:: LoadProfile :: Constant ( bpt. as_u128 ( ) as u64 ) ,
82- LoadProfile :: Linear {
83- initial_bytes_per_second,
84- rate,
85- } => model:: LoadProfile :: Linear {
86- start : initial_bytes_per_second. as_u128 ( ) as u64 ,
87- rate : rate. as_u128 ( ) as u64 ,
88- } ,
83+ LoadProfile :: Constant ( rate) => {
84+ let ( mode, cap) = resolve_rate ( & rate) ?;
85+ match mode {
86+ ThrottleMode :: Bytes => Ok ( model:: LoadProfile :: Constant ( u64:: from ( cap. get ( ) ) ) ) ,
87+ ThrottleMode :: Blocks => Ok ( model:: LoadProfile :: Blocks {
88+ blocks_per_tick : u64:: from ( cap. get ( ) ) ,
89+ } ) ,
90+ }
91+ }
92+ LoadProfile :: Linear { initial, rate } => {
93+ let ( m1, init) = resolve_rate ( & initial) ?;
94+ let ( m2, rate) = resolve_rate ( & rate) ?;
95+ if m1 != m2 {
96+ return Err ( ThrottleConversionError :: MixedModes ) ;
97+ }
98+ match m1 {
99+ ThrottleMode :: Bytes => Ok ( model:: LoadProfile :: Linear {
100+ start : u64:: from ( init. get ( ) ) ,
101+ rate : u64:: from ( rate. get ( ) ) ,
102+ } ) ,
103+ ThrottleMode :: Blocks => Ok ( model:: LoadProfile :: BlocksLinear {
104+ start : u64:: from ( init. get ( ) ) ,
105+ rate : u64:: from ( rate. get ( ) ) ,
106+ } ) ,
107+ }
108+ }
89109 }
90110 }
91111}
92112
113+ fn resolve_rate ( rate : & RateSpec ) -> Result < ( ThrottleMode , NonZeroU32 ) , ThrottleConversionError > {
114+ match rate {
115+ RateSpec :: Bytes { bytes_per_second } => {
116+ let val = bytes_per_second. as_u128 ( ) ;
117+ let val = u32:: try_from ( val)
118+ . map_err ( |_| ThrottleConversionError :: ValueTooLarge ( * bytes_per_second) ) ?;
119+ NonZeroU32 :: new ( val)
120+ . map ( |n| ( ThrottleMode :: Bytes , n) )
121+ . ok_or ( ThrottleConversionError :: Zero )
122+ }
123+ RateSpec :: Blocks { blocks_per_second } => Ok ( ( ThrottleMode :: Blocks , * blocks_per_second) ) ,
124+ }
125+ }
126+
93127#[ derive( thiserror:: Error , Debug ) ]
94128/// Error for `LogrotateFs`
95129pub enum Error {
@@ -99,6 +133,9 @@ pub enum Error {
99133 /// Creation of payload blocks failed.
100134 #[ error( "Block creation error: {0}" ) ]
101135 Block ( #[ from] block:: Error ) ,
136+ /// Throttle conversion error
137+ #[ error( "Throttle configuration error: {0}" ) ]
138+ ThrottleConversion ( #[ from] ThrottleConversionError ) ,
102139 /// Failed to convert, value is 0
103140 #[ error( "Value provided must not be zero" ) ]
104141 Zero ,
@@ -154,10 +191,18 @@ impl Server {
154191 // divvy this up in the future.
155192 total_bytes. get ( ) as usize ,
156193 ) ?;
194+ let load_profile = config. load_profile . to_model ( ) ?;
157195
158196 let start_time = Instant :: now ( ) ;
159197 let start_time_system = SystemTime :: now ( ) ;
160198
199+ let block_cache_size = block_cache. total_size ( ) ;
200+ info ! (
201+ "LogrotateFS block cache initialized: requested={}, actual={} bytes, blocks={}" ,
202+ config. maximum_prebuild_cache_size_bytes,
203+ block_cache_size,
204+ block_cache. len( )
205+ ) ;
161206 let state = model:: State :: new (
162207 & mut rng,
163208 start_time. elapsed ( ) . as_secs ( ) ,
@@ -166,7 +211,7 @@ impl Server {
166211 block_cache,
167212 config. max_depth ,
168213 config. concurrent_logs ,
169- config . load_profile . to_model ( ) ,
214+ load_profile,
170215 ) ;
171216
172217 info ! (
@@ -481,6 +526,7 @@ impl Filesystem for LogrotateFS {
481526#[ cfg( test) ]
482527mod tests {
483528 use super :: LoadProfile ;
529+ use crate :: generator:: common:: RateSpec ;
484530 use serde:: Deserialize ;
485531 use serde_yaml:: with:: singleton_map_recursive;
486532
@@ -504,8 +550,28 @@ mod tests {
504550 "# ;
505551 let w = parse_wrapper ( yaml) ;
506552 assert ! ( matches!( w. load_profile, LoadProfile :: Constant ( ..) ) ) ;
507- if let LoadProfile :: Constant ( bytes) = w. load_profile {
508- assert_eq ! ( bytes. as_u64( ) , 5 * 1024 * 1024 ) ;
553+ if let LoadProfile :: Constant ( rate) = w. load_profile {
554+ assert ! ( matches!( rate, RateSpec :: Bytes { .. } ) ) ;
555+ if let RateSpec :: Bytes { bytes_per_second } = rate {
556+ assert_eq ! ( bytes_per_second. as_u64( ) , 5 * 1024 * 1024 ) ;
557+ }
558+ }
559+ }
560+
561+ #[ test]
562+ fn load_profile_constant_blocks_per_second ( ) {
563+ let yaml = r#"
564+ load_profile:
565+ constant:
566+ blocks_per_second: 100
567+ "# ;
568+ let w = parse_wrapper ( yaml) ;
569+ assert ! ( matches!( w. load_profile, LoadProfile :: Constant ( _) ) ) ;
570+ if let LoadProfile :: Constant ( rate) = w. load_profile {
571+ assert ! ( matches!( rate, RateSpec :: Blocks { .. } ) ) ;
572+ if let RateSpec :: Blocks { blocks_per_second } = rate {
573+ assert_eq ! ( blocks_per_second. get( ) , 100 ) ;
574+ }
509575 }
510576 }
511577
@@ -520,13 +586,61 @@ mod tests {
520586
521587 let w = parse_wrapper ( yaml) ;
522588 assert ! ( matches!( w. load_profile, LoadProfile :: Linear { .. } ) ) ;
523- if let LoadProfile :: Linear {
524- initial_bytes_per_second,
525- rate,
526- } = w. load_profile
527- {
528- assert_eq ! ( initial_bytes_per_second. as_u64( ) , 10 * 1024 * 1024 ) ;
529- assert_eq ! ( rate. as_u64( ) , 1 * 1024 * 1024 ) ;
589+ if let LoadProfile :: Linear { initial, rate } = w. load_profile {
590+ assert ! ( matches!( initial, RateSpec :: Bytes { .. } ) ) ;
591+ if let RateSpec :: Bytes { bytes_per_second } = initial {
592+ assert_eq ! ( bytes_per_second. as_u64( ) , 10 * 1024 * 1024 ) ;
593+ }
594+ assert ! ( matches!( rate, RateSpec :: Bytes { .. } ) ) ;
595+ if let RateSpec :: Bytes { bytes_per_second } = rate {
596+ assert_eq ! ( bytes_per_second. as_u64( ) , 1 * 1024 * 1024 ) ;
597+ }
598+ }
599+ }
600+
601+ #[ test]
602+ fn load_profile_linear_new_format_flattened_bytes ( ) {
603+ let yaml = r#"
604+ load_profile:
605+ linear:
606+ initial: "1 MiB"
607+ rate: "100 KiB"
608+ "# ;
609+ let w = parse_wrapper ( yaml) ;
610+ assert ! ( matches!( w. load_profile, LoadProfile :: Linear { .. } ) ) ;
611+ if let LoadProfile :: Linear { initial, rate } = w. load_profile {
612+ assert ! ( matches!( initial, RateSpec :: Bytes { .. } ) ) ;
613+ if let RateSpec :: Bytes { bytes_per_second } = initial {
614+ assert_eq ! ( bytes_per_second. as_u64( ) , 1 * 1024 * 1024 ) ;
615+ }
616+ assert ! ( matches!( rate, RateSpec :: Bytes { .. } ) ) ;
617+ if let RateSpec :: Bytes { bytes_per_second } = rate {
618+ assert_eq ! ( bytes_per_second. as_u64( ) , 100 * 1024 ) ;
619+ }
620+ }
621+ }
622+
623+ #[ test]
624+ fn load_profile_linear_blocks_per_second ( ) {
625+ let yaml = r#"
626+ load_profile:
627+ linear:
628+ initial:
629+ blocks_per_second: 100
630+ rate:
631+ blocks_per_second: 10
632+ "# ;
633+ let w = parse_wrapper ( yaml) ;
634+ assert ! ( matches!( w. load_profile, LoadProfile :: Linear { .. } ) ) ;
635+ if let LoadProfile :: Linear { initial, rate } = w. load_profile {
636+ assert ! ( matches!( initial, RateSpec :: Blocks { .. } ) ) ;
637+ if let RateSpec :: Blocks { blocks_per_second } = initial {
638+ assert_eq ! ( blocks_per_second. get( ) , 100 ) ;
639+ }
640+ assert ! ( matches!( rate, RateSpec :: Blocks { .. } ) ) ;
641+ if let RateSpec :: Blocks { blocks_per_second } = rate {
642+ assert_eq ! ( blocks_per_second. get( ) , 10 ) ;
643+ }
530644 }
531645 }
532646}
0 commit comments