Skip to content

Commit fd61a17

Browse files
committed
Introduce proper distinction between {Raw,Weak,Strong}BlobRef
Previously a RawBlobRef served dual purpose for raw and strong. Keep that distinction clear. For the moment, all three have equivalent representations, but this is likely to change in a later refactoring of the reference counting API. Also split the functions for making blob refs from runs or the write buffer into the two variants that we use: raw (internally) and weak (externally).
1 parent 65c3f04 commit fd61a17

File tree

11 files changed

+141
-72
lines changed

11 files changed

+141
-72
lines changed

src-extras/Database/LSMTree/Extras/NoThunks.hs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -417,17 +417,21 @@ deriving anyclass instance NoThunks RawOverflowPage
417417
BlobRef
418418
-------------------------------------------------------------------------------}
419419

420-
deriving stock instance Generic (RawBlobRef m h)
421-
deriving anyclass instance (Typeable h, Typeable (PrimState m))
422-
=> NoThunks (RawBlobRef m h)
423-
424420
deriving stock instance Generic BlobSpan
425421
deriving anyclass instance NoThunks BlobSpan
426422

427423
deriving stock instance Generic (BlobFile m h)
428424
deriving anyclass instance (Typeable h, Typeable (PrimState m))
429425
=> NoThunks (BlobFile m h)
430426

427+
deriving stock instance Generic (RawBlobRef m h)
428+
deriving anyclass instance (Typeable h, Typeable (PrimState m))
429+
=> NoThunks (RawBlobRef m h)
430+
431+
deriving stock instance Generic (WeakBlobRef m h)
432+
deriving anyclass instance (Typeable h, Typeable (PrimState m))
433+
=> NoThunks (WeakBlobRef m h)
434+
431435
{-------------------------------------------------------------------------------
432436
Arena
433437
-------------------------------------------------------------------------------}

src/Database/LSMTree/Internal/BlobRef.hs

