parconc-examples-0.3: distrib-db/DatabaseSample.hs
{-# LANGUAGE TemplateHaskell, DeriveDataTypeable, OverloadedStrings,
DeriveGeneric #-}
module DatabaseSample (
Database,
createDB,
get, set,
rcdata,
) where
import Control.Distributed.Process
import Control.Distributed.Process.Closure
import Control.Monad.IO.Class
import Control.Monad
import Text.Printf
import Control.Concurrent hiding (newChan)
import GHC.Generics (Generic)
import qualified Data.Binary
import Data.Typeable
import System.IO.Error hiding (catch)
import Data.Char
import qualified Data.Map as Map
import Data.Map (Map)
import WorkerSample
import Prelude hiding (catch)
import Control.Exception hiding (catch)
dbProc :: [NodeId] -> Process ()
dbProc peers = do
ps <- forM peers $ \nid -> do
say $ printf "spawning on %s" (show nid)
spawn nid $(mkStaticClosure 'worker)
when (null ps) $ liftIO $ ioError (userError "no workers")
mapM_ monitor ps
-- group the workers:
let pairs [] = []
pairs (a:b:xs) = [a,b] : pairs xs
pairs [x] = []
-- don't use the last node if we have an odd number
worker_pairs = pairs ps
n_slices = length worker_pairs
loop worker_pairs n_slices
loop :: [[ProcessId]] -> Int -> Process ()
loop worker_pairs n_slices
= receiveWait
[ match $ \req -> handleRequest req >> loop worker_pairs n_slices
, match $ \(ProcessMonitorNotification _ pid reason) -> do
say (printf "process %s died: %s" (show pid) (show reason))
loop (map (filter (/= pid)) worker_pairs) n_slices
]
where
workersForKey :: Key -> [ProcessId]
workersForKey k = worker_pairs !! (ord (head k) `mod` n_slices)
handleRequest :: Request -> Process ()
handleRequest r =
case r of
Set k _ -> mapM_ (! r) (workersForKey k)
Get k _ -> mapM_ (! r) (workersForKey k)
type Database = ProcessId
createDB :: [NodeId] -> Process Database
createDB peers = spawnLocal (dbProc peers)
set :: Database -> Key -> Value -> Process ()
set db k v = db ! Set k v
get :: Database -> Key -> Process (Maybe Value)
get db k = do
(s,r) <- newChan
db ! Get k s
receiveChan r
rcdata = WorkerSample.__remoteTable