@@ -109,9 +109,6 @@ data TableContent m h = TableContent {
109
109
-- | A hierarchy of levels. The vector indexes double as level numbers.
110
110
, tableLevels :: ! (Levels m h )
111
111
-- | Cache of flattened 'levels'.
112
- --
113
- -- INVARIANT: when 'level's is modified, this cache should be updated as
114
- -- well, for example using 'mkLevelsCache'.
115
112
, tableCache :: ! (LevelsCache m (Handle h ))
116
113
}
117
114
@@ -121,21 +118,21 @@ addReferenceTableContent ::
121
118
=> TempRegistry m
122
119
-> TableContent m h
123
120
-> m ()
124
- addReferenceTableContent reg (TableContent _wb wbb levels _cache ) = do
121
+ addReferenceTableContent reg (TableContent _wb wbb levels cache ) = do
125
122
allocateTemp reg (WBB. addReference wbb) (\ _ -> WBB. removeReference wbb)
126
123
addReferenceLevels reg levels
127
- -- references on the cache are implicit
124
+ addReferenceLevelsCache reg cache
128
125
129
126
{-# SPECIALISE removeReferenceTableContent :: TempRegistry IO -> TableContent IO h -> IO () #-}
130
127
removeReferenceTableContent ::
131
128
(PrimMonad m , MonadMask m , MonadMVar m )
132
129
=> TempRegistry m
133
130
-> TableContent m h
134
131
-> m ()
135
- removeReferenceTableContent reg (TableContent _wb wbb levels _cache ) = do
132
+ removeReferenceTableContent reg (TableContent _wb wbb levels cache ) = do
136
133
freeTemp reg (WBB. removeReference wbb)
137
134
removeReferenceLevels reg levels
138
- -- references on the cache are implicit
135
+ removeReferenceLevelsCache reg cache
139
136
140
137
{- ------------------------------------------------------------------------------
141
138
Levels cache
@@ -148,30 +145,97 @@ removeReferenceTableContent reg (TableContent _wb wbb levels _cache) = do
148
145
-- handles. This allows for quick access in the lookup code. Recomputing this
149
146
-- cache should be relatively rare.
150
147
--
151
- -- Use 'mkLevelsCache' to ensure that there are no mismatches between the vector
152
- -- of runs and the vectors of run components.
148
+ -- Caches take reference counts for its runs on construction, and they release
149
+ -- references when the cache is invalidated. This is done so that incremental
150
+ -- merges can remove references for their input runs when a merge completes,
151
+ -- without closing runs that might be in use for other operations such as
152
+ -- lookups. This does mean that a cache can keep runs open for longer than
153
+ -- necessary, so caches should be rebuilt using, e.g., 'rebuildCache', in a
154
+ -- timely manner.
153
155
data LevelsCache m h = LevelsCache_ {
154
156
cachedRuns :: ! (V. Vector (Run m h ))
155
157
, cachedFilters :: ! (V. Vector (Bloom SerialisedKey ))
156
158
, cachedIndexes :: ! (V. Vector IndexCompact )
157
159
, cachedKOpsFiles :: ! (V. Vector h )
158
160
}
159
161
160
- {-# SPECIALISE mkLevelsCache :: Levels IO h -> IO (LevelsCache IO (Handle h)) #-}
162
+ {-# SPECIALISE mkLevelsCache ::
163
+ TempRegistry IO
164
+ -> Levels IO h
165
+ -> IO (LevelsCache IO (Handle h)) #-}
161
166
-- | Flatten the argument 'Level's into a single vector of runs, and use that to
162
- -- populate the 'LevelsCache'.
167
+ -- populate the 'LevelsCache'. The cache will take a reference for each of the
168
+ -- runs that end up in the cache.
163
169
mkLevelsCache ::
164
- MonadMVar m
165
- => Levels m h -> m (LevelsCache m (Handle h ))
166
- mkLevelsCache lvls = do
167
- rs <- forRunM lvls pure
170
+ (PrimMonad m , MonadMVar m , MonadMask m )
171
+ => TempRegistry m
172
+ -> Levels m h
173
+ -> m (LevelsCache m (Handle h ))
174
+ mkLevelsCache reg lvls = do
175
+ rs <- forRunM lvls $ \ r -> allocateTemp reg (Run. addReference r) (\ _ -> Run. removeReference r) >> pure r
168
176
pure $! LevelsCache_ {
169
177
cachedRuns = rs
170
178
, cachedFilters = mapStrict Run. runFilter rs
171
179
, cachedIndexes = mapStrict Run. runIndex rs
172
180
, cachedKOpsFiles = mapStrict Run. runKOpsFile rs
173
181
}
174
182
183
+ {-# SPECIALISE rebuildCache ::
184
+ TempRegistry IO
185
+ -> LevelsCache IO (Handle h)
186
+ -> Levels IO h
187
+ -> IO (LevelsCache IO (Handle h)) #-}
188
+ -- | Remove references to runs in the old cache, and create a new cache with
189
+ -- fresh references taken for the runs in the new levels.
190
+ --
191
+ -- TODO: caches are currently only rebuilt in flushWriteBuffer. If an
192
+ -- OngoingMerge is completed, then tables will only rebuild the cache, and
193
+ -- therefore release "old" runs, when a flush is initiated. This is sub-optimal,
194
+ -- and there are at least two solutions, but it is unclear which is faster or
195
+ -- more convenient.
196
+ --
197
+ -- * Get rid of the cache entirely, and have each batch of lookups take
198
+ -- references for runs in the levels structure.
199
+ --
200
+ -- * Keep the cache feature, but force a rebuild every once in a while, e.g.,
201
+ -- once in every 100 lookups.
202
+ rebuildCache ::
203
+ (PrimMonad m , MonadMVar m , MonadMask m )
204
+ => TempRegistry m
205
+ -> LevelsCache m (Handle h ) -- ^ old cache
206
+ -> Levels m h -- ^ new levels
207
+ -> m (LevelsCache m (Handle h )) -- ^ new cache
208
+ rebuildCache reg oldCache newLevels = do
209
+ removeReferenceLevelsCache reg oldCache
210
+ mkLevelsCache reg newLevels
211
+
212
+ {-# SPECIALISE addReferenceLevelsCache ::
213
+ TempRegistry IO
214
+ -> LevelsCache IO (Handle h)
215
+ -> IO () #-}
216
+ addReferenceLevelsCache ::
217
+ (PrimMonad m , MonadMask m , MonadMVar m )
218
+ => TempRegistry m
219
+ -> LevelsCache m (Handle h )
220
+ -> m ()
221
+ addReferenceLevelsCache reg cache =
222
+ V. forM_ (cachedRuns cache) $ \ r ->
223
+ allocateTemp reg
224
+ (Run. addReference r)
225
+ (\ _ -> Run. removeReference r)
226
+
227
+ {-# SPECIALISE removeReferenceLevelsCache ::
228
+ TempRegistry IO
229
+ -> LevelsCache IO (Handle h)
230
+ -> IO () #-}
231
+ removeReferenceLevelsCache ::
232
+ (PrimMonad m , MonadMVar m , MonadMask m )
233
+ => TempRegistry m
234
+ -> LevelsCache m (Handle h )
235
+ -> m ()
236
+ removeReferenceLevelsCache reg cache =
237
+ V. forM_ (cachedRuns cache) $ \ r -> freeTemp reg (Run. removeReference r)
238
+
175
239
{- ------------------------------------------------------------------------------
176
240
Levels, runs and ongoing merges
177
241
-------------------------------------------------------------------------------}
@@ -434,12 +498,12 @@ flushWriteBuffer tr conf@TableConfig{confDiskCachePolicy}
434
498
freeTemp reg (WBB. removeReference (tableWriteBufferBlobs tc))
435
499
wbblobs' <- allocateTemp reg (WBB. new hfs (Paths. tableBlobPath root n)) WBB. removeReference
436
500
levels' <- addRunToLevels tr conf resolve hfs hbio root uc r reg (tableLevels tc)
437
- cache ' <- mkLevelsCache levels'
501
+ tableCache ' <- rebuildCache reg (tableCache tc) levels'
438
502
pure $! TableContent {
439
503
tableWriteBuffer = WB. empty
440
504
, tableWriteBufferBlobs = wbblobs'
441
505
, tableLevels = levels'
442
- , tableCache = cache '
506
+ , tableCache = tableCache '
443
507
}
444
508
445
509
{- TODO: re-enable
0 commit comments