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
486532 #[ derive( Debug , Deserialize ) ]
@@ -503,8 +549,28 @@ mod tests {
503549 "# ;
504550 let w = parse_wrapper ( yaml) ;
505551 assert ! ( matches!( w. load_profile, LoadProfile :: Constant ( ..) ) ) ;
506- if let LoadProfile :: Constant ( bytes) = w. load_profile {
507- assert_eq ! ( bytes. as_u64( ) , 5 * 1024 * 1024 ) ;
552+ if let LoadProfile :: Constant ( rate) = w. load_profile {
553+ assert ! ( matches!( rate, RateSpec :: Bytes { .. } ) ) ;
554+ if let RateSpec :: Bytes { bytes_per_second } = rate {
555+ assert_eq ! ( bytes_per_second. as_u64( ) , 5 * 1024 * 1024 ) ;
556+ }
557+ }
558+ }
559+
560+ #[ test]
561+ fn load_profile_constant_blocks_per_second ( ) {
562+ let yaml = r#"
563+ load_profile:
564+ constant:
565+ blocks_per_second: 100
566+ "# ;
567+ let w = parse_wrapper ( yaml) ;
568+ assert ! ( matches!( w. load_profile, LoadProfile :: Constant ( _) ) ) ;
569+ if let LoadProfile :: Constant ( rate) = w. load_profile {
570+ assert ! ( matches!( rate, RateSpec :: Blocks { .. } ) ) ;
571+ if let RateSpec :: Blocks { blocks_per_second } = rate {
572+ assert_eq ! ( blocks_per_second. get( ) , 100 ) ;
573+ }
508574 }
509575 }
510576
@@ -519,13 +585,61 @@ mod tests {
519585
520586 let w = parse_wrapper ( yaml) ;
521587 assert ! ( matches!( w. load_profile, LoadProfile :: Linear { .. } ) ) ;
522- if let LoadProfile :: Linear {
523- initial_bytes_per_second,
524- rate,
525- } = w. load_profile
526- {
527- assert_eq ! ( initial_bytes_per_second. as_u64( ) , 10 * 1024 * 1024 ) ;
528- assert_eq ! ( rate. as_u64( ) , 1 * 1024 * 1024 ) ;
588+ if let LoadProfile :: Linear { initial, rate } = w. load_profile {
589+ assert ! ( matches!( initial, RateSpec :: Bytes { .. } ) ) ;
590+ if let RateSpec :: Bytes { bytes_per_second } = initial {
591+ assert_eq ! ( bytes_per_second. as_u64( ) , 10 * 1024 * 1024 ) ;
592+ }
593+ assert ! ( matches!( rate, RateSpec :: Bytes { .. } ) ) ;
594+ if let RateSpec :: Bytes { bytes_per_second } = rate {
595+ assert_eq ! ( bytes_per_second. as_u64( ) , 1 * 1024 * 1024 ) ;
596+ }
597+ }
598+ }
599+
600+ #[ test]
601+ fn load_profile_linear_new_format_flattened_bytes ( ) {
602+ let yaml = r#"
603+ load_profile:
604+ linear:
605+ initial: "1 MiB"
606+ rate: "100 KiB"
607+ "# ;
608+ let w = parse_wrapper ( yaml) ;
609+ assert ! ( matches!( w. load_profile, LoadProfile :: Linear { .. } ) ) ;
610+ if let LoadProfile :: Linear { initial, rate } = w. load_profile {
611+ assert ! ( matches!( initial, RateSpec :: Bytes { .. } ) ) ;
612+ if let RateSpec :: Bytes { bytes_per_second } = initial {
613+ assert_eq ! ( bytes_per_second. as_u64( ) , 1 * 1024 * 1024 ) ;
614+ }
615+ assert ! ( matches!( rate, RateSpec :: Bytes { .. } ) ) ;
616+ if let RateSpec :: Bytes { bytes_per_second } = rate {
617+ assert_eq ! ( bytes_per_second. as_u64( ) , 100 * 1024 ) ;
618+ }
619+ }
620+ }
621+
622+ #[ test]
623+ fn load_profile_linear_blocks_per_second ( ) {
624+ let yaml = r#"
625+ load_profile:
626+ linear:
627+ initial:
628+ blocks_per_second: 100
629+ rate:
630+ blocks_per_second: 10
631+ "# ;
632+ let w = parse_wrapper ( yaml) ;
633+ assert ! ( matches!( w. load_profile, LoadProfile :: Linear { .. } ) ) ;
634+ if let LoadProfile :: Linear { initial, rate } = w. load_profile {
635+ assert ! ( matches!( initial, RateSpec :: Blocks { .. } ) ) ;
636+ if let RateSpec :: Blocks { blocks_per_second } = initial {
637+ assert_eq ! ( blocks_per_second. get( ) , 100 ) ;
638+ }
639+ assert ! ( matches!( rate, RateSpec :: Blocks { .. } ) ) ;
640+ if let RateSpec :: Blocks { blocks_per_second } = rate {
641+ assert_eq ! ( blocks_per_second. get( ) , 10 ) ;
642+ }
529643 }
530644 }
531645}
0 commit comments