@@ -2073,6 +2073,7 @@ type L0Organizer struct {
20732073 cmp base.Compare
20742074 formatKey base.FormatKey
20752075 flushSplitBytes int64
2076+ generation int64
20762077
20772078 // levelMetadata is the current L0.
20782079 levelMetadata LevelMetadata
@@ -2108,68 +2109,108 @@ func NewL0Organizer(comparer *base.Comparer, flushSplitBytes int64) *L0Organizer
21082109 return o
21092110}
21102111
2111- // SublevelFiles returns the sublevels as LevelSlices. The returned value (both
2112- // the slice and each LevelSlice) is immutable. The L0Organizer creates new
2113- // slices every time L0 changes.
2114- func (o * L0Organizer ) SublevelFiles () []LevelSlice {
2115- return o .l0Sublevels .Levels
2116- }
2117-
2118- // Update the L0 organizer with the given L0 changes.
2119- func (o * L0Organizer ) Update (
2120- addedL0Tables map [base.FileNum ]* TableMetadata ,
2121- deletedL0Tables map [base.FileNum ]* TableMetadata ,
2122- newLevelMeta * LevelMetadata ,
2123- ) {
2112+ // PrepareUpdate is the first step in the two-step process to update the
2113+ // L0Organizer. This first step performs as much work as it can without
2114+ // modifying the L0Organizer.
2115+ //
2116+ // This method can be called concurrently with other methods (other than
2117+ // PerformUpdate). It allows doing most of the update work outside an important
2118+ // lock.
2119+ func (o * L0Organizer ) PrepareUpdate (bve * BulkVersionEdit , newVersion * Version ) L0PreparedUpdate {
2120+ addedL0Tables := bve .AddedTables [0 ]
2121+ deletedL0Tables := bve .DeletedTables [0 ]
2122+ newLevelMeta := & newVersion .Levels [0 ]
21242123 if invariants .Enabled && invariants .Sometimes (10 ) {
21252124 // Verify that newLevelMeta = m.levelMetadata + addedL0Tables - deletedL0Tables.
21262125 verifyLevelMetadataTransition (& o .levelMetadata , newLevelMeta , addedL0Tables , deletedL0Tables )
21272126 }
2128- o . levelMetadata = * newLevelMeta
2127+
21292128 if len (addedL0Tables ) == 0 && len (deletedL0Tables ) == 0 {
2130- return
2129+ return L0PreparedUpdate {
2130+ generation : o .generation ,
2131+ newSublevels : o .l0Sublevels ,
2132+ }
21312133 }
2132- // If we only added tables, try to use addL0Files.
2134+
21332135 if len (deletedL0Tables ) == 0 {
21342136 if files , ok := o .l0Sublevels .canUseAddL0Files (addedL0Tables , newLevelMeta ); ok {
2135- newSublevels := o .l0Sublevels .addL0Files (files , o .flushSplitBytes , newLevelMeta )
2136- // In invariants mode, sometimes rebuild from scratch to verify that
2137- // AddL0Files did the right thing. Note that NewL0Sublevels updates
2138- // fields in TableMetadata like L0Index, so we don't want to do this
2139- // every time.
2140- if invariants .Enabled && invariants .Sometimes (10 ) {
2141- expectedSublevels , err := newL0Sublevels (newLevelMeta , o .cmp , o .formatKey , o .flushSplitBytes )
2142- if err != nil {
2143- panic (fmt .Sprintf ("error when regenerating sublevels: %s" , err ))
2144- }
2145- s1 := describeSublevels (o .formatKey , false /* verbose */ , expectedSublevels .Levels )
2146- s2 := describeSublevels (o .formatKey , false /* verbose */ , newSublevels .Levels )
2147- if s1 != s2 {
2148- // Add verbosity.
2149- s1 := describeSublevels (o .formatKey , true /* verbose */ , expectedSublevels .Levels )
2150- s2 := describeSublevels (o .formatKey , true /* verbose */ , newSublevels .Levels )
2151- panic (fmt .Sprintf ("incremental L0 sublevel generation produced different output than regeneration: %s != %s" , s1 , s2 ))
2152- }
2137+ return L0PreparedUpdate {
2138+ generation : o .generation ,
2139+ addL0Files : files ,
21532140 }
2154- o .l0Sublevels = newSublevels
2155- return
21562141 }
21572142 }
2158- var err error
2159- o .l0Sublevels , err = newL0Sublevels (newLevelMeta , o .cmp , o .formatKey , o .flushSplitBytes )
2143+ newSublevels , err := newL0Sublevels (newLevelMeta , o .cmp , o .formatKey , o .flushSplitBytes )
21602144 if err != nil {
21612145 panic (errors .AssertionFailedf ("error generating L0Sublevels: %s" , err ))
21622146 }
2147+
2148+ return L0PreparedUpdate {
2149+ generation : o .generation ,
2150+ newSublevels : newSublevels ,
2151+ }
2152+ }
2153+
2154+ // L0PreparedUpdate is returned by L0Organizer.PrepareUpdate(), to be passed to
2155+ // PerformUpdate().
2156+ type L0PreparedUpdate struct {
2157+ generation int64
2158+
2159+ // Exactly one of the following fields will be set.
2160+ addL0Files []* TableMetadata
2161+ newSublevels * l0Sublevels
2162+ }
2163+
2164+ // PerformUpdate applies an update the L0 organizer which was previously
2165+ // prepared using PrepareUpdate.
2166+ //
2167+ // Sets newVersion.L0SublevelFiles (which is immutable once set).
2168+ //
2169+ // This method cannot be called concurrently with any other methods.
2170+ func (o * L0Organizer ) PerformUpdate (prepared L0PreparedUpdate , newVersion * Version ) {
2171+ if prepared .generation != o .generation {
2172+ panic ("invalid L0 update generation" )
2173+ }
2174+ o .levelMetadata = newVersion .Levels [0 ]
2175+ o .generation ++
2176+ if prepared .addL0Files != nil {
2177+ newSublevels := o .l0Sublevels .addL0Files (prepared .addL0Files , o .flushSplitBytes , & o .levelMetadata )
2178+ // In invariants mode, sometimes rebuild from scratch to verify that
2179+ // AddL0Files did the right thing. Note that NewL0Sublevels updates
2180+ // fields in TableMetadata like L0Index, so we don't want to do this
2181+ // every time.
2182+ if invariants .Enabled && invariants .Sometimes (10 ) {
2183+ expectedSublevels , err := newL0Sublevels (& o .levelMetadata , o .cmp , o .formatKey , o .flushSplitBytes )
2184+ if err != nil {
2185+ panic (fmt .Sprintf ("error when regenerating sublevels: %s" , err ))
2186+ }
2187+ s1 := describeSublevels (o .formatKey , false /* verbose */ , expectedSublevels .Levels )
2188+ s2 := describeSublevels (o .formatKey , false /* verbose */ , newSublevels .Levels )
2189+ if s1 != s2 {
2190+ // Add verbosity.
2191+ s1 := describeSublevels (o .formatKey , true /* verbose */ , expectedSublevels .Levels )
2192+ s2 := describeSublevels (o .formatKey , true /* verbose */ , newSublevels .Levels )
2193+ panic (fmt .Sprintf ("incremental L0 sublevel generation produced different output than regeneration: %s != %s" , s1 , s2 ))
2194+ }
2195+ }
2196+ o .l0Sublevels = newSublevels
2197+ } else {
2198+ o .l0Sublevels = prepared .newSublevels
2199+ }
2200+ newVersion .L0SublevelFiles = o .l0Sublevels .Levels
21632201}
21642202
2165- // ResetForTesting reinitializes the L0Organizer to reflect a given L0 level.
2166- func (o * L0Organizer ) ResetForTesting (levelMetadata * LevelMetadata ) {
2167- o .levelMetadata = * levelMetadata
2203+ // ResetForTesting reinitializes the L0Organizer to reflect the given version.
2204+ // Sets v.L0SublevelFiles.
2205+ func (o * L0Organizer ) ResetForTesting (v * Version ) {
2206+ o .levelMetadata = v .Levels [0 ]
2207+ o .generation = 0
21682208 var err error
2169- o .l0Sublevels , err = newL0Sublevels (levelMetadata , o .cmp , o .formatKey , o .flushSplitBytes )
2209+ o .l0Sublevels , err = newL0Sublevels (& v . Levels [ 0 ] , o .cmp , o .formatKey , o .flushSplitBytes )
21702210 if err != nil {
21712211 panic (errors .AssertionFailedf ("error generating L0Sublevels: %s" , err ))
21722212 }
2213+ v .L0SublevelFiles = o .l0Sublevels .Levels
21732214}
21742215
21752216// verifyLevelMetadataTransition verifies that newLevel matches oldLevel after
0 commit comments