{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}
module Rqlite.Status
( RQStatus (..)
, getLeader
, retryUntilAlive
, queryStatus
) where
import Control.Concurrent (threadDelay)
import Control.Exception
import Data.Aeson hiding (Result)
import qualified Data.ByteString.Char8 as C8
import GHC.Generics
import Network.HTTP hiding (host)
import Rqlite
queryStatus :: String -> IO RQStatus
queryStatus host = do
resp <- reify $ simpleHTTP $ getRequest $ concat
[ "http://"
, host
, "/status?pretty"
]
case eitherDecodeStrict $ C8.pack $ resp of
Left e -> throwIO $ UnexpectedResponse $ concat
["Got ", e, " while trying to decode ", resp, " as PostResult"]
Right st -> return st
data RQState = Leader | Follower | UnknownState
deriving (Show, Eq, Generic)
readState :: String -> RQState
readState "Leader" = Leader
readState "Follower" = Follower
readState _ = UnknownState
data RQStatus = RQStatus {
path :: String
, leader :: Maybe String
, peers :: [String]
, state :: RQState
, fk_constraints :: Bool
} deriving (Show, Eq, Generic)
instance FromJSON RQStatus where
parseJSON j = do
Object o <- parseJSON j
Object store <- o .: "store"
pth <- store .: "dir"
ldr <- store .: "leader"
let mLeader = if ldr == "" then Nothing else Just ldr
prs :: [String] <- store .: "peers"
sqliteInfo <- store .: "sqlite3"
raft <- store .: "raft"
stStr <- raft .: "state"
let st = readState stStr
fk' :: String <- sqliteInfo .: "fk_constraints"
let fk = fk' /= "disabled"
return $ RQStatus pth mLeader prs st fk
getLeader :: String -> IO (Maybe String)
getLeader host = do
mstatus <- queryStatus host
return $ leader mstatus
retryUntilAlive :: String -> IO ()
retryUntilAlive host = go 40
where
go :: Int -> IO ()
go n = do
mStatus <- try $ queryStatus host
case mStatus of
Right _ -> return ()
Left (NodeUnreachable e _) -> do
threadDelay 500000
if n > 0 then go $ n - 1
else throwIO $ NodeUnreachable e 40
Left e -> throwIO e