Skip to content

Commit dd7596f

Browse files
committed
Demo
1 parent b05fc93 commit dd7596f

File tree

4 files changed

+253
-0
lines changed

4 files changed

+253
-0
lines changed

.hlint.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
- ignore: {name: "Use notElem"}
4141
- ignore: {name: "Use elem"}
4242
- ignore: {name: "Use infix"}
43+
- ignore: {name: "Redundant pure"}
4344

4445
# Specify additional command line arguments
4546
#

app/Database/LSMTree/Demo.hs

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
{-# LANGUAGE CPP #-}
2+
{-# LANGUAGE OverloadedLists #-}
3+
{-# LANGUAGE OverloadedStrings #-}
4+
{-# OPTIONS_GHC -Wno-orphans #-}
5+
{-# OPTIONS_GHC -Wno-unused-do-bind #-}
6+
7+
{- HLINT ignore "Redundant pure" -}
8+
9+
module Database.LSMTree.Demo (demo) where
10+
11+
import Control.Exception (SomeException, try)
12+
import Control.Monad (when)
13+
import Control.Monad.Class.MonadST (MonadST (..))
14+
import qualified Control.Monad.IOSim as IOSim
15+
import Control.Monad.Primitive (RealWorld)
16+
import Control.Monad.ST.Unsafe (unsafeIOToST)
17+
import Control.Tracer (nullTracer)
18+
import Data.Functor (void)
19+
import Data.Primitive.PrimVar (PrimVar, newPrimVar, readPrimVar,
20+
writePrimVar)
21+
import Data.Typeable (Typeable)
22+
import qualified Data.Vector as V
23+
import Data.Word (Word64)
24+
import Database.LSMTree as LSMT
25+
import qualified System.Directory as IO (createDirectoryIfMissing,
26+
doesDirectoryExist, removeDirectoryRecursive)
27+
import qualified System.FS.API as FS
28+
import qualified System.FS.BlockIO.API as FS
29+
import qualified System.FS.BlockIO.IO as FS
30+
import qualified System.FS.BlockIO.Sim as FSSim
31+
import qualified System.FS.IO as FS
32+
import qualified System.FS.Sim.MockFS as FSSim
33+
import System.IO.Unsafe (unsafePerformIO)
34+
35+
-- | Interactive demo showing functional requiremens for the @lsm-tree@ library
36+
-- are met.
37+
--
38+
-- The functional requirements are discussed in this document: "Storing the
39+
-- Cardano ledger state on disk: final report for high-performance backend"
40+
--
41+
-- Sections of the demo code are headed by the number of the corresponding
42+
-- functional requirement.
43+
demo :: IO ()
44+
demo = do
45+
freshDirectory "_demo"
46+
withOpenSessionIO tracer "_demo" $ \session -> do
47+
withTableWith config session $ \(table :: Table IO K V B) -> do
48+
pause -- [0]
49+
50+
-- 2. basic key-value store operations
51+
52+
inserts table $ V.fromList [ (K i, V i, Just (B i)) | i <- [1 .. 10_000] ]
53+
as <- lookups table $ V.fromList [ K 1, K 2, K 3, K 4 ]
54+
print (fmap getValue as)
55+
pause -- [1]
56+
57+
deletes table $ V.fromList [ K i | i <- [1 .. 10_000], even i ]
58+
bs <- lookups table $ V.fromList [ K 1, K 2, K 3, K 4 ]
59+
print (fmap getValue bs)
60+
pause -- [2]
61+
62+
-- 2. Intermezzo: blob retrieval
63+
64+
cs <- try @SomeException $ retrieveBlobs session $ V.mapMaybe getBlob as
65+
print cs
66+
pause -- [3]
67+
68+
ds <- try @SomeException $ retrieveBlobs session $ V.mapMaybe getBlob bs
69+
print ds
70+
pause -- [4]
71+
72+
-- 3. range lookups and cursors
73+
74+
es <- rangeLookup table $ FromToIncluding (K 1) (K 4)
75+
print (fmap getEntryValue es)
76+
pause -- [5]
77+
78+
withCursorAtOffset table (K 1) $ \cursor -> do
79+
fs <- LSMT.take 2 cursor
80+
print (fmap getEntryValue fs)
81+
pause -- [6]
82+
83+
-- 4. upserts (or monoidal updates)
84+
85+
-- better than lookup followed by insert
86+
upserts table $ V.fromList [ (K i, V 1) | i <- [1 .. 10_000] ]
87+
gs <- lookups table $ V.fromList [ K 1, K 2, K 3, K 4 ]
88+
print (fmap getValue gs)
89+
pause -- [7]
90+
91+
-- 5. multiple independently writable references
92+
93+
withDuplicate table $ \dupliTable -> do
94+
inserts dupliTable $ V.fromList [ (K i, V 1, Nothing) | i <- [1 .. 10_000] ]
95+
hs <- lookups dupliTable $ V.fromList [ K 1, K 2, K 3, K 4 ]
96+
print (fmap getValue hs)
97+
pause -- [8]
98+
99+
is <- lookups table $ V.fromList [ K 1, K 2, K 3, K 4]
100+
print (fmap getValue is)
101+
pause -- [9]
102+
103+
-- 6. snapshots
104+
105+
saveSnapshot "odds_evens" label table
106+
saveSnapshot "all_ones" label dupliTable
107+
js <- listSnapshots session
108+
print js
109+
pause -- [10]
110+
111+
-- 6. snapshots continued
112+
113+
withTableFromSnapshot session "odds_evens" label $ \(table :: Table IO K V B) -> do
114+
withTableFromSnapshot session "all_ones" label $ \(dupliTable :: Table IO K V B) -> do
115+
pause -- [11]
116+
117+
-- 7. table unions
118+
119+
withUnion table dupliTable $ \uniTable -> do
120+
ks <- lookups uniTable $ V.fromList [ K 1, K 2, K 3, K 4]
121+
print (fmap getValue ks)
122+
pause -- [12]
123+
124+
withIncrementalUnion table dupliTable $ \uniTable -> do
125+
ls <- lookups uniTable $ V.fromList [ K 1, K 2, K 3, K 4]
126+
print (fmap getValue ls)
127+
pause -- [13]
128+
129+
m@(UnionDebt m') <- remainingUnionDebt uniTable
130+
supplyUnionCredits uniTable (UnionCredits (m' `div` 2))
131+
print m
132+
pause -- [14]
133+
134+
ns <- lookups uniTable $ V.fromList [ K 1, K 2, K 3, K 4]
135+
print (fmap getValue ns)
136+
pause -- [15]
137+
138+
-- 8. simulation
139+
140+
let
141+
simpleAction ::
142+
(LSMT.IOLike m, Typeable h)
143+
=> FS.HasFS m h -> FS.HasBlockIO m h -> m ()
144+
simpleAction hasFS hasBlockIO = do
145+
let sessionDir = FS.mkFsPath ["_demo"]
146+
FS.createDirectoryIfMissing hasFS False sessionDir
147+
withOpenSession tracer hasFS hasBlockIO 17 sessionDir $ \session -> do
148+
withTableWith config session $ \(table :: Table m K V B) -> do
149+
inserts table $ V.fromList [ (K i, V i, Just (B i)) | i <- [1 .. 10_000] ]
150+
os <- lookups table $ V.fromList [ K 1, K 2, K 3, K 4 ]
151+
print' (fmap getValue os)
152+
153+
do
154+
let hasFS = FS.ioHasFS (FS.MountPoint "")
155+
FS.withIOHasBlockIO hasFS FS.defaultIOCtxParams $ \hasBlockIO -> do
156+
simpleAction hasFS hasBlockIO
157+
pause -- [16]
158+
159+
do
160+
pure $! IOSim.runSimOrThrow $ do
161+
(hasFS, hasBlockIO) <- FSSim.simHasBlockIO' FSSim.empty
162+
simpleAction hasFS hasBlockIO
163+
pause -- [17]
164+
165+
{-------------------------------------------------------------------------------
166+
Types
167+
-------------------------------------------------------------------------------}
168+
169+
newtype K = K Word64
170+
deriving stock (Show, Eq)
171+
deriving newtype SerialiseKey
172+
173+
newtype V = V Word64
174+
deriving stock (Show, Eq)
175+
deriving newtype (Num, SerialiseValue)
176+
instance ResolveValue V where
177+
resolve = (+)
178+
179+
newtype B = B Word64
180+
deriving stock (Show, Eq)
181+
deriving newtype (Num, SerialiseValue)
182+
183+
config :: TableConfig
184+
config = defaultTableConfig {
185+
confWriteBufferAlloc = AllocNumEntries 172
186+
}
187+
188+
tracer :: Monad m => Tracer m LSMTreeTrace
189+
tracer = nullTracer
190+
191+
label :: SnapshotLabel
192+
label = "KVB"
193+
194+
{-------------------------------------------------------------------------------
195+
Utils
196+
-------------------------------------------------------------------------------}
197+
198+
{-# NOINLINE pauseRef #-}
199+
pauseRef :: PrimVar RealWorld Int
200+
pauseRef = unsafePerformIO $ newPrimVar 0
201+
202+
incrPauseRef :: IO Int
203+
incrPauseRef = do
204+
x <- readPrimVar pauseRef
205+
writePrimVar pauseRef $! x + 1
206+
pure x
207+
208+
pause :: IO ()
209+
pause = do
210+
x <- incrPauseRef
211+
putStr ("[" <> show x <> "] " <> "press ENTER to continue...")
212+
void $ getLine
213+
214+
freshDirectory :: FilePath -> IO ()
215+
freshDirectory path = do
216+
b <- IO.doesDirectoryExist path
217+
when b $ IO.removeDirectoryRecursive path
218+
IO.createDirectoryIfMissing False path
219+
220+
print' :: (Show a, MonadST m) => a -> m ()
221+
print' x = stToIO $ unsafeIOToST $ print x

app/Main.hs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module Main (main) where
2+
3+
import Database.LSMTree.Demo (demo)
4+
import System.IO (BufferMode (..), hSetBuffering, stdout)
5+
6+
main :: IO ()
7+
main = do
8+
hSetBuffering stdout NoBuffering
9+
demo

lsm-tree.cabal

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,3 +1252,25 @@ test-suite control-test
12521252
, QuickCheck
12531253
, tasty
12541254
, tasty-quickcheck
1255+
1256+
executable demo
1257+
import: language, warnings
1258+
scope: private
1259+
hs-source-dirs: app
1260+
main-is: Main.hs
1261+
other-modules: Database.LSMTree.Demo
1262+
build-depends:
1263+
, base <5
1264+
, blockio
1265+
, blockio:sim
1266+
, contra-tracer
1267+
, directory
1268+
, fs-api
1269+
, fs-sim
1270+
, io-classes
1271+
, io-sim
1272+
, lsm-tree
1273+
, primitive
1274+
, vector
1275+
1276+
ghc-options: -threaded

0 commit comments

Comments
 (0)