module Main where import Control.Concurrent (threadDelay) import Control.Concurrent.Async (async, wait) import Control.Monad (void, replicateM) import Control.Monad.Catch (bracket_, finally) import Data.BTree.Impure (Tree, toList, insertTree) import Data.ByteString (ByteString) import Data.Int (Int32) import Data.Text.Encoding (encodeUtf8) import qualified Data.BTree.Impure as Tree import qualified Data.Text as Text import Database.Haskey.Alloc.Concurrent (ConcurrentDb, ConcurrentHandles, concurrentHandles, lockConcurrentDb, unlockConcurrentDb, openConcurrentDb, createConcurrentDb, transact_, transactReadOnly, commit_) import Database.Haskey.Store.File (FileStoreT, runFileStoreT, defFileStoreConfig) import Database.Haskey.Store.InMemory (MemoryStoreT, MemoryFiles, newEmptyMemoryStore, runMemoryStoreT, defMemoryStoreConfig) import System.Directory (removeDirectoryRecursive) import System.Random (randomIO) concurrency :: Integral a => a concurrency = 100 type Root = Tree Int32 ByteString main :: IO () main = do inMemoryMain root fileMain root `finally` delRoot where root = "example-database.haskey" delRoot = removeDirectoryRecursive root inMemoryMain :: FilePath -> IO () inMemoryMain root = do store <- newEmptyMemoryStore db <- openOrCreate store writers <- mapM (async . writer store db) [1..concurrency] readers <- replicateM concurrency . async $ do delay <- randomIO reader store db (delay `rem` 5000) mapM_ wait writers mapM_ wait readers putStrLn "InMemory: done" where writer :: MemoryFiles FilePath -> ConcurrentDb Root -> Int32 -> IO () writer store db i = runDatabase store $ transact_ tx db where bs = encodeUtf8 $ Text.pack (show i) tx tree = insertTree i bs tree >>= commit_ reader :: MemoryFiles FilePath -> ConcurrentDb Root -> Int -> IO () reader files db delay = void $ replicateM 10 $ do threadDelay delay runDatabase files $ transactReadOnly toList db openOrCreate :: MemoryFiles FilePath -> IO (ConcurrentDb Root) openOrCreate store = runDatabase store $ do maybeDb <- openConcurrentDb handles case maybeDb of Nothing -> createConcurrentDb handles Tree.empty Just db -> return db runDatabase :: MemoryFiles FilePath -> MemoryStoreT FilePath m a -> m a runDatabase files action = runMemoryStoreT action defMemoryStoreConfig files handles :: ConcurrentHandles handles = concurrentHandles root fileMain :: FilePath -> IO () fileMain root = bracket_ (runDatabase $ lockConcurrentDb handles) (runDatabase $ unlockConcurrentDb handles) $ do db <- openOrCreate writers <- mapM (async . writer db) [1..concurrency] readers <- replicateM concurrency . async $ do delay <- randomIO reader db (delay `rem` 5000) mapM_ wait writers mapM_ wait readers putStrLn "File: done" where writer :: ConcurrentDb Root -> Int32 -> IO () writer db i = runDatabase $ transact_ tx db where bs = encodeUtf8 $ Text.pack (show i) tx tree = insertTree i bs tree >>= commit_ reader :: ConcurrentDb Root -> Int -> IO () reader db delay = void $ replicateM 10 $ do threadDelay delay runDatabase $ transactReadOnly toList db openOrCreate :: IO (ConcurrentDb Root) openOrCreate = runDatabase $ do maybeDb <- openConcurrentDb handles case maybeDb of Nothing -> createConcurrentDb handles Tree.empty Just db -> return db runDatabase :: Monad m => FileStoreT FilePath m a -> m a runDatabase action = runFileStoreT action defFileStoreConfig handles :: ConcurrentHandles handles = concurrentHandles root