eventstore-1.4.0: Database/EventStore/Internal/Exec.hs
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
--------------------------------------------------------------------------------
-- |
-- Module : Database.EventStore.Internal.Exec
-- Copyright : (C) 2017 Yorick Laupa
-- License : (see the file LICENSE)
--
-- Maintainer : Yorick Laupa <yo.eight@gmail.com>
-- Stability : provisional
-- Portability : non-portable
--
--------------------------------------------------------------------------------
module Database.EventStore.Internal.Exec
( Exec
, Terminated(..)
, newExec
, execSettings
, execWaitTillClosed
) where
--------------------------------------------------------------------------------
import Prelude (String)
import Data.Typeable
--------------------------------------------------------------------------------
import Database.EventStore.Internal.Communication
import Database.EventStore.Internal.Connection
import Database.EventStore.Internal.ConnectionManager
import Database.EventStore.Internal.Control
import Database.EventStore.Internal.Discovery
import Database.EventStore.Internal.Logger
import Database.EventStore.Internal.Prelude
import Database.EventStore.Internal.TimerService
--------------------------------------------------------------------------------
type ServicePendingInit = HashMap Service ()
--------------------------------------------------------------------------------
data Stage
= Init
| Available Publish
| Errored String
--------------------------------------------------------------------------------
newtype Terminated = Terminated String deriving (Show, Typeable)
--------------------------------------------------------------------------------
instance Exception Terminated
--------------------------------------------------------------------------------
data Exec =
Exec { execSettings :: Settings
, _execPub :: STM Publish
, _internal :: Internal
, execWaitTillClosed :: IO ()
}
--------------------------------------------------------------------------------
data Internal =
Internal { _initRef :: IORef ServicePendingInit
, _finishRef :: IORef ServicePendingInit
, _stageVar :: TVar Stage
}
--------------------------------------------------------------------------------
instance Pub Exec where
publishSTM e a = do
pub <- _execPub e
handled <- publishSTM pub a
unless handled $
throwSTM $ Terminated "Connection Closed."
return handled
--------------------------------------------------------------------------------
stageSTM :: TVar Stage -> STM Publish
stageSTM var = do
stage <- readTVar var
case stage of
Init -> retrySTM
Available pub -> return pub
Errored msg -> throwSTM $ Terminated msg
--------------------------------------------------------------------------------
errored :: TVar Stage -> String -> STM ()
errored var err = do
stage <- readTVar var
case stage of
Errored _ -> return ()
_ -> writeTVar var (Errored err)
--------------------------------------------------------------------------------
initServicePending :: ServicePendingInit
initServicePending = foldMap (\svc -> singletonMap svc ()) [minBound..]
--------------------------------------------------------------------------------
newExec :: Settings -> Bus -> ConnectionBuilder -> Discovery -> IO Exec
newExec setts mainBus builder disc = do
internal <- Internal <$> newIORef initServicePending
<*> newIORef initServicePending
<*> newTVarIO Init
let stagePub = stageSTM $ _stageVar internal
exe = Exec setts stagePub internal (busProcessedEverything mainBus)
hub = asHub mainBus
timerService hub
connectionManager setts builder disc hub
subscribe mainBus (onInit internal)
subscribe mainBus (onInitFailed internal)
subscribe mainBus (onFatal internal)
subscribe mainBus (onTerminated internal)
subscribe mainBus (onShutdown internal)
publishWith mainBus SystemInit
return exe
--------------------------------------------------------------------------------
onInit :: Internal -> Initialized -> EventStore ()
onInit Internal{..} (Initialized svc) = do
$logInfo [i|Service #{svc} initialized|]
initialized <- atomicModifyIORef' _initRef $ \m ->
let m' = deleteMap svc m in
(m', null m')
when initialized $ do
$logInfo "Entire system initialized properly"
pub <- publisher
atomically $ writeTVar _stageVar (Available pub)
--------------------------------------------------------------------------------
onInitFailed :: Internal -> InitFailed -> EventStore ()
onInitFailed Internal{..} (InitFailed svc) = do
atomically $ errored _stageVar "Driver failed to initialized"
$logError [i|Service #{svc} failed to initialize.|]
stopBus
$logError "System can't start."
--------------------------------------------------------------------------------
onFatal :: Internal -> FatalException -> EventStore ()
onFatal self@Internal{..} situation = do
case situation of
FatalException e ->
$(logOther "Fatal") [i|Fatal exception: #{e}|]
FatalCondition msg ->
$(logOther "Fatal") [i|Driver is in unrecoverable state: #{msg}.|]
shutdown self
--------------------------------------------------------------------------------
onTerminated :: Internal -> ServiceTerminated -> EventStore ()
onTerminated Internal{..} (ServiceTerminated svc) = do
$logInfo [i|Service #{svc} terminated.|]
terminated <- atomicModifyIORef' _finishRef $ \m ->
let m' = deleteMap svc m in
(m', null m')
when terminated $ do
$logInfo "Entire system shutdown properly"
stopBus
--------------------------------------------------------------------------------
onShutdown :: Internal -> SystemShutdown -> EventStore ()
onShutdown Internal{..} _ =
atomically $ writeTVar _stageVar (Errored "Connection closed")
--------------------------------------------------------------------------------
shutdown :: Internal -> EventStore ()
shutdown Internal{..} = do
atomically $ writeTVar _stageVar (Errored "Connection closed")
publish SystemShutdown