Skip to content

Commit e256e9b

Browse files
authored
Merge pull request #489 from IntersectMBO/jdral/n-way-unions
Add n-way table unions
2 parents 5f4e933 + ad5d621 commit e256e9b

File tree

9 files changed

+207
-26
lines changed

9 files changed

+207
-26
lines changed

src/Database/LSMTree.hs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ module Database.LSMTree (
8080

8181
-- * Table union
8282
, union
83+
, unions
8384

8485
-- * Serialisation
8586
, SerialiseKey
@@ -530,6 +531,16 @@ union :: forall m k v b.
530531
-> m (Table m k v b)
531532
union = error "union: not yet implemented" $ union @m @k @v @b
532533

534+
{-# SPECIALISE unions ::
535+
ResolveValue v
536+
=> V.Vector (Table IO k v b)
537+
-> IO (Table IO k v b) #-}
538+
unions :: forall m k v b.
539+
(IOLike m, ResolveValue v)
540+
=> V.Vector (Table m k v b)
541+
-> m (Table m k v b)
542+
unions = error "unions: not yet implemented" $ unions @m @k @v
543+
533544
{-------------------------------------------------------------------------------
534545
Monoidal value resolution
535546
-------------------------------------------------------------------------------}

src/Database/LSMTree/Monoidal.hs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ module Database.LSMTree.Monoidal (
9797

9898
-- * Table union
9999
, union
100+
, unions
100101

101102
-- * Concurrency
102103
-- $concurrency
@@ -672,6 +673,24 @@ union :: forall m k v.
672673
-> m (Table m k v)
673674
union = error "union: not yet implemented" $ union @m @k @v
674675

676+
{-# SPECIALISE unions ::
677+
ResolveValue v
678+
=> V.Vector (Table IO k v)
679+
-> IO (Table IO k v) #-}
680+
-- | Like 'union', but for @n@ tables.
681+
--
682+
-- A good mental model of this operation is @'Data.Map.Lazy.unionsWith' (<>)@ on
683+
-- @'Data.Map.Lazy.Map' k v@.
684+
--
685+
-- Exceptions:
686+
--
687+
-- * Unioning 0 tables is an exception.
688+
unions :: forall m k v.
689+
(IOLike m, ResolveValue v)
690+
=> V.Vector (Table m k v)
691+
-> m (Table m k v)
692+
unions = error "unions: not yet implemented" $ unions @m @k @v
693+
675694
{-------------------------------------------------------------------------------
676695
Monoidal value resolution
677696
-------------------------------------------------------------------------------}

src/Database/LSMTree/Normal.hs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ module Database.LSMTree.Normal (
9898

9999
-- * Table union
100100
, union
101+
, unions
101102

102103
-- * Concurrency #concurrency#
103104
-- $concurrency
@@ -787,3 +788,18 @@ union :: forall m k v b.
787788
-> Table m k v b
788789
-> m (Table m k v b)
789790
union = error "union: not yet implemented" $ union @m @k @v
791+
792+
{-# SPECIALISE unions :: V.Vector (Table IO k v b) -> IO (Table IO k v b) #-}
793+
-- | Like 'union', but for @n@ tables.
794+
--
795+
-- A good mental model of this operation is @'Data.Map.Lazy.unions'@ on
796+
-- @'Data.Map.Lazy.Map' k v@.
797+
--
798+
-- Exceptions:
799+
--
800+
-- * Unioning 0 tables is an exception.
801+
unions :: forall m k v b.
802+
IOLike m
803+
=> V.Vector (Table m k v b)
804+
-> m (Table m k v b)
805+
unions = error "union: not yet implemented" $ union @m @k @v

test/Database/LSMTree/Class.hs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module Database.LSMTree.Class (
88
, withTableFromSnapshot
99
, withTableDuplicate
1010
, withTableUnion
11+
, withTableUnions
1112
, withCursor
1213
, module Common
1314
, module Types
@@ -162,6 +163,13 @@ class (IsSession (Session h)) => IsTable h where
162163
-> h m k v b
163164
-> m (h m k v b)
164165

166+
unions ::
167+
( IOLike m
168+
, C k v b
169+
)
170+
=> V.Vector (h m k v b)
171+
-> m (h m k v b)
172+
165173
withTableNew :: forall h m k v b a.
166174
(IOLike m, IsTable h, C k v b)
167175
=> Session h m
@@ -194,6 +202,13 @@ withTableUnion :: forall h m k v b a.
194202
-> m a
195203
withTableUnion table1 table2 = bracket (table1 `union` table2) close
196204

205+
withTableUnions :: forall h m k v b a.
206+
(IOLike m, IsTable h, C k v b)
207+
=> V.Vector (h m k v b)
208+
-> (h m k v b -> m a)
209+
-> m a
210+
withTableUnions tables = bracket (unions tables) close
211+
197212
withCursor :: forall h m k v b a.
198213
(IOLike m, IsTable h, C k v b)
199214
=> Maybe k
@@ -232,3 +247,4 @@ instance IsTable R.Table where
232247

233248
duplicate = R.duplicate
234249
union = R.union
250+
unions = R.unions

test/Database/LSMTree/Model/IO.hs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,26 @@ module Database.LSMTree.Model.IO (
1717
import Control.Concurrent.Class.MonadSTM.Strict
1818
import Control.Exception (Exception)
1919
import Control.Monad.Class.MonadThrow (MonadThrow (..))
20+
import qualified Data.Vector as V
2021
import qualified Database.LSMTree.Class as Class
2122
import Database.LSMTree.Model.Session (TableConfig (..))
2223
import qualified Database.LSMTree.Model.Session as Model
2324

2425
newtype Session m = Session (StrictTVar m (Maybe Model.Model))
2526

2627
data Table m k v b = Table {
27-
_thSession :: !(Session m)
28-
, _thTable :: !(Model.Table k v b)
28+
thSession :: !(Session m)
29+
, thTable :: !(Model.Table k v b)
2930
}
3031

3132
data BlobRef m b = BlobRef {
32-
_brSession :: !(Session m)
33-
, _brBlobRef :: !(Model.BlobRef b)
33+
brSession :: !(Session m)
34+
, brBlobRef :: !(Model.BlobRef b)
3435
}
3536

3637
data Cursor m k v b = Cursor {
37-
_cSession :: !(Session m)
38-
, _cCursor :: !(Model.Cursor k v b)
38+
cSession :: !(Session m)
39+
, cCursor :: !(Model.Cursor k v b)
3940
}
4041

4142
newtype Err = Err (Model.Err)
@@ -76,7 +77,7 @@ instance Class.IsTable Table where
7677

7778
rangeLookup (Table s t) x1 = fmap (fmap (BlobRef s)) <$>
7879
runInOpenSession s (Model.rangeLookup x1 t)
79-
retrieveBlobs _ s x1 = runInOpenSession s (Model.retrieveBlobs (fmap _brBlobRef x1))
80+
retrieveBlobs _ s x1 = runInOpenSession s (Model.retrieveBlobs (fmap brBlobRef x1))
8081

8182
newCursor k (Table s t) = Cursor s <$> runInOpenSession s (Model.newCursor k t)
8283
closeCursor _ (Cursor s c) = runInOpenSession s (Model.closeCursor c)
@@ -90,3 +91,8 @@ instance Class.IsTable Table where
9091

9192
union (Table s1 t1) (Table _s2 t2) =
9293
Table s1 <$> runInOpenSession s1 (Model.union Model.getResolve t1 t2)
94+
95+
unions ts =
96+
Table s <$> runInOpenSession s (Model.unions Model.getResolve (V.map thTable ts))
97+
where
98+
Table s _ = V.head ts

test/Database/LSMTree/Model/Session.hs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ module Database.LSMTree.Model.Session (
7373
, duplicate
7474
-- * Table union
7575
, union
76+
, unions
7677
) where
7778

7879
import Control.Monad (when)
@@ -204,6 +205,8 @@ data Err =
204205
| ErrSnapshotWrongType
205206
| ErrBlobRefInvalidated
206207
| ErrCursorClosed
208+
-- | Passed zero tables to 'unions'
209+
| ErrUnionsZeroTables
207210
deriving stock (Show, Eq)
208211

209212
{-------------------------------------------------------------------------------
@@ -628,3 +631,21 @@ union r th1 th2 = do
628631
(_, t1) <- guardTableIsOpen th1
629632
(_, t2) <- guardTableIsOpen th2
630633
newTableWith TableConfig $ Model.union r t1 t2
634+
635+
unions ::
636+
( MonadState Model m
637+
, MonadError Err m
638+
, C k v b
639+
)
640+
=> ResolveSerialisedValue v
641+
-> V.Vector (Table k v b)
642+
-> m (Table k v b)
643+
unions r tables
644+
| n == 0 = throwError ErrUnionsZeroTables
645+
| otherwise = do
646+
tables' <- V.forM tables $ \table -> do
647+
(_, table') <- guardTableIsOpen table
648+
pure table'
649+
newTableWith TableConfig $ Model.unions r tables'
650+
where
651+
n = V.length tables

test/Database/LSMTree/Model/Table.hs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ module Database.LSMTree.Model.Table (
3838
, duplicate
3939
-- * Table union
4040
, union
41+
, unions
4142
-- * Testing
4243
, size
4344
) where
@@ -301,24 +302,22 @@ readCursor n c =
301302
)
302303

303304
{-------------------------------------------------------------------------------
304-
Merging tables
305+
Table union
305306
-------------------------------------------------------------------------------}
306307

307-
-- | Merge full tables, creating a new table.
308-
--
309-
-- NOTE: close tables using 'close' as soon as they are
310-
-- unused.
311-
--
312-
-- Multiple tables of the same type but with different configuration parameters
313-
-- can live in the same session. However, some operations, like
308+
-- | Union two full tables, creating a new table.
314309
union ::
315310
ResolveSerialisedValue v
316311
-> Table k v b
317312
-> Table k v b
318313
-> Table k v b
319314
union r (Table xs) (Table ys) =
320-
Table (Map.unionWith f xs ys)
321-
where
322-
f (v1, bMay1) (v2, bMay2) =
323-
(resolveSerialised r v1 v2, getFirst (First bMay1 <> First bMay2))
315+
Table (Map.unionWith (resolveValueAndBlob r) xs ys)
324316

317+
-- | Like 'union', but for @n@ tables.
318+
unions ::
319+
ResolveSerialisedValue v
320+
-> V.Vector (Table k v b)
321+
-> Table k v b
322+
unions r tables =
323+
Table (Map.unionsWith (resolveValueAndBlob r) (V.map values tables))

0 commit comments

Comments
 (0)