Lines changed: 83 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ module Database.LSMTree.Internal.BlobRef (
1313
, deRefWeakBlobRef
1414
, deRefWeakBlobRefs
1515
, WeakBlobRefInvalid (..)
16+
, rawToWeakBlobRef
1617
, removeReference
1718
, removeReferences
19+
, readRawBlobRef
1820
, readBlob
1921
, readBlobIOOp
2022
) where
@@ -25,9 +27,9 @@ import Control.Monad.Class.MonadThrow (Exception, MonadMask,
2527
MonadThrow (..), bracket, throwIO)
2628
import Control.Monad.Primitive
2729
import qualified Control.RefCount as RC
28-
import Data.Coerce (coerce)
2930
import qualified Data.Primitive.ByteArray as P (MutableByteArray)
3031
import qualified Data.Vector as V
32+
import qualified Data.Vector.Mutable as VM
3133
import Database.LSMTree.Internal.BlobFile (BlobFile (..), BlobSpan (..))
3234
import qualified Database.LSMTree.Internal.BlobFile as BlobFile
3335
import Database.LSMTree.Internal.Serialise (SerialisedBlob (..))
@@ -54,8 +56,8 @@ data RawBlobRef m h = RawBlobRef {
5456
instance NFData h => NFData (RawBlobRef m h) where
5557
rnf (RawBlobRef a b) = rnf a `seq` rnf b
5658

57-
blobRefSpanSize :: RawBlobRef m h -> Int
58-
blobRefSpanSize = fromIntegral . blobSpanSize . rawBlobRefSpan
59+
blobRefSpanSize :: StrongBlobRef m h -> Int
60+
blobRefSpanSize = fromIntegral . blobSpanSize . strongBlobRefSpan
5961

6062
-- | A \"weak\" reference to a blob within a blob file. These are the ones we
6163
-- can return in the public API and can outlive their parent table.
@@ -67,8 +69,33 @@ blobRefSpanSize = fromIntegral . blobSpanSize . rawBlobRefSpan
6769
--
6870
-- See 'Database.LSMTree.Common.BlobRef' for more info.
6971
--
70-
newtype WeakBlobRef m h = WeakBlobRef (RawBlobRef m h)
71-
deriving newtype (Show, NFData)
72+
data WeakBlobRef m h = WeakBlobRef {
73+
weakBlobRefFile :: {-# NOUNPACK #-} !(BlobFile m h)
74+
, weakBlobRefSpan :: {-# UNPACK #-} !BlobSpan
75+
}
76+
deriving stock (Show)
77+
78+
-- | A \"strong\" reference to a blob within a blob file. The blob file remains
79+
-- open while the strong reference is live. Thus it is safe to do I\/O to
80+
-- retrieve the blob based on the reference. Strong references must be released
81+
-- using 'releaseBlobRef' when no longer in use (e.g. after completing I\/O).
82+
--
83+
data StrongBlobRef m h = StrongBlobRef {
84+
strongBlobRefFile :: {-# NOUNPACK #-} !(BlobFile m h)
85+
, strongBlobRefSpan :: {-# UNPACK #-} !BlobSpan
86+
}
87+
deriving stock (Show)
88+
89+
-- | Convert a 'RawBlobRef' to a 'WeakBlobRef'.
90+
rawToWeakBlobRef :: RawBlobRef m h -> WeakBlobRef m h
91+
rawToWeakBlobRef RawBlobRef {rawBlobRefFile, rawBlobRefSpan} =
92+
-- This doesn't need to really do anything, becuase the raw version
93+
-- does not maintain an independent ref count, and the weak one does
94+
-- not either.
95+
WeakBlobRef {
96+
weakBlobRefFile = rawBlobRefFile,
97+
weakBlobRefSpan = rawBlobRefSpan
98+
}
7299

73100
-- | The 'WeakBlobRef' now points to a blob that is no longer available.
74101
newtype WeakBlobRefInvalid = WeakBlobRefInvalid Int
@@ -77,7 +104,7 @@ newtype WeakBlobRefInvalid = WeakBlobRefInvalid Int
77104

78105
{-# SPECIALISE withWeakBlobRef ::
79106
WeakBlobRef IO h
80-
-> (RawBlobRef IO h -> IO a)
107+
-> (StrongBlobRef IO h -> IO a)
81108
-> IO a #-}
82109
-- | 'WeakBlobRef's are weak references. They do not keep the blob file open.
83110
-- Dereference a 'WeakBlobRef' to a strong 'BlobRef' to allow I\/O using
@@ -89,82 +116,98 @@ newtype WeakBlobRefInvalid = WeakBlobRefInvalid Int
89116
withWeakBlobRef ::
90117
(MonadMask m, PrimMonad m)
91118
=> WeakBlobRef m h
92-
-> (RawBlobRef m h -> m a)
119+
-> (StrongBlobRef m h -> m a)
93120
-> m a
94121
withWeakBlobRef wref = bracket (deRefWeakBlobRef wref) removeReference
95122

96123
{-# SPECIALISE withWeakBlobRefs ::
97124
V.Vector (WeakBlobRef IO h)
98-
-> (V.Vector (RawBlobRef IO h) -> IO a)
125+
-> (V.Vector (StrongBlobRef IO h) -> IO a)
99126
-> IO a #-}
100127
-- | The same as 'withWeakBlobRef' but for many references in one go.
101128
--
102129
withWeakBlobRefs ::
103130
(MonadMask m, PrimMonad m)
104131
=> V.Vector (WeakBlobRef m h)
105-
-> (V.Vector (RawBlobRef m h) -> m a)
132+
-> (V.Vector (StrongBlobRef m h) -> m a)
106133
-> m a
107134
withWeakBlobRefs wrefs = bracket (deRefWeakBlobRefs wrefs) removeReferences
108135

109136
{-# SPECIALISE deRefWeakBlobRef ::
110137
WeakBlobRef IO h
111-
-> IO (RawBlobRef IO h) #-}
138+
-> IO (StrongBlobRef IO h) #-}
112139
deRefWeakBlobRef ::
113140
(MonadThrow m, PrimMonad m)
114141
=> WeakBlobRef m h
115-
-> m (RawBlobRef m h)
116-
deRefWeakBlobRef (WeakBlobRef ref) = do
117-
ok <- RC.upgradeWeakReference (blobFileRefCounter (rawBlobRefFile ref))
142+
-> m (StrongBlobRef m h)
143+
deRefWeakBlobRef WeakBlobRef{weakBlobRefFile, weakBlobRefSpan} = do
144+
ok <- RC.upgradeWeakReference (blobFileRefCounter weakBlobRefFile)
118145
when (not ok) $ throwIO (WeakBlobRefInvalid 0)
119-
pure ref
146+
return StrongBlobRef{
147+
strongBlobRefFile = weakBlobRefFile,
148+
strongBlobRefSpan = weakBlobRefSpan
149+
}
120150

121151
{-# SPECIALISE deRefWeakBlobRefs ::
122152
V.Vector (WeakBlobRef IO h)
123-
-> IO (V.Vector (RawBlobRef IO h)) #-}
153+
-> IO (V.Vector (StrongBlobRef IO h)) #-}
124154
deRefWeakBlobRefs ::
125155
forall m h.
126156
(MonadMask m, PrimMonad m)
127157
=> V.Vector (WeakBlobRef m h)
128-
-> m (V.Vector (RawBlobRef m h))
158+
-> m (V.Vector (StrongBlobRef m h))
129159
deRefWeakBlobRefs wrefs = do
130-
let refs :: V.Vector (RawBlobRef m h)
131-
refs = coerce wrefs -- safely coerce away the newtype wrappers
132-
V.iforM_ wrefs $ \i (WeakBlobRef ref) -> do
133-
ok <- RC.upgradeWeakReference (blobFileRefCounter (rawBlobRefFile ref))
134-
when (not ok) $ do
135-
-- drop refs on the previous ones taken successfully so far
136-
V.mapM_ removeReference (V.take i refs)
137-
throwIO (WeakBlobRefInvalid i)
138-
pure refs
139-
140-
{-# SPECIALISE removeReference :: RawBlobRef IO h -> IO () #-}
141-
removeReference :: (MonadMask m, PrimMonad m) => RawBlobRef m h -> m ()
142-
removeReference = BlobFile.removeReference . rawBlobRefFile
143-
144-
{-# SPECIALISE removeReferences :: V.Vector (RawBlobRef IO h) -> IO () #-}
145-
removeReferences :: (MonadMask m, PrimMonad m) => V.Vector (RawBlobRef m h) -> m ()
160+
refs <- VM.new (V.length wrefs)
161+
V.iforM_ wrefs $ \i WeakBlobRef {weakBlobRefFile, weakBlobRefSpan} -> do
162+
ok <- RC.upgradeWeakReference (blobFileRefCounter weakBlobRefFile)
163+
if ok
164+
then VM.write refs i StrongBlobRef {
165+
strongBlobRefFile = weakBlobRefFile,
166+
strongBlobRefSpan = weakBlobRefSpan
167+
}
168+
else do
169+
-- drop refs on the previous ones taken successfully so far
170+
VM.mapM_ removeReference (VM.take i refs)
171+
throwIO (WeakBlobRefInvalid i)
172+
V.unsafeFreeze refs
173+
174+
{-# SPECIALISE removeReference :: StrongBlobRef IO h -> IO () #-}
175+
removeReference :: (MonadMask m, PrimMonad m) => StrongBlobRef m h -> m ()
176+
removeReference = BlobFile.removeReference . strongBlobRefFile
177+
178+
{-# SPECIALISE removeReferences :: V.Vector (StrongBlobRef IO h) -> IO () #-}
179+
removeReferences :: (MonadMask m, PrimMonad m) => V.Vector (StrongBlobRef m h) -> m ()
146180
removeReferences = V.mapM_ removeReference
147181

182+
{-# INLINE readRawBlobRef #-}
183+
readRawBlobRef ::
184+
(MonadThrow m, PrimMonad m)
185+
=> HasFS m h
186+
-> RawBlobRef m h
187+
-> m SerialisedBlob
188+
readRawBlobRef fs RawBlobRef {rawBlobRefFile, rawBlobRefSpan} =
189+
BlobFile.readBlobFile fs rawBlobRefFile rawBlobRefSpan
190+
148191
{-# SPECIALISE readBlob ::
149192
HasFS IO h
150-
-> RawBlobRef IO h
193+
-> StrongBlobRef IO h
151194
-> IO SerialisedBlob #-}
152195
readBlob ::
153196
(MonadThrow m, PrimMonad m)
154197
=> HasFS m h
155-
-> RawBlobRef m h
198+
-> StrongBlobRef m h
156199
-> m SerialisedBlob
157-
readBlob fs RawBlobRef {rawBlobRefFile, rawBlobRefSpan} =
158-
BlobFile.readBlobFile fs rawBlobRefFile rawBlobRefSpan
200+
readBlob fs StrongBlobRef {strongBlobRefFile, strongBlobRefSpan} =
201+
BlobFile.readBlobFile fs strongBlobRefFile strongBlobRefSpan
159202

160203
readBlobIOOp ::
161204
P.MutableByteArray s -> Int
162-
-> RawBlobRef m h
205+
-> StrongBlobRef m h
163206
-> FS.IOOp s h
164207
readBlobIOOp buf bufoff
165-
RawBlobRef {
166-
rawBlobRefFile = BlobFile {blobFileHandle},
167-
rawBlobRefSpan = BlobSpan {blobSpanOffset, blobSpanSize}
208+
StrongBlobRef {
209+
strongBlobRefFile = BlobFile {blobFileHandle},
210+
strongBlobRefSpan = BlobSpan {blobSpanOffset, blobSpanSize}
168211
} =
169212
FS.IOOpRead
170213
blobFileHandle

src/Database/LSMTree/Internal/Cursor.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import Control.Monad.Fix (MonadFix)
1111
import qualified Data.Vector as V
1212
import Database.LSMTree.Internal.BlobRef (RawBlobRef,
1313
WeakBlobRef (..))
14+
import qualified Database.LSMTree.Internal.BlobRef as BlobRef
1415
import Database.LSMTree.Internal.Entry (Entry)
1516
import qualified Database.LSMTree.Internal.Entry as Entry
1617
import Database.LSMTree.Internal.Lookup (ResolveSerialisedValue)
@@ -126,6 +127,6 @@ readEntriesWhile resolve keyIsWanted fromEntry readers n =
126127
-> Maybe res
127128
toResult key = \case
128129
Entry.Insert v -> Just $ fromEntry key v Nothing
129-
Entry.InsertWithBlob v b -> Just $ fromEntry key v (Just (WeakBlobRef b))
130+
Entry.InsertWithBlob v b -> Just $ fromEntry key v (Just (BlobRef.rawToWeakBlobRef b))
130131
Entry.Mupdate v -> Just $ fromEntry key v Nothing
131132
Entry.Delete -> Nothing

src/Database/LSMTree/Internal/Lookup.hs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ import Database.LSMTree.Internal.Page (PageSpan (..), getNumPages,
4747
import Database.LSMTree.Internal.RawBytes (RawBytes (..))
4848
import qualified Database.LSMTree.Internal.RawBytes as RB
4949
import Database.LSMTree.Internal.RawPage
50-
import Database.LSMTree.Internal.Run (Run, mkBlobRefForRun)
50+
import Database.LSMTree.Internal.Run (Run)
51+
import qualified Database.LSMTree.Internal.Run as Run
5152
import Database.LSMTree.Internal.Serialise
5253
import qualified Database.LSMTree.Internal.Vector as V
5354
import qualified Database.LSMTree.Internal.WriteBuffer as WB
@@ -245,8 +246,7 @@ intraPageLookups !resolveV !wb !wbblobs !rs !ks !rkixs !ioops !ioress = do
245246
res <- VM.generateM (V.length ks) $ \ki ->
246247
case WB.lookup wb (V.unsafeIndex ks ki) of
247248
Nothing -> pure Nothing
248-
Just e -> pure $! Just $!
249-
fmap (WeakBlobRef . WBB.mkBlobRef wbblobs) e
249+
Just e -> pure $! Just $! fmap (WBB.mkWeakBlobRef wbblobs) e
250250
-- TODO: ^^ we should be able to avoid this allocation by
251251
-- combining the conversion with other later conversions.
252252
loop res 0
@@ -275,8 +275,7 @@ intraPageLookups !resolveV !wb !wbblobs !rs !ks !rkixs !ioops !ioress = do
275275
-- Laziness ensures that we only compute the forcing of the value in
276276
-- the entry when the result is needed.
277277
LookupEntry e -> do
278-
let e' = bimap copySerialisedValue
279-
(WeakBlobRef . mkBlobRefForRun r) e
278+
let e' = bimap copySerialisedValue (Run.mkWeakBlobRef r) e
280279
-- TODO: ^^ we should be able to avoid this allocation by
281280
-- combining the conversion with other later conversions.
282281
V.unsafeInsertWithMStrict res (combine resolveV) kix e'
@@ -289,7 +288,7 @@ intraPageLookups !resolveV !wb !wbblobs !rs !ks !rkixs !ioops !ioress = do
289288
(unBufferOffset (ioopBufferOffset ioop) + 4096)
290289
(fromIntegral m)
291290
buf)
292-
e' = bimap v' (WeakBlobRef . mkBlobRefForRun r) e
291+
e' = bimap v' (Run.mkWeakBlobRef r) e
293292
V.unsafeInsertWithMStrict res (combine resolveV) kix e'
294293
loop res (ioopix + 1)
295294

src/Database/LSMTree/Internal/Run.hs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ module Database.LSMTree.Internal.Run (
4545
, addReference
4646
, removeReference
4747
, removeReferenceN
48-
, mkBlobRefForRun
48+
, mkRawBlobRef
49+
, mkWeakBlobRef
4950
-- ** Run creation
5051
, fromMutable
5152
, fromWriteBuffer
@@ -68,7 +69,7 @@ import Data.Foldable (for_)
6869
import Data.Word (Word64)
6970
import Database.LSMTree.Internal.BlobFile hiding (removeReference)
7071
import qualified Database.LSMTree.Internal.BlobFile as BlobFile
71-
import Database.LSMTree.Internal.BlobRef hiding (removeReference)
72+
import Database.LSMTree.Internal.BlobRef (RawBlobRef (..), WeakBlobRef (..))
7273
import Database.LSMTree.Internal.BloomFilter (bloomFilterFromSBS)
7374
import qualified Database.LSMTree.Internal.CRC32C as CRC
7475
import Database.LSMTree.Internal.Entry (NumEntries (..))
@@ -143,14 +144,22 @@ removeReference r = RC.removeReference (runRefCounter r)
143144
removeReferenceN :: (PrimMonad m, MonadMask m) => Run m h -> Word64 -> m ()
144145
removeReferenceN r = RC.removeReferenceN (runRefCounter r)
145146

146-
-- | Helper function to make a 'BlobRef' that points into a 'Run'.
147-
mkBlobRefForRun :: Run m h -> BlobSpan -> RawBlobRef m h
148-
mkBlobRefForRun Run{runBlobFile} blobspan =
147+
-- | Helper function to make a 'WeakBlobRef' that points into a 'Run'.
148+
mkRawBlobRef :: Run m h -> BlobSpan -> RawBlobRef m h
149+
mkRawBlobRef Run{runBlobFile} blobspan =
149150
RawBlobRef {
150151
rawBlobRefFile = runBlobFile,
151152
rawBlobRefSpan = blobspan
152153
}
153154

155+
-- | Helper function to make a 'WeakBlobRef' that points into a 'Run'.
156+
mkWeakBlobRef :: Run m h -> BlobSpan -> WeakBlobRef m h
157+
mkWeakBlobRef Run{runBlobFile} blobspan =
158+
WeakBlobRef {
159+
weakBlobRefFile = runBlobFile,
160+
weakBlobRefSpan = blobspan
161+
}
162+
154163
{-# SPECIALISE close ::
155164
Run IO h
156165
-> IO () #-}
@@ -250,7 +259,7 @@ fromWriteBuffer ::
250259
fromWriteBuffer fs hbio caching alloc fsPaths buffer blobs = do
251260
builder <- Builder.new fs hbio fsPaths (WB.numEntries buffer) alloc
252261
for_ (WB.toList buffer) $ \(k, e) ->
253-
Builder.addKeyOp builder k (fmap (WBB.mkBlobRef blobs) e)
262+
Builder.addKeyOp builder k (fmap (WBB.mkRawBlobRef blobs) e)
254263
--TODO: the fmap entry here reallocates even when there are no blobs
255264
fromMutable caching (RefCount 1) builder
256265

src/Database/LSMTree/Internal/RunBuilder.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ copyBlob ::
283283
-> RawBlobRef m h
284284
-> m BlobSpan
285285
copyBlob builder@RunBuilder {..} blobref = do
286-
blob <- BlobRef.readBlob runBuilderHasFS blobref
286+
blob <- BlobRef.readRawBlobRef runBuilderHasFS blobref
287287
writeBlob builder blob
288288

289289
{-# SPECIALISE writeFilter ::

src/Database/LSMTree/Internal/RunReader.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,14 +246,14 @@ next reader@RunReader {..} = do
246246
go 0 p -- try again on the new page
247247
IndexEntry key entry -> do
248248
modifyPrimVar readerCurrentEntryNo (+1)
249-
let entry' = fmap (Run.mkBlobRefForRun readerRun) entry
249+
let entry' = fmap (Run.mkRawBlobRef readerRun) entry
250250
let rawEntry = Entry entry'
251251
return (ReadEntry key rawEntry)
252252
IndexEntryOverflow key entry lenSuffix -> do
253253
-- TODO: we know that we need the next page, could already load?
254254
modifyPrimVar readerCurrentEntryNo (+1)
255255
let entry' :: E.Entry SerialisedValue (RawBlobRef m h)
256-
entry' = fmap (Run.mkBlobRefForRun readerRun) entry
256+
entry' = fmap (Run.mkRawBlobRef readerRun) entry
257257
overflowPages <- readOverflowPages readerHasFS readerKOpsHandle lenSuffix
258258
let rawEntry = mkEntryOverflow entry' page lenSuffix overflowPages
259259
return (ReadEntry key rawEntry)

src/Database/LSMTree/Internal/RunReaders.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ new !offsetKey wbs runs = do
132132
-> m (Maybe (ReadCtx m h))
133133
fromWB wb wbblobs = do
134134
--TODO: this BlobSpan to BlobRef conversion involves quite a lot of allocation
135-
kops <- newMutVar $ map (fmap (fmap (WB.mkBlobRef wbblobs))) $
135+
kops <- newMutVar $ map (fmap (fmap (WB.mkRawBlobRef wbblobs))) $
136136
Map.toList $ filterWB $ WB.toMap wb
137137
nextReadCtx (ReaderNumber 0) (ReadBuffer kops)
138138
where

0 commit comments

Comments
 (0)