Skip to content

Commit 508d499

Browse files
authored
Merge pull request #623 from IntersectMBO/jdral/ordinary-index-nothunks
Run `Class`-based and `StateMachine` tests using both index types
2 parents 6c219ca + 766d172 commit 508d499

File tree

8 files changed

+105
-54
lines changed

8 files changed

+105
-54
lines changed

bench/micro/Bench/Database/LSMTree/Internal/Index.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ searchBenchmarkable index = whnf $ foldl' (\ _ key -> rnf (search key index)) ()
112112
-- ** Incremental construction
113113

114114
-- | Constructs append operations to be used in index construction.
115-
incrementalConstructionAppends
116-
:: Int -- ^ Number of keys used in the construction
115+
incrementalConstructionAppends ::
116+
Int -- ^ Number of keys used in the construction
117117
-> [Append] -- ^ Constructed append operations
118118
incrementalConstructionAppends = appendsForIndexCompact
119119

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

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -461,9 +461,31 @@ deriving anyclass instance Typeable s
461461
GrowingVector
462462
-------------------------------------------------------------------------------}
463463

464-
deriving stock instance Generic (GrowingVector s a)
465-
deriving anyclass instance (Typeable s, Typeable a, NoThunks a)
466-
=> NoThunks (GrowingVector s a)
464+
instance (NoThunks a, Typeable s, Typeable a) => NoThunks (GrowingVector s a) where
465+
showTypeOf (p :: Proxy (GrowingVector s a)) = show $ typeRep p
466+
wNoThunks ctx
467+
(GrowingVector (a :: STRef s (VM.MVector s a)) (b :: PrimVar s Int))
468+
= allNoThunks [
469+
noThunks ctx b
470+
-- Check that the STRef is in WHNF
471+
, noThunks ctx $ OnlyCheckWhnf a
472+
-- Check that the MVector is in WHNF
473+
, do
474+
mvec <- unsafeSTToIO $ readSTRef a
475+
noThunks ctx' $ OnlyCheckWhnf mvec
476+
-- Check that the vector elements contain no thunks. The vector
477+
-- contains undefined elements after the first @n@ elements
478+
, do
479+
n <- unsafeSTToIO $ readPrimVar b
480+
mvec <- unsafeSTToIO $ readSTRef a
481+
allNoThunks [
482+
unsafeSTToIO (VM.read mvec i) >>= \x -> noThunks ctx'' x
483+
| i <- [0..n-1]
484+
]
485+
]
486+
where
487+
ctx' = showTypeOf (Proxy @(STRef s (VM.MVector s a))) : ctx
488+
ctx'' = showTypeOf (Proxy @(VM.MVector s a)) : ctx'
467489

