@@ -249,7 +249,7 @@ type L0Sublevels struct {
249249
250250 fileBytes uint64
251251 // All the L0 files, ordered from oldest to youngest.
252- levelMetadata * LevelMetadata
252+ levelMetadata LevelMetadata
253253
254254 // The file intervals in increasing key order.
255255 orderedIntervals []fileInterval
@@ -281,7 +281,7 @@ func NewL0Sublevels(
281281 levelMetadata * LevelMetadata , cmp Compare , formatKey base.FormatKey , flushSplitMaxBytes int64 ,
282282) (* L0Sublevels , error ) {
283283 s := & L0Sublevels {cmp : cmp , formatKey : formatKey }
284- s .levelMetadata = levelMetadata
284+ s .levelMetadata = * levelMetadata
285285 keys := make ([]intervalKeyTemp , 0 , 2 * s .levelMetadata .Len ())
286286 iter := levelMetadata .Iter ()
287287 for i , f := 0 , iter .First (); f != nil ; i , f = i + 1 , iter .Next () {
@@ -480,7 +480,7 @@ func (s *L0Sublevels) AddL0Files(
480480 * newVal = * s
481481
482482 newVal .addL0FilesCalled = false
483- newVal .levelMetadata = levelMetadata
483+ newVal .levelMetadata = * levelMetadata
484484 // Deep copy levelFiles and Levels, as they are mutated and sorted below.
485485 // Shallow copies of slices that we just append to, are okay.
486486 newVal .levelFiles = make ([][]* TableMetadata , len (s .levelFiles ))
@@ -2073,3 +2073,164 @@ func (s *L0Sublevels) extendCandidateToRectangle(
20732073 }
20742074 return addedCount > 0
20752075}
2076+
2077+ // L0Organizer keeps track of L0 state, including the subdivision into
2078+ // sublevels.
2079+ //
2080+ // It is designed to be used as a singleton (per store) which gets updated as
2081+ // the version changes. It is used to initialize L0-related Version fields.
2082+ type L0Organizer struct {
2083+ cmp base.Compare
2084+ formatKey base.FormatKey
2085+ flushSplitBytes int64
2086+
2087+ // levelMetadata is the current L0.
2088+ levelMetadata LevelMetadata
2089+
2090+ // sublevels reflects the current L0.
2091+ sublevels * L0Sublevels
2092+ }
2093+
2094+ // NewL0Organizer creates the L0 organizer. The L0 organizer is responsible for
2095+ // maintaining the current L0 state and is kept in-sync with the current Version.
2096+ //
2097+ // flushSplitBytes denotes the target number of bytes per sublevel in each flush
2098+ // split interval (i.e. range between two flush split keys) in L0 sstables. When
2099+ // set to zero, only a single sstable is generated by each flush. When set to a
2100+ // non-zero value, flushes are split at points to meet L0's TargetFileSize, any
2101+ // grandparent-related overlap options, and at boundary keys of L0 flush split
2102+ // intervals (which are targeted to contain around FlushSplitBytes bytes in each
2103+ // sublevel between pairs of boundary keys). Splitting sstables during flush
2104+ // allows increased compaction flexibility and concurrency when those tables are
2105+ // compacted to lower levels.
2106+ func NewL0Organizer (comparer * base.Comparer , flushSplitBytes int64 ) * L0Organizer {
2107+ o := & L0Organizer {
2108+ cmp : comparer .Compare ,
2109+ formatKey : comparer .FormatKey ,
2110+ flushSplitBytes : flushSplitBytes ,
2111+ levelMetadata : MakeLevelMetadata (comparer .Compare , 0 , nil ),
2112+ }
2113+ var err error
2114+ o .sublevels , err = NewL0Sublevels (& o .levelMetadata , o .cmp , o .formatKey , o .flushSplitBytes )
2115+ if err != nil {
2116+ panic (errors .AssertionFailedf ("error generating empty L0Sublevels: %s" , err ))
2117+ }
2118+ return o
2119+ }
2120+
2121+ // Sublevels returns the *L0Sublevels reflecting the current L0 state.
2122+ func (o * L0Organizer ) Sublevels () * L0Sublevels {
2123+ return o .sublevels
2124+ }
2125+
2126+ // Update the L0 organizer with the given L0 changes.
2127+ func (o * L0Organizer ) Update (
2128+ addedL0Tables map [base.FileNum ]* TableMetadata ,
2129+ deletedL0Tables map [base.FileNum ]* TableMetadata ,
2130+ newLevelMeta * LevelMetadata ,
2131+ ) {
2132+ if invariants .Enabled && invariants .Sometimes (10 ) {
2133+ // Verify that newLevelMeta = m.levelMetadata + addedL0Tables - deletedL0Tables.
2134+ verifyLevelMetadataTransition (& o .levelMetadata , newLevelMeta , addedL0Tables , deletedL0Tables )
2135+ }
2136+ o .levelMetadata = * newLevelMeta
2137+ if len (addedL0Tables ) == 0 && len (deletedL0Tables ) == 0 {
2138+ return
2139+ }
2140+ // If we only added tables, try to use AddL0Files.
2141+ if len (deletedL0Tables ) == 0 {
2142+ // Construct the file slice needed by AddL0Files.
2143+ // TODO(radu): change AddL0Files to do this internally.
2144+ files := make ([]* TableMetadata , 0 , len (addedL0Tables ))
2145+ iter := newLevelMeta .Iter ()
2146+ for t := iter .Last (); len (files ) < len (addedL0Tables ); t = iter .Prev () {
2147+ if t == nil || addedL0Tables [t .FileNum ] == nil {
2148+ break
2149+ }
2150+ files = append (files , t )
2151+ }
2152+ if len (files ) == len (addedL0Tables ) {
2153+ slices .Reverse (files )
2154+ newSublevels , err := o .sublevels .AddL0Files (files , o .flushSplitBytes , newLevelMeta )
2155+ if err == nil {
2156+ // In invariants mode, sometimes rebuild from scratch to verify that
2157+ // AddL0Files did the right thing. Note that NewL0Sublevels updates
2158+ // fields in TableMetadata like L0Index, so we don't want to do this
2159+ // every time.
2160+ if invariants .Enabled && invariants .Sometimes (10 ) {
2161+ expectedSublevels , err := NewL0Sublevels (newLevelMeta , o .cmp , o .formatKey , o .flushSplitBytes )
2162+ if err != nil {
2163+ panic (fmt .Sprintf ("error when regenerating sublevels: %s" , err ))
2164+ }
2165+ s1 := describeSublevels (o .formatKey , false /* verbose */ , expectedSublevels .Levels )
2166+ s2 := describeSublevels (o .formatKey , false /* verbose */ , newSublevels .Levels )
2167+ if s1 != s2 {
2168+ // Add verbosity.
2169+ s1 := describeSublevels (o .formatKey , true /* verbose */ , expectedSublevels .Levels )
2170+ s2 := describeSublevels (o .formatKey , true /* verbose */ , newSublevels .Levels )
2171+ panic (fmt .Sprintf ("incremental L0 sublevel generation produced different output than regeneration: %s != %s" , s1 , s2 ))
2172+ }
2173+ }
2174+ o .sublevels = newSublevels
2175+ return
2176+ }
2177+ if ! errors .Is (err , errInvalidL0SublevelsOpt ) {
2178+ panic (errors .AssertionFailedf ("error generating L0Sublevels: %s" , err ))
2179+ }
2180+ }
2181+ }
2182+ var err error
2183+ o .sublevels , err = NewL0Sublevels (newLevelMeta , o .cmp , o .formatKey , o .flushSplitBytes )
2184+ if err != nil {
2185+ panic (errors .AssertionFailedf ("error generating L0Sublevels: %s" , err ))
2186+ }
2187+ }
2188+
2189+ // Reset the L0Organizer to reflect a given L0 level. Used for testing.
2190+ func (o * L0Organizer ) Reset (levelMetadata * LevelMetadata ) {
2191+ o .levelMetadata = * levelMetadata
2192+ var err error
2193+ o .sublevels , err = NewL0Sublevels (levelMetadata , o .cmp , o .formatKey , o .flushSplitBytes )
2194+ if err != nil {
2195+ panic (errors .AssertionFailedf ("error generating L0Sublevels: %s" , err ))
2196+ }
2197+ }
2198+
2199+ // verifyLevelMetadataTransition verifies that newLevel matches oldLevel after
2200+ // adding and removing the specified tables.
2201+ func verifyLevelMetadataTransition (
2202+ oldLevel , newLevel * LevelMetadata ,
2203+ addedTables map [base.FileNum ]* TableMetadata ,
2204+ deletedTables map [base.FileNum ]* TableMetadata ,
2205+ ) {
2206+ m := make (map [base.FileNum ]* TableMetadata , oldLevel .Len ())
2207+ iter := oldLevel .Iter ()
2208+ for t := iter .First (); t != nil ; t = iter .Next () {
2209+ m [t .FileNum ] = t
2210+ }
2211+ for n , t := range addedTables {
2212+ if m [n ] != nil {
2213+ panic ("added table that already exists in old level" )
2214+ }
2215+ m [n ] = t
2216+ }
2217+ for n , t := range deletedTables {
2218+ if m [n ] == nil {
2219+ panic ("deleted table not in old level" )
2220+ }
2221+ if m [n ] != t {
2222+ panic ("deleted table does not match old level" )
2223+ }
2224+ delete (m , n )
2225+ }
2226+ iter = newLevel .Iter ()
2227+ for t := iter .First (); t != nil ; t = iter .Next () {
2228+ if m [t .FileNum ] == nil {
2229+ panic ("unknown table in new level" )
2230+ }
2231+ delete (m , t .FileNum )
2232+ }
2233+ if len (m ) != 0 {
2234+ panic ("tables missing from the new level" )
2235+ }
2236+ }
0 commit comments