module Database.Redis ( -- * How To Use This Package -- $package-usage -- ** Managing connections -- $connection-management Connection, -- | To create a connection one need to contruct a 'ConnectInfo' record. -- The easiest way to do this is to use the 'parseConnectInfo' function, which takes a URL and returns a 'ConnectInfo' record. ConnectInfo(..), ConnectAddr(..), defaultConnectInfo, parseConnectInfo, disconnect, -- -- *** Single node -- -- | If you are connecting to a single Redis node, use the 'connect' or 'checkedConnect' functions. connect, checkedConnect, withConnect, withCheckedConnect, ConnectError(..), -- *** Clustered -- -- | If you are connecting to a Redis cluster, use the 'connectCluster' or 'checkedConnectCluster' functions. -- -- At this point, some functions are not supported in the cluster mode: -- -- * CONFIG -- * AUTH -- * SCAN -- * MOVE, SELECT -- * RESET connectCluster, checkedConnectCluster, ClusterConnectError (..), -- *** Sentinel -- | If you are connecting to a Redis sentinel, use functions from the "Database.Redis.Sentinel" module, -- such as 'Database.Redis.Sentinel.connect' or 'Database.Redis.Sentinel.checkedConnectSentinel'. -- -- Those functions live in a separate module to simplify move from the Single-node to Sentinel mode. -- ** Running Commands -- $command-type-signatures module Database.Redis.Commands, -- | It's important to understand several features that the library provides. -- *** Automatic Pipelining -- $pipelining -- *** 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. -- -- ** Transactions -- -- | 'hedis' supports Redis transactions. See "Database.Redis.Transactions" for more information. module Database.Redis.Transactions, -- ** Pub\/Sub -- | 'hedis' supports Redis Pub/Sub. See "Database.Redis.PubSub" for more information. module Database.Redis.PubSub, -- * Advanced Usage -- ** The Redis Monad Redis(), runRedis, runRedisNonBlocking, unRedis, reRedis, RedisCtx(..), MonadRedis(..), -- ** 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 () -- documents the exact semantics of the scripting commands and value -- conversion. -- ** Hooks Hooks(..), SendRequestHook, SendPubSubHook, CallbackHook, SendHook, ReceiveHook, defaultHooks, -- ** Low-Level Command API sendRequest, Reply(..), Status(..), RedisArg(..), RedisResult(..), ConnectionLostException(..), ConnectTimeout(..), 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) -- $package-usage -- -- -- Simplest usage of this package is to connect to a Redis server and run commands against it and close connection when done: -- -- Connect to a Redis server: -- -- @ -- let Right ci = 'parseConnectInfo' "redis://localhost:6379" -- conn <- 'checkedConnect' ci -- @ -- -- 'connect' or 'checkedConnect' creates a connection pool under the hood. This poll manages reuse of the connections connection livetimes, -- and connection restore in the case of the connection loss. -- -- 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, and destroy the pool: -- -- @ -- 'disconnect' 'conn' -- @ -- -- $connection-management -- -- Redis connections are managed by a connection pool enclosed in the 'Connection' type, that keeps all required state. -- -- @ -- -- 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 -- @ -- -- See following sections for more details on the connection options and command behavior. -- -- $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) -- @ -- -- To see all commands refer to the "Database.Redis.Commands" module: -- $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. -- -- This feature has several implications. Consider the following example: -- -- @ -- 'runRedis' conn $ do -- 'set' "key1" "value1" -- _ <- 'get' "nonexistingkey" -- 'set' "key2" "value2" -- @ -- -- Even the "nonexistingkey" does not exist and would return a error it does not -- prevent commands from execution, so both @key1@ and @key2@ will be updated. -- -- To enforce verification of the reply one could wrap commands in ExceptT: -- -- @ -- 'runRedis' conn $ runExceptT $ do -- ExceptT $ 'set' "key1" "value1" -- _ <- ExceptT $'get' "nonexistingkey" -- ExceptT $ 'set' "key2" "value2" -- @ -- -- But this code will break the pipelining and second command will be sent only after first command reply is received.