1
1
//! Model the internal logic of a logrotate filesystem.
2
2
3
- use std:: collections:: { HashMap , HashSet } ;
4
-
5
3
use bytes:: Bytes ;
6
4
use lading_payload:: block;
7
5
use metrics:: counter;
8
6
use rand:: Rng ;
7
+ use rustc_hash:: FxHashMap ;
8
+ use std:: collections:: BTreeSet ;
9
9
10
10
/// Time representation of the model
11
11
pub ( crate ) type Tick = u64 ;
@@ -249,12 +249,21 @@ impl File {
249
249
250
250
/// Model representation of a `Directory`. Contains children are `Directory`
251
251
/// instances or `File` instances. Root directory will not have a `parent`.
252
- #[ derive( Debug ) ]
252
+ #[ derive( Debug , Default ) ]
253
253
pub ( crate ) struct Directory {
254
- children : HashSet < Inode > ,
254
+ children : BTreeSet < Inode > ,
255
255
parent : Option < Inode > ,
256
256
}
257
257
258
+ impl Directory {
259
+ fn new ( parent : Option < Inode > ) -> Self {
260
+ Self {
261
+ children : BTreeSet :: default ( ) ,
262
+ parent,
263
+ }
264
+ }
265
+ }
266
+
258
267
/// A filesystem object, either a `File` or a `Directory`.
259
268
#[ derive( Debug ) ]
260
269
pub ( crate ) enum Node {
@@ -278,7 +287,7 @@ pub(crate) enum Node {
278
287
/// filesystem. It does not contain any bytes, the caller must maintain this
279
288
/// themselves.
280
289
pub ( crate ) struct State {
281
- nodes : HashMap < Inode , Node > ,
290
+ nodes : FxHashMap < Inode , Node > ,
282
291
root_inode : Inode ,
283
292
now : Tick ,
284
293
block_cache : block:: Cache ,
@@ -288,6 +297,7 @@ pub(crate) struct State {
288
297
group_names : Vec < Vec < String > > ,
289
298
next_inode : Inode ,
290
299
next_file_handle : u64 ,
300
+ inode_scratch : Vec < Inode > ,
291
301
}
292
302
293
303
impl std:: fmt:: Debug for State {
@@ -297,6 +307,7 @@ impl std::fmt::Debug for State {
297
307
. field ( "root_inode" , & self . root_inode )
298
308
. field ( "now" , & self . now )
299
309
// intentionally leaving out block_cache
310
+ // intentionally leaving out inode_scratch
300
311
. field ( "max_rotations" , & self . max_rotations )
301
312
. field ( "max_bytes_per_file" , & self . max_bytes_per_file )
302
313
. field ( "group_names" , & self . group_names )
@@ -349,12 +360,9 @@ impl State {
349
360
R : Rng ,
350
361
{
351
362
let root_inode: Inode = 1 ; // `/`
352
- let mut nodes = HashMap :: new ( ) ;
363
+ let mut nodes = FxHashMap :: default ( ) ;
353
364
354
- let root_dir = Directory {
355
- children : HashSet :: new ( ) ,
356
- parent : None ,
357
- } ;
365
+ let root_dir = Directory :: default ( ) ;
358
366
nodes. insert (
359
367
root_inode,
360
368
Node :: Directory {
@@ -373,6 +381,7 @@ impl State {
373
381
group_names : Vec :: new ( ) ,
374
382
next_inode : 2 ,
375
383
next_file_handle : 0 ,
384
+ inode_scratch : Vec :: with_capacity ( concurrent_logs as usize ) ,
376
385
} ;
377
386
378
387
if concurrent_logs == 0 {
@@ -435,10 +444,7 @@ impl State {
435
444
let new_inode = state. next_inode ;
436
445
state. next_inode += 1 ;
437
446
438
- let new_dir = Directory {
439
- children : HashSet :: new ( ) ,
440
- parent : Some ( current_inode) ,
441
- } ;
447
+ let new_dir = Directory :: new ( Some ( current_inode) ) ;
442
448
state. nodes . insert (
443
449
new_inode,
444
450
Node :: Directory {
@@ -536,8 +542,11 @@ impl State {
536
542
fn advance_time_inner ( & mut self , now : Tick ) {
537
543
assert ! ( now >= self . now) ;
538
544
539
- let mut inodes: Vec < Inode > = self . nodes . keys ( ) . copied ( ) . collect ( ) ;
540
- for inode in inodes. drain ( ..) {
545
+ for inode in self . nodes . keys ( ) {
546
+ self . inode_scratch . push ( * inode) ;
547
+ }
548
+
549
+ for inode in self . inode_scratch . drain ( ..) {
541
550
let ( rotated_inode, parent_inode, bytes_per_tick, group_id, ordinal) = {
542
551
// If the node pointed to by inode doesn't exist, that's a
543
552
// catastrophic programming error. We just copied all inode to node
@@ -806,14 +815,15 @@ impl State {
806
815
}
807
816
}
808
817
809
- /// Read inodes from a directory
818
+ /// Read inodes from a directory.
810
819
///
811
820
/// Returns None if the inode is a `File`, else returns the hashset of
812
- /// children inodes.
821
+ /// children inodes. Guaranteed to be in the same order so long as time does
822
+ /// not advance.
813
823
///
814
824
/// Function does not advance time in the model.
815
825
#[ tracing:: instrument( skip( self ) ) ]
816
- pub ( crate ) fn readdir ( & self , inode : Inode ) -> Option < & HashSet < Inode > > {
826
+ pub ( crate ) fn readdir ( & self , inode : Inode ) -> Option < & BTreeSet < Inode > > {
817
827
if let Some ( Node :: Directory { dir, .. } ) = self . nodes . get ( & inode) {
818
828
Some ( & dir. children )
819
829
} else {
0 commit comments