Skip to content

Commit 0ecbdc0

Browse files
committed
fix: apply fixes based on PR feedback
1 parent 56b4767 commit 0ecbdc0

File tree

6 files changed

+57
-48
lines changed

6 files changed

+57
-48
lines changed

src/Database/LSMTree/Internal/Snapshot.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,16 +97,16 @@ newtype SnapshotLabel = SnapshotLabel Text
9797
-- TODO: revisit if we need three table types.
9898
data SnapshotTableType
9999
= SnapSimpleTable
100+
| SnapFullTable
100101
| SnapNormalTable
101102
| SnapMonoidalTable
102-
| SnapFullTable
103103
deriving stock (Eq, Show)
104104

105105
instance NFData SnapshotTableType where
106106
rnf SnapSimpleTable = ()
107+
rnf SnapFullTable = ()
107108
rnf SnapNormalTable = ()
108109
rnf SnapMonoidalTable = ()
109-
rnf SnapFullTable = ()
110110

111111
data SnapshotMetaData = SnapshotMetaData {
112112
-- | See 'SnapshotLabel'.

src/Database/LSMTree/Internal/Snapshot/Codec.hs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -247,19 +247,19 @@ instance DecodeVersioned SnapshotLabel where
247247
-- TableType
248248

249249
instance Encode SnapshotTableType where
250-
encode SnapNormalTable = encodeWord 0
251-
encode SnapMonoidalTable = encodeWord 1
252-
encode SnapFullTable = encodeWord 2
253-
encode SnapSimpleTable = encodeWord 3
250+
encode SnapSimpleTable = encodeWord 0
251+
encode SnapFullTable = encodeWord 1
252+
encode SnapNormalTable = encodeWord 2
253+
encode SnapMonoidalTable = encodeWord 3
254254

255255
instance DecodeVersioned SnapshotTableType where
256256
decodeVersioned V0 = do
257257
tag <- decodeWord
258258
case tag of
259-
0 -> pure SnapNormalTable
260-
1 -> pure SnapMonoidalTable
261-
2 -> pure SnapFullTable
262-
3 -> pure SnapSimpleTable
259+
0 -> pure SnapSimpleTable
260+
1 -> pure SnapFullTable
261+
2 -> pure SnapNormalTable
262+
3 -> pure SnapMonoidalTable
263263
_ -> fail ("[SnapshotTableType] Unexpected tag: " <> show tag)
264264

265265
instance Encode SnapshotRun where

src/Database/LSMTree/Simple.hs

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
-- TODO: Disable this warning once LSMTreeError is split.
2-
{-# OPTIONS_GHC -Wno-incomplete-patterns #-}
3-
41
{- |
52
Module : Database.LSMTree.Simple
63
Copyright : (c) 2023, Input Output Global, Inc. (IOG)
@@ -126,7 +123,7 @@ module Database.LSMTree.Simple (
126123
TableClosedError (..),
127124
TableCorruptedError (..),
128125
TableTooLargeError (..),
129-
TableNotCompatibleError (..),
126+
TableUnionNotCompatibleError (..),
130127
SnapshotExistsError (..),
131128
SnapshotDoesNotExistError (..),
132129
SnapshotCorruptedError (..),
@@ -137,7 +134,7 @@ module Database.LSMTree.Simple (
137134
) where
138135

139136
import Control.Exception.Base (Exception, SomeException (..), bracket,
140-
mapException)
137+
mapException, assert)
141138
import Control.Monad (join)
142139
import Data.Bifunctor (Bifunctor (..))
143140
import Data.Kind (Type)
@@ -150,7 +147,7 @@ import Database.LSMTree.Internal (BlobRefInvalidError (..),
150147
SnapshotCorruptedError (..),
151148
SnapshotDoesNotExistError (..), SnapshotExistsError (..),
152149
SnapshotNotCompatibleError (..), TableClosedError (..),
153-
TableCorruptedError (..), TableNotCompatibleError (..),
150+
TableCorruptedError (..), TableUnionNotCompatibleError (..),
154151
TableTooLargeError (..))
155152
import qualified Database.LSMTree.Internal as Internal
156153
import Database.LSMTree.Internal.Config
@@ -238,10 +235,10 @@ To prevent resource and memory leaks due to asynchronous exceptions,
238235
it is recommended to use the [bracketed](#bracketed) functions whenever
239236
possible, and otherwise:
240237
241-
* Run functions that allocate, use, and release a resource with asynchronous
238+
* Run functions that allocate and release a resource with asynchronous
242239
exceptions masked.
243-
* Pair functions that allocate a resource with a masked cleanup function,
244-
e.g., using 'bracket'.
240+
* Ensure that every use allocate operation is followed by the corresponding release
241+
operation even in the presence of asynchronous exceptions, e.g., using 'bracket'.
245242
-}
246243

247244
--------------------------------------------------------------------------------
@@ -269,12 +266,18 @@ the table by using 'duplicate' and performing the read operations on the duplica
269266
However, this requires that the 'duplicate' operation /happens before/ the subsequent
270267
writes, as it is a race to duplicate concurrently with any writes.
271268
As this package does not provide any construct for synchronisation or atomic
272-
operations, this ordering of operations must be accomplished by other means.
269+
operations, this ordering of operations must be accomplished by the user through
270+
other means.
273271
274-
An 'Cursor' creates a stable view of a table and can safely be read while
272+
A 'Cursor' creates a stable view of a table and can safely be read while
275273
modifying the original table. However, reading the 'next' key\/value pair from
276274
a cursor locks the view, so concurrent reads on the same cursor block.
277275
This is because 'next' updates the cursor's current position.
276+
277+
Session handles may be used concurrently from multiple Haskell threads,
278+
but concurrent use of read and write operations may introduce races.
279+
Specifically, it is a race to use `listSnapshots` and `deleteSnapshots`
280+
with the same session handle concurrently.
278281
-}
279282

280283
--------------------------------------------------------------------------------
@@ -314,11 +317,11 @@ newtype Session = Session {unSession :: Internal.Session IO HandleIO}
314317
{- |
315318
Throws the following exceptions:
316319
317-
['SessionDoesNotExistError']:
320+
['SessionDirDoesNotExistError']:
318321
If the session directory does not exist.
319-
['SessionLockedError']:
322+
['SessionDirLockedError']:
320323
If the session directory is locked by another process.
321-
['SessionCorruptedError']:
324+
['SessionDirCorruptedError']:
322325
If the session directory is malformed.
323326
-}
324327
withSession ::
@@ -338,11 +341,11 @@ withSession dir action = do
338341
{- |
339342
Throws the following exceptions:
340343
341-
['SessionDoesNotExistError']:
344+
['SessionDirDoesNotExistError']:
342345
If the session directory does not exist.
343-
['SessionLockedError']:
346+
['SessionDirLockedError']:
344347
If the session directory is locked by another process.
345-
['SessionCorruptedError']:
348+
['SessionDirCorruptedError']:
346349
If the session directory is malformed.
347350
-}
348351
openSession ::
@@ -365,9 +368,7 @@ closeSession = Internal.closeSession . unSession
365368
-- Tables
366369
--------------------------------------------------------------------------------
367370

368-
{- | A table is a handle to an LSM-tree key\/value store.
369-
370-
Each table is a handle to an individual key\/value store with both in-memory and on-disk parts.
371+
{- | A table is a handle to an individual LSM-tree key\/value store with both in-memory and on-disk parts.
371372
372373
__Warning:__ Tables are ephemeral. Once you close a table, its data is lost forever. To persist tables, use [snapshots](#g:snapshots).
373374
-}
@@ -382,7 +383,7 @@ data Table k v
382383
383384
This function is exception-safe for both synchronous and asynchronous exceptions.
384385
385-
It is recommended to use this function instead of 'new' and 'closeTable'.
386+
It is recommended to use this function instead of 'newTable' and 'closeTable'.
386387
387388
Throws the following exceptions:
388389
@@ -540,8 +541,9 @@ rangeLookup ::
540541
Range k ->
541542
IO (Vector (k, v))
542543
rangeLookup (Table table) range =
543-
Internal.rangeLookup const (Internal.serialiseKey <$> range) table $ \k v !_b ->
544-
(Internal.deserialiseKey k, Internal.deserialiseValue v)
544+
Internal.rangeLookup const (Internal.serialiseKey <$> range) table $ \ !k !v !b ->
545+
assert (null b) $
546+
(Internal.deserialiseKey k, Internal.deserialiseValue v)
545547

546548
--------------------------------------------------------------------------------
547549
-- Updates
@@ -672,6 +674,9 @@ withDuplicate table =
672674

673675
{- | Duplicate a table.
674676
677+
The duplicate is an independent copy of the given table.
678+
The duplicate is unaffected by subsequent updates to the given table and vice versa.
679+
675680
__Warning:__ The duplicate must be independently closed using 'closeTable'.
676681
677682
Throws the following exceptions:
@@ -697,16 +702,16 @@ This function is exception-safe for both synchronous and asynchronous exceptions
697702
698703
It is recommended to use this function instead of 'union' and 'closeTable'.
699704
700-
__Warning:__ Both input tables must be from the same 'Session' and have the same configuration parameters.
705+
__Warning:__ Both input tables must be from the same 'Session'.
701706
702707
Throws the following exceptions:
703708
704709
['SessionClosedError']:
705710
If the session is closed.
706711
['TableClosedError']:
707712
If the table is closed.
708-
['TableNotCompatibleError']:
709-
If both tables are not from the same 'Session' or have different configuration parameters.
713+
['TableUnionNotCompatibleError']:
714+
If both tables are not from the same 'Session'.
710715
-}
711716
withUnion ::
712717
Table k v ->
@@ -726,18 +731,20 @@ withUnions tables =
726731

727732
{- | Create a table that contains the union of the entries of the given tables.
728733
734+
The union of two tables is left-biased.
735+
729736
__Warning:__ The new table must be independently closed using 'closeTable'.
730737
731-
__Warning:__ Both input tables must be from the same 'Session' and have the same configuration parameters.
738+
__Warning:__ Both input tables must be from the same 'Session'.
732739
733740
Throws the following exceptions:
734741
735742
['SessionClosedError']:
736743
If the session is closed.
737744
['TableClosedError']:
738745
If the table is closed.
739-
['TableNotCompatibleError']:
740-
If both tables are not from the same 'Session' or have different configuration parameters.
746+
['TableUnionNotCompatibleError']:
747+
If both tables are not from the same 'Session'.
741748
-}
742749
union ::
743750
Table k v ->
@@ -1008,10 +1015,11 @@ next iterator = do
10081015
{- | Read the next batch of table entries from the cursor.
10091016
10101017
The size of the batch is /at most/ equal to the given number, but may contain fewer entries.
1018+
In practice, this occurs only when the cursor reaches the end of the table.
10111019
10121020
The following property holds:
10131021
1014-
prop> take n cursor = catMaybes <$> sequence (replicate n (next cursor))
1022+
prop> take n cursor = catMaybes <$> replicateM n (next cursor)
10151023
10161024
Throws the following exceptions:
10171025
@@ -1025,8 +1033,9 @@ take ::
10251033
Cursor k v ->
10261034
IO (Vector (k, v))
10271035
take n (Cursor cursor) =
1028-
Internal.readCursor const n cursor $ \ !k !v !_b ->
1029-
(Internal.deserialiseKey k, Internal.deserialiseValue v)
1036+
Internal.readCursor const n cursor $ \ !k !v !b ->
1037+
assert (null b) $
1038+
(Internal.deserialiseKey k, Internal.deserialiseValue v)
10301039

10311040
{- | Variant of 'take' that accepts an additional predicate to determine whether or not to continue reading.
10321041
@@ -1048,6 +1057,7 @@ takeWhile ::
10481057
Cursor k v ->
10491058
IO (Vector (k, v))
10501059
takeWhile n p (Cursor cursor) =
1060+
-- TODO: Rewrite to use a variant of 'readCursorWhile' that works without the maximum batch size.
10511061
Internal.readCursorWhile const (p . Internal.deserialiseKey) n cursor $ \ !k !v !_b ->
10521062
(Internal.deserialiseKey k, Internal.deserialiseValue v)
10531063

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,7 @@ instance Arbitrary SnapshotLabel where
221221
shrink (SnapshotLabel txt) = SnapshotLabel <$> shrink txt
222222

223223
instance Arbitrary SnapshotTableType where
224-
-- TODO: add SnapSimpleTable and SnapFullTable
225-
arbitrary = elements [SnapNormalTable, SnapMonoidalTable]
224+
arbitrary = elements [SnapSimpleTable, SnapFullTable, SnapNormalTable, SnapMonoidalTable]
226225
shrink _ = []
227226

228227
instance Arbitrary SnapshotRun where

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,10 @@ enumerateSnapshotLabel =
193193

194194
enumerateSnapshotTableType :: [(ComponentAnnotation, SnapshotTableType)]
195195
enumerateSnapshotTableType =
196-
-- TODO: add SnapSimpleTable
197-
[ ("N0", SnapNormalTable)
198-
, ("N1", SnapMonoidalTable)
199-
, ("N2", SnapFullTable)
196+
[ ("N0", SnapSimpleTable)
197+
, ("N1", SnapFullTable)
198+
, ("N2", SnapNormalTable)
199+
, ("N3", SnapMonoidalTable)
200200
]
201201

202202
enumerateTableConfig :: [(ComponentAnnotation, TableConfig)]
49 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)