hedis-0.16.1: src/Database/Redis.hs
module Database.Redis (
-- * How To Use This Module
-- |
-- Connect to a Redis server:
--
-- @
-- -- connects to localhost:6379
-- conn <- 'checkedConnect' 'defaultConnectInfo'
-- @
--
-- Connect to a Redis server using TLS:
--
-- @
-- -- connects to foobar.redis.cache.windows.net:6380
-- import Network.TLS
-- import Network.TLS.Extra.Cipher
-- import Data.X509.CertificateStore
-- import Data.Default.Class (def)
-- (Just certStore) <- readCertificateStore "azure-redis.crt"
-- let tlsParams = (defaultParamsClient "foobar.redis.cache.windows.net" "") { clientSupported = def { supportedCiphers = ciphersuite_strong }, clientShared = def { sharedCAStore = certStore } }
-- let redisConnInfo = defaultConnectInfo { connectAddr = ConnectAddrHostPort "foobar.redis.cache.windows.net" 6380, connectTLSParams = Just tlsParams, connectAuth = Just "Foobar!" }
-- conn <- checkedConnect redisConnInfo
-- @
--
-- Send commands to the server:
--
-- @
-- {-\# LANGUAGE OverloadedStrings \#-}
-- ...
-- 'runRedis' conn $ do
-- 'set' \"hello\" \"hello\"
-- set \"world\" \"world\"
-- hello <- 'get' \"hello\"
-- world <- get \"world\"
-- liftIO $ print (hello,world)
-- @
--
-- disconnect all idle resources in the connection pool:
--
-- @
-- 'disconnect' 'conn'
-- @
-- ** Command Type Signatures
-- |Redis commands behave differently when issued in- or outside of a
-- transaction. To make them work in both contexts, most command functions
-- have a type signature similar to the following:
--
-- @
-- 'echo' :: ('RedisCtx' m f) => ByteString -> m (f ByteString)
-- @
--
-- Here is how to interpret this type signature:
--
-- * The argument types are independent of the execution context. 'echo'
-- always takes a 'ByteString' parameter, whether in- or outside of a
-- transaction. This is true for all command functions.
--
-- * All Redis commands return their result wrapped in some \"container\".
-- The type @f@ of this container depends on the commands execution
-- context @m@. The 'ByteString' return type in the example is specific
-- to the 'echo' command. For other commands, it will often be another
-- type.
--
-- * In the \"normal\" context 'Redis', outside of any transactions,
-- results are wrapped in an @'Either' 'Reply'@.
--
-- * Inside a transaction, in the 'RedisTx' context, results are wrapped in
-- a 'Queued'.
--
-- In short, you can view any command with a 'RedisCtx' constraint in the
-- type signature, to \"have two types\". For example 'echo' \"has both
-- types\":
--
-- @
-- echo :: ByteString -> Redis (Either Reply ByteString)
-- echo :: ByteString -> RedisTx (Queued ByteString)
-- @
--
-- [Exercise] What are the types of 'expire' inside a transaction and
-- 'lindex' outside of a transaction? The solutions are at the very
-- bottom of this page.
-- ** Lua Scripting
-- |Lua values returned from the 'eval' and 'evalsha' functions will be
-- converted to Haskell values by the 'decode' function from the
-- 'RedisResult' type class.
--
-- @
-- Lua Type | Haskell Type | Conversion Example
-- --------------|--------------------|-----------------------------
-- Number | Integer | 1.23 => 1
-- String | ByteString, Double | \"1.23\" => \"1.23\" or 1.23
-- Boolean | Bool | false => False
-- Table | List | {1,2} => [1,2]
-- @
--
-- Additionally, any of the Haskell types from the table above can be
-- wrapped in a 'Maybe':
--
-- @
-- 42 => Just 42 :: Maybe Integer
-- nil => Nothing :: Maybe Integer
-- @
--
-- Note that Redis imposes some limitations on the possible conversions:
--
-- * Lua numbers can only be converted to Integers. Only Lua strings can be
-- interpreted as Doubles.
--
-- * Associative Lua tables can not be converted at all. Returned tables
-- must be \"arrays\", i.e. indexed only by integers.
--
-- The Redis Scripting website (<http://redis.io/commands/eval>)
-- documents the exact semantics of the scripting commands and value
-- conversion.
-- ** Automatic Pipelining
-- |Commands are automatically pipelined as much as possible. For example,
-- in the above \"hello world\" example, all four commands are pipelined.
-- Automatic pipelining makes use of Haskell's laziness. As long as a
-- previous reply is not evaluated, subsequent commands can be pipelined.
--
-- Automatic pipelining is limited to the scope of 'runRedis' call and
-- it is guaranteed that every reply expected as a part of 'runRedis'
-- execution gets received after 'runRedis` invocation.
--
-- To keep memory usage low, the number of requests \"in the pipeline\" is
-- limited (per connection) to 1000. After that number, the next command is
-- sent only when at least one reply has been received. That means, command
-- functions may block until there are less than 1000 outstanding replies.
--
-- ** Error Behavior
-- |
-- [Operations against keys holding the wrong kind of value:] Outside of a
-- transaction, if the Redis server returns an 'Error', command functions
-- will return 'Left' the 'Reply'. The library user can inspect the error
-- message to gain information on what kind of error occured.
--
-- [Connection to the server lost:] In case of a lost connection, command
-- functions throw a 'ConnectionLostException'. It can only be caught
-- outside of 'runRedis'.
--
-- [Trying to connect to an unreachable server:] When trying to connect to
-- a server that does not exist or can't be reached, the connection pool
-- only starts the first connection when actually executing a call to
-- the server. This can lead to discovering very late that the server is
-- not available, for example when running a server that logs to Redis.
-- To prevent this, run a 'ping' command directly after connecting or
-- use the 'checkedConnect' function which encapsulates this behavior.
--
-- [Exceptions:] Any exceptions can only be caught /outside/ of 'runRedis'.
-- This way the connection pool can properly close the connection, making
-- sure it is not left in an unusable state, e.g. closed or inside a
-- transaction.
--
-- * The Redis Monad
Redis(), runRedis, runRedisNonBlocking,
unRedis, reRedis,
RedisCtx(..), MonadRedis(..),
-- * Connection
Connection, ConnectError(..), connect, checkedConnect,
ClusterConnectError (..), connectCluster, checkedConnectCluster,
disconnect, withConnect, withCheckedConnect,
ConnectInfo(..), defaultConnectInfo, parseConnectInfo, ConnectAddr(..),
-- * Commands
module Database.Redis.Commands,
-- * Transactions
module Database.Redis.Transactions,
-- * Pub\/Sub
module Database.Redis.PubSub,
-- * Hooks
Hooks(..), SendRequestHook, SendPubSubHook, CallbackHook, SendHook, ReceiveHook, defaultHooks,
-- * Low-Level Command API
sendRequest,
Reply(..), Status(..), RedisArg(..), RedisResult(..), ConnectionLostException(..),
ConnectTimeout(..),
-- |[Solution to Exercise]
--
-- Type of 'expire' inside a transaction:
--
-- > expire :: ByteString -> Integer -> RedisTx (Queued Bool)
--
-- Type of 'lindex' outside of a transaction:
--
-- > lindex :: ByteString -> Integer -> Redis (Either Reply ByteString)
--
HashSlot, keyToSlot
) where
import Database.Redis.Core
import Database.Redis.Connection
( runRedis
, runRedisNonBlocking
, connectCluster
, defaultConnectInfo
, ConnectInfo(..)
, disconnect
, checkedConnect
, connect
, checkedConnectCluster
, connectCluster
, ConnectError(..)
, ClusterConnectError(..)
, Connection(..)
, withConnect
, withCheckedConnect)
import Database.Redis.ConnectionContext(ConnectAddr(..), ConnectionLostException(..), ConnectTimeout(..))
import Database.Redis.PubSub
import Database.Redis.Protocol
import Database.Redis.Transactions
import Database.Redis.Types
import Database.Redis.URL
import Database.Redis.Commands
import Database.Redis.Cluster.HashSlot(HashSlot, keyToSlot)