1
1
// Copyright 2023 Oxide Computer Company
2
2
use std:: cmp:: Ordering ;
3
- use std:: collections:: { HashMap , HashSet } ;
3
+ use std:: collections:: { BTreeSet , HashMap , HashSet } ;
4
4
use std:: convert:: TryInto ;
5
5
use std:: fmt:: Debug ;
6
6
use std:: fs:: { rename, File , OpenOptions } ;
@@ -844,7 +844,7 @@ impl Region {
844
844
845
845
// Select extents we're going to flush, while respecting the
846
846
// extent_limit if one was provided.
847
- let dirty_extents: Vec < usize > = match extent_limit {
847
+ let dirty_extents: BTreeSet < usize > = match extent_limit {
848
848
None => self . dirty_extents . iter ( ) . copied ( ) . collect ( ) ,
849
849
Some ( el) => {
850
850
if el > self . def . extent_count ( ) . try_into ( ) . unwrap ( ) {
@@ -859,13 +859,40 @@ impl Region {
859
859
}
860
860
} ;
861
861
862
- // TODO: parallelism?
863
- let mut results = vec ! [ ] ;
864
- for eid in & dirty_extents {
865
- let log = self . log . clone ( ) ;
866
- let extent = self . get_opened_extent_mut ( * eid) ;
867
- results. push ( extent. flush ( flush_number, gen_number, job_id, & log) ) ;
868
- }
862
+ // This is a bit sneaky: we want to perform each flush in a separate
863
+ // task for *parallelism*, but can't call `self.get_opened_extent_mut`
864
+ // multiple times. In addition, we can't use the tokio thread pool to
865
+ // spawn a task, because that requires a 'static lifetime, which we
866
+ // can't get from a borrowed Extent.
867
+ //
868
+ // We'll combine two tricks to work around the issue:
869
+ // - Do the work in the rayon thread pool instead of using tokio tasks
870
+ // - Carefully walk self.extents.as_mut_slice() to mutably borrow
871
+ // multiple at the same time.
872
+
873
+ let mut slice_start = 0 ;
874
+ let mut slice = self . extents . as_mut_slice ( ) ;
875
+ let mut results = vec ! [ Ok ( ( ) ) ; dirty_extents. len( ) ] ;
876
+ rayon:: scope ( |s| {
877
+ for ( eid, r) in dirty_extents. iter ( ) . zip ( results. iter_mut ( ) ) {
878
+ let next = eid - slice_start;
879
+ slice = & mut slice[ next..] ;
880
+ let ( extent, rest) = slice. split_first_mut ( ) . unwrap ( ) ;
881
+ let ExtentState :: Opened ( extent) = extent else {
882
+ panic ! ( "can't flush closed extent" ) ;
883
+ } ;
884
+ slice = rest;
885
+ slice_start += next + 1 ;
886
+ s. spawn ( |_| {
887
+ * r = extent. flush (
888
+ flush_number,
889
+ gen_number,
890
+ job_id,
891
+ & self . log ,
892
+ )
893
+ } ) ;
894
+ }
895
+ } ) ;
869
896
870
897
cdt:: os__flush__done!( || job_id. 0 ) ;
871
898
@@ -1932,7 +1959,7 @@ pub(crate) mod test {
1932
1959
let mut file = OpenOptions :: new ( )
1933
1960
. read ( true )
1934
1961
. write ( true )
1935
- . open ( & extent_file) ?;
1962
+ . open ( extent_file) ?;
1936
1963
extent_inner_raw:: RawInner :: import (
1937
1964
& mut file,
1938
1965
& ddef,
0 commit comments