468490
{-------------------------------------------------------------------------------
469491
Baler
@@ -663,15 +685,21 @@ instance (NoThunks a, Typeable s, Typeable a) => NoThunks (MutableHeap s a) wher
663685
(MH (a :: PrimVar s Int) (b :: SmallMutableArray s a))
664686
= allNoThunks [
665687
noThunks ctx a
666-
-- the small array may contain bogus/undefined placeholder values
667-
-- after the first @n@ elements in the heap
668-
, noThunks ctx $! do
688+
-- Check that the array is in WHNF
689+
, noThunks ctx (OnlyCheckWhnf b)
690+
-- Check that the array elements contain no thunks. The small array
691+
-- may contain undefined placeholder values after the first @n@
692+
-- elements in the array. The very first element of the array can also
693+
-- be undefined.
694+
, do
669695
n <- unsafeSTToIO (readPrimVar a)
670696
allNoThunks [
671-
unsafeSTToIO (readSmallArray b i) >>= \x -> noThunks ctx x
672-
| i <- [0..n-1]
697+
unsafeSTToIO (readSmallArray b i) >>= \x -> noThunks ctx' x
698+
| i <- [1..n-1]
673699
]
674700
]
701+
where
702+
ctx' = showTypeOf (Proxy @(SmallMutableArray s a)) : ctx
675703

676704
{-------------------------------------------------------------------------------
677705
IOLike

src/Database/LSMTree/Internal/Index.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ fromSBS Ordinary input = second OrdinaryIndex <$> Ordinary.fromSBS input
154154
Incremental index construction is only guaranteed to work correctly when the
155155
supplied key ranges do not overlap and are given in ascending order.
156156
-}
157-
data IndexAcc s = CompactIndexAcc (IndexCompactAcc s)
158-
| OrdinaryIndexAcc (IndexOrdinaryAcc s)
157+
data IndexAcc s = CompactIndexAcc !(IndexCompactAcc s)
158+
| OrdinaryIndexAcc !(IndexOrdinaryAcc s)
159159

160160
-- | Create a new index accumulator, using a default configuration.
161161
newWithDefaults :: IndexType -> ST s (IndexAcc s)

src/Database/LSMTree/Internal/Index/Ordinary.hs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,17 @@ toLastKeys (IndexOrdinary lastKeys) = lastKeys
8080
type-agnostic version]('Database.LSMTree.Internal.Index.search').
8181
-}
8282
search :: SerialisedKey -> IndexOrdinary -> PageSpan
83-
search key (IndexOrdinary lastKeys) = assert (pageCount > 0) result where
83+
search key (IndexOrdinary lastKeys)
84+
-- TODO: ideally, we could assert that an index is never empty, but
85+
-- unfortunately we can not currently do this. Runs (and thefeore indexes)
86+
-- /can/ be empty if they were created by a last-level merge where all input
87+
-- entries were deletes. Other parts of the @lsm-tree@ code won't fail as long
88+
-- as we return @PageSpan 0 0@ when we search an empty ordinary index. The
89+
-- ideal fix would be to remove empty runs from the levels entirely, but this
90+
-- requires more involved changes to the merge schedule and until then we'll
91+
-- just hack the @pageCount <= 0@ case in.
92+
| pageCount <= 0 = PageSpan (PageNo 0) (PageNo 0)
93+
| otherwise = assert (pageCount > 0) result where
8494

8595
protoStart :: Int
8696
!protoStart = binarySearchL lastKeys key
@@ -224,7 +234,7 @@ fromSBS shortByteString@(SBS unliftedByteArray)
224234
= Primitive.splitAt firstSize postFirstSizeBytes
225235

226236
first :: SerialisedKey
227-
first = SerialisedKey' (Primitive.force firstBytes)
237+
!first = SerialisedKey' (Primitive.force firstBytes)
228238

229239
others <- lastKeys othersBytes
230240
return (first : others)

