tigerbeetle-hs-0.1.0.0: src/Database/TigerBeetle/Client/Async.hs
{-# LANGUAGE RecordWildCards #-}
module Database.TigerBeetle.Client.Async
( -- * Types
ThreadContext (..)
-- * Connecting
, withClient
-- * Commands
, createAccounts
, createTransfers
-- * Queries
, lookupAccounts
, getAccountBalances
, getAccountTransfers
, queryAccounts
, queryTransfers
)
where
import Control.Monad.IO.Class
import Control.Monad.Reader
import Data.Word
import Database.TigerBeetle.Account
import Database.TigerBeetle.Address
import Database.TigerBeetle.ClusterId
import Database.TigerBeetle.Internal.FFI.Client
import Database.TigerBeetle.Raw.Account qualified as Raw
import Database.TigerBeetle.Raw.Client qualified as Raw
import Database.TigerBeetle.Raw.Response qualified as Raw
import Database.TigerBeetle.Raw.Transfer qualified as Raw
import Database.TigerBeetle.Response
import Database.TigerBeetle.Transfer
import Foreign.C.Types
import Foreign.Storable
newtype AsyncState = AsyncState {asyncStateClientPtr :: Raw.ClientPtr}
newtype AsyncClientT m a = AsyncClientT {getAsyncClient :: ReaderT AsyncState m a}
deriving (Applicative, Functor, Monad, MonadIO, MonadReader AsyncState)
-- | A thread ID that will be associated with a given command.
newtype ThreadContext = ThreadContext {getThreadContext :: Word64}
-- | Initializes a TigerBeetle client connection to accept commands.
--
-- Provide a callback to receive the responses from the server.
--
-- @
-- callback _ result = print result
-- withClient (ClusterId 0) (Address "3000") (ThreadContext 1) callback $ do
-- createAccounts [CreateAccount 0 0 100]
-- @
withClient
:: (MonadIO m)
=> ClusterId
-> Address
-> ThreadContext
-> (ThreadContext -> Response -> IO ())
-> AsyncClientT m ()
-> m ()
withClient clusterId address (ThreadContext userCtxt) callback clientAction = do
cb <- liftIO $ Raw.makeCompletionCallback $ \(CUIntPtr ctx) tbPacketPtr _ resultDataPtr resultLen -> do
tbPacket <- peek tbPacketPtr
tbResponse <- Raw.decodeResponse tbPacket resultDataPtr $ fromIntegral resultLen
callback (ThreadContext ctx) $ toResponse tbResponse
clientInitResult <- liftIO $ Raw.initClient clusterId address (CUIntPtr userCtxt) cb
case clientInitResult of
Left err -> error $ show err
Right clientPtr -> do
let asyncState = AsyncState{asyncStateClientPtr = clientPtr}
(`runReaderT` asyncState) . getAsyncClient $ clientAction
createAccounts :: (MonadIO m) => [CreateAccount] -> AsyncClientT m ()
createAccounts createAccountParams = do
AsyncState{..} <- ask
status <- Raw.submit asyncStateClientPtr Raw.createAccounts createAccountParams
case status of
ClientOk -> pure ()
_ -> error $ show status
lookupAccounts :: (MonadIO m) => [AccountId] -> AsyncClientT m ()
lookupAccounts lookupAccountParams = do
AsyncState{..} <- ask
status <- Raw.submit asyncStateClientPtr Raw.lookupAccounts lookupAccountParams
case status of
ClientOk -> pure ()
_ -> error $ show status
getAccountBalances :: (MonadIO m) => [AccountBalances] -> AsyncClientT m ()
getAccountBalances accountBalanceParams = do
AsyncState{..} <- ask
status <- Raw.submit asyncStateClientPtr Raw.getAccountBalances accountBalanceParams
case status of
ClientOk -> pure ()
_ -> error $ show status
getAccountTransfers :: (MonadIO m) => [AccountTransfers] -> AsyncClientT m ()
getAccountTransfers accountTransferParams = do
AsyncState{..} <- ask
status <- Raw.submit asyncStateClientPtr Raw.getAccountTransfers accountTransferParams
case status of
ClientOk -> pure ()
_ -> error $ show status
-- | Query accounts by the intersection of some fields and time
-- ranges.
--
-- It is not possible to query more than 8189 accounts
-- atomically. When issuing multiple queries (eg: when paginating the
-- full result set) it can happen that other operations may be
-- interleaved leading to read skew.
--
-- Note that this can be worked around with a flag in more recent
-- versions of Tigerbeetle.
queryAccounts :: (MonadIO m) => [AccountQuery] -> AsyncClientT m ()
queryAccounts queryAccountParams = do
AsyncState{..} <- ask
status <- Raw.submit asyncStateClientPtr Raw.queryAccounts queryAccountParams
case status of
ClientOk -> pure ()
_ -> error $ show status
createTransfers :: (MonadIO m) => [CreateTransfer] -> AsyncClientT m ()
createTransfers transferParams = do
AsyncState{..} <- ask
status <- Raw.submit asyncStateClientPtr Raw.createTransfers transferParams
case status of
ClientOk -> pure ()
_ -> error $ show status
-- | Query transfers by the intersection of some fields and time
-- ranges.
--
-- It is not possible to query more than 8189 transfers
-- atomically. When issuing multiple queries (eg: when paginating the
-- full result set) it can happen that other operations may be
-- interleaved leading to read skew.
--
-- Note that this can be worked around with a flag in more recent
-- versions of Tigerbeetle.
queryTransfers :: (MonadIO m) => [TransferQuery] -> AsyncClientT m ()
queryTransfers transferQueryParams = do
AsyncState{..} <- ask
status <- Raw.submit asyncStateClientPtr Raw.queryTransfers transferQueryParams
case status of
ClientOk -> pure ()
_ -> error $ show status