src/Database/LSMTree/Internal/Vector/Growing.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ append (GrowingVector bufferRef lengthRef) count val
7070
length <- readPrimVar lengthRef
7171
makeRoom
7272
buffer' <- readSTRef bufferRef
73-
Mutable.set (Mutable.slice length count buffer') val
73+
Mutable.set (Mutable.slice length count buffer') $! val
7474
where
7575

7676
makeRoom :: ST s ()

test/Test/Database/LSMTree/Class.hs

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import Database.LSMTree.Common (mkSnapshotName)
2424
import Database.LSMTree.Extras.Generators ()
2525
import qualified Database.LSMTree.Model.IO as ModelIO
2626
import qualified System.FS.API as FS
27+
import Test.Database.LSMTree.StateMachine ()
2728
import Test.QuickCheck.Monadic (monadicIO, monitor, run)
2829
import Test.Tasty (TestName, TestTree, testGroup)
2930
import qualified Test.Tasty.QuickCheck as QC
@@ -36,19 +37,17 @@ tests = testGroup "Test.Database.LSMTree.Class"
3637
, testGroup "Real" $ zipWith ($) (props tbl2) expectFailures2
3738
]
3839
where
39-
tbl1 :: Proxy ModelIO.Table
40-
tbl1 = Setup {
41-
testTableConfig = ModelIO.TableConfig
40+
tbl1 :: RunSetup ModelIO.Table IO
41+
tbl1 = RunSetup $ \conf -> Setup {
42+
testTableConfig = conf
4243
, testWithSessionArgs = \action -> action ModelIO.NoSessionArgs
4344
}
4445

4546
expectFailures1 = repeat False
4647

47-
tbl2 :: Proxy R.Table
48-
tbl2 = Setup {
49-
testTableConfig = R.defaultTableConfig {
50-
R.confWriteBufferAlloc = R.AllocNumEntries (R.NumEntries 3)
51-
}
48+
tbl2 :: RunSetup R.Table IO
49+
tbl2 = RunSetup $ \conf -> Setup {
50+
testTableConfig = conf
5251
, testWithSessionArgs = \action ->
5352
FS.withTempIOHasBlockIO "R" $ \hfs hbio ->
5453
action (SessionArgs hfs hbio (FS.mkFsPath []))
@@ -83,33 +82,33 @@ tests = testGroup "Test.Database.LSMTree.Class"
8382
, True -- merge
8483
] ++ repeat False
8584

86-
props tbl =
87-
[ testProperty' "lookup-insert" $ prop_lookupInsert tbl
88-
, testProperty' "lookup-insert-else" $ prop_lookupInsertElse tbl
89-
, testProperty' "lookup-insert-blob" $ prop_lookupInsertBlob tbl
90-
, testProperty' "lookup-delete" $ prop_lookupDelete tbl
91-
, testProperty' "lookup-delete-else" $ prop_lookupDeleteElse tbl
92-
, testProperty' "insert-insert" $ prop_insertInsert tbl
93-
, testProperty' "insert-insert-blob" $ prop_insertInsertBlob tbl
94-
, testProperty' "insert-commutes" $ prop_insertCommutes tbl
95-
, testProperty' "insert-commutes-blob" $ prop_insertCommutesBlob tbl
96-
, testProperty' "invalidated-blob-references" $ prop_updatesMayInvalidateBlobRefs tbl
97-
, testProperty' "dup-insert-insert" $ prop_dupInsertInsert tbl
98-
, testProperty' "dup-insert-comm" $ prop_dupInsertCommutes tbl
99-
, testProperty' "dup-nochanges" $ prop_dupNoChanges tbl
100-
, testProperty' "lookupRange-like-lookups" $ prop_lookupRangeLikeLookups tbl
101-
, testProperty' "lookupRange-insert" $ prop_insertLookupRange tbl
102-
, testProperty' "readCursor-sorted" $ prop_readCursorSorted tbl
103-
, testProperty' "readCursor-num-results" $ prop_readCursorNumResults tbl
104-
, testProperty' "readCursor-insert" $ prop_readCursorInsert tbl
105-
, testProperty' "readCursor-delete" $ prop_readCursorDelete tbl
106-
, testProperty' "readCursor-delete-else" $ prop_readCursorDeleteElse tbl
107-
, testProperty' "readCursor-stable-view" $ prop_readCursorStableView tbl
108-
, testProperty' "readCursor-offset" $ prop_readCursorOffset tbl
109-
, testProperty' "snapshot-nochanges" $ prop_snapshotNoChanges tbl
110-
, testProperty' "snapshot-nochanges2" $ prop_snapshotNoChanges2 tbl
111-
, testProperty' "lookup-mupsert" $ prop_lookupUpdate tbl
112-
, testProperty' "union" $ prop_union tbl
85+
props RunSetup {..} =
86+
[ testProperty' "lookup-insert" $ prop_lookupInsert . runSetup
87+
, testProperty' "lookup-insert-else" $ prop_lookupInsertElse . runSetup
88+
, testProperty' "lookup-insert-blob" $ prop_lookupInsertBlob . runSetup
89+
, testProperty' "lookup-delete" $ prop_lookupDelete . runSetup
90+
, testProperty' "lookup-delete-else" $ prop_lookupDeleteElse . runSetup
91+
, testProperty' "insert-insert" $ prop_insertInsert . runSetup
92+
, testProperty' "insert-insert-blob" $ prop_insertInsertBlob . runSetup
93+
, testProperty' "insert-commutes" $ prop_insertCommutes . runSetup
94+
, testProperty' "insert-commutes-blob" $ prop_insertCommutesBlob . runSetup
95+
, testProperty' "invalidated-blob-references" $ prop_updatesMayInvalidateBlobRefs . runSetup
96+
, testProperty' "dup-insert-insert" $ prop_dupInsertInsert . runSetup
97+
, testProperty' "dup-insert-comm" $ prop_dupInsertCommutes . runSetup
98+
, testProperty' "dup-nochanges" $ prop_dupNoChanges . runSetup
99+
, testProperty' "lookupRange-like-lookups" $ prop_lookupRangeLikeLookups . runSetup
100+
, testProperty' "lookupRange-insert" $ prop_insertLookupRange . runSetup
101+
, testProperty' "readCursor-sorted" $ prop_readCursorSorted . runSetup
102+
, testProperty' "readCursor-num-results" $ prop_readCursorNumResults . runSetup
103+
, testProperty' "readCursor-insert" $ prop_readCursorInsert . runSetup
104+
, testProperty' "readCursor-delete" $ prop_readCursorDelete . runSetup
105+
, testProperty' "readCursor-delete-else" $ prop_readCursorDeleteElse . runSetup
106+
, testProperty' "readCursor-stable-view" $ prop_readCursorStableView . runSetup
107+
, testProperty' "readCursor-offset" $ prop_readCursorOffset . runSetup
108+
, testProperty' "snapshot-nochanges" $ prop_snapshotNoChanges . runSetup
109+
, testProperty' "snapshot-nochanges2" $ prop_snapshotNoChanges2 . runSetup
110+
, testProperty' "lookup-mupsert" $ prop_lookupUpdate . runSetup
111+
, testProperty' "union" $ prop_union . runSetup
113112
]
114113

115114
testProperty' :: forall a. Testable a => TestName -> a -> Bool -> TestTree
@@ -138,6 +137,10 @@ label = SnapshotLabel "Word64 ByteString ByteString"
138137

139138
type Proxy h = Setup h IO
140139

140+
newtype RunSetup h m = RunSetup {
141+
runSetup :: TableConfig h -> Setup h m
142+
}
143+
141144
data Setup h m = Setup {
142145
testTableConfig :: TableConfig h
143146
, testWithSessionArgs :: forall a. (SessionArgs (Session h) m -> m a) -> m a

test/Test/Database/LSMTree/Internal/Snapshot/Codec/Golden.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ handleOutputFiles :: TestTree -> TestTree
5555
handleOutputFiles = Tasty.localOption Au.OnPass
5656

5757
-- | Internally, the function will infer the correct filepath names.
58-
snapshotCodecTest
59-
:: String -- ^ Name of the test
58+
snapshotCodecTest ::
59+
String -- ^ Name of the test
6060
-> SnapshotMetaData -- ^ Data to be serialized
6161
-> TestTree
6262
snapshotCodecTest name datum =

test/Test/Database/LSMTree/StateMachine.hs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,22 +223,25 @@ instance Arbitrary R.TableConfig where
223223
, (4, pure R.Incremental)
224224
]
225225
confWriteBufferAlloc <- QC.arbitrary
226+
confFencePointerIndex <- QC.arbitrary
226227
pure $ R.TableConfig {
227228
R.confMergePolicy = R.MergePolicyLazyLevelling
228229
, R.confSizeRatio = R.Four
229230
, confWriteBufferAlloc
230231
, R.confBloomFilterAlloc = R.AllocFixed 10
231-
, R.confFencePointerIndex = R.CompactIndex
232+
, confFencePointerIndex
232233
, R.confDiskCachePolicy = R.DiskCacheNone
233234
, confMergeSchedule
234235
}
235236

236237
shrink R.TableConfig{..} =
237238
[ R.TableConfig {
238239
confWriteBufferAlloc = confWriteBufferAlloc'
240+
, confFencePointerIndex = confFencePointerIndex'
239241
, ..
240242
}
241-
| confWriteBufferAlloc' <- QC.shrink confWriteBufferAlloc
243+
| ( confWriteBufferAlloc', confFencePointerIndex')
244+
<- QC.shrink (confWriteBufferAlloc, confFencePointerIndex)
242245
]
243246

244247
-- TODO: the current generator is suboptimal, and should be improved. There are
@@ -273,6 +276,13 @@ instance Arbitrary R.WriteBufferAlloc where
273276
| QC.Positive x' <- QC.shrink (QC.Positive x)
274277
]
275278

279+
deriving stock instance Enum R.FencePointerIndex
280+
deriving stock instance Bounded R.FencePointerIndex
281+
instance Arbitrary R.FencePointerIndex where
282+
arbitrary = QC.arbitraryBoundedEnum
283+
shrink R.OrdinaryIndex = []
284+
shrink R.CompactIndex = [R.OrdinaryIndex]
285+
276286
propLockstep_RealImpl_RealFS_IO ::
277287
Tracer IO R.LSMTreeTrace
278288
-> Actions (Lockstep (ModelState R.Table))

0 commit comments

Comments
 (0)