HORIZON HASKELLDocslts/ghc-9.10.xc74966e2026-09-27Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · c74966e · 2026-09-27

Modulehedis-0.15.2Haskell2010

Database.Redis

  • 51 types
  • 3 classes
  • 251 values
  • Packagehedis-0.15.2
  • Exports305
  • LanguageHaskell2010
  • LicenceBSD-3-Clause
  • SourceInternal.hs

How To Use This Module

0 declarations

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 { connectHost = "foobar.redis.cache.windows.net", connectPort = PortNumber 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

6 declarations
newtypenewtype Redis a
#

Context for normal command execution, outside of transactions. Use runRedis to run actions of this type.

In this context, each result is wrapped in an Either to account for the possibility of Redis returning an Error reply.

Instances8Monad, Functor, MonadFail, Applicative, MonadIO, MonadUnliftIO, …
valueunRedis :: Redis a -> ReaderT RedisEnv IO a
#

Deconstruct Redis constructor.

unRedis and reRedis can be used to define instances for arbitrary typeclasses.

WARNING! These functions are considered internal and no guarantee is given at this point that they will not break in future.

classclass MonadRedis m => RedisCtx (m :: Type -> Type) (f :: Type -> Type) | m -> f where
#

This class captures the following behaviour: In a context m, a command will return its result wrapped in a "container" of type f.

Please refer to the Command Type Signatures section of this page for more information.

Methods

Instances2RedisCtx

Connection

12 declarations
datadata Connection
#

A threadsafe pool of network connections to a Redis server. Use the connect function to create one.

datadata ConnectInfo
#

Information for connnecting to a Redis server.

It is recommended to not use the ConnInfo data constructor directly. Instead use defaultConnectInfo and update it with record syntax. For example to connect to a password protected Redis server running on localhost and listening to the default port:

myConnectInfo :: ConnectInfo
myConnectInfo = defaultConnectInfo {connectAuth = Just "secret"}

Constructors

Instances1Show

Default information for connecting:

 connectHost           = "localhost"
 connectPort           = PortNumber 6379 -- Redis default port
 connectAuth           = Nothing         -- No password
 connectDatabase       = 0               -- SELECT database 0
 connectMaxConnections = 50              -- Up to 50 connections
 connectMaxIdleTime    = 30              -- Keep open for 30 seconds
 connectTimeout        = Nothing         -- Don't add timeout logic
 connectTLSParams      = Nothing         -- Do not use TLS

Parse a ConnectInfo from a URL

Username is ignored, path is used to specify the database:

Example1 expression
parseConnectInfo "redis://username:password@host:42/2"Right (ConnInfo {connectHost = "host", connectPort = PortNumber 42, connectAuth = Just "password", connectDatabase = 2, connectMaxConnections = 50, connectMaxIdleTime = 30s, connectTimeout = Nothing, connectTLSParams = Nothing})
Example1 expression
parseConnectInfo "redis://username:password@host:42/db"Left "Invalid port: db"

The scheme is validated, to prevent mixing up configurations:

Example1 expression
parseConnectInfo "postgres://"Left "Wrong scheme"

Beyond that, all values are optional. Omitted values are taken from defaultConnectInfo:

Example1 expression
parseConnectInfo "redis://"Right (ConnInfo {connectHost = "localhost", connectPort = PortNumber 6379, connectAuth = Nothing, connectDatabase = 0, connectMaxConnections = 50, connectMaxIdleTime = 30s, connectTimeout = Nothing, connectTLSParams = Nothing})

Constructs a ShardMap of connections to clustered nodes. The argument is a ConnectInfo for any node in the cluster

Some Redis commands are currently not supported in cluster mode - CONFIG, AUTH - SCAN - MOVE, SELECT - PUBLISH, SUBSCRIBE, PSUBSCRIBE, UNSUBSCRIBE, PUNSUBSCRIBE, RESET

Commands

251 declarations

Redis default MigrateOpts. Equivalent to omitting all optional parameters.

MigrateOpts
    { migrateCopy    = False -- remove the key from the local instance
    , migrateReplace = False -- don't replace existing key on the remote instance
    }
newtypenewtype Cursor
#
Instances4Eq, Show, RedisResult, RedisArg
  • Eq CursorDefined in hedis-0.15.2 · Database.Redis.ManualCommands
  • Show CursorDefined in hedis-0.15.2 · Database.Redis.ManualCommands
  • RedisResult CursorDefined in hedis-0.15.2 · Database.Redis.ManualCommands
  • RedisArg CursorDefined in hedis-0.15.2 · Database.Redis.ManualCommands

Redis default ScanOpts. Equivalent to omitting all optional parameters.

ScanOpts
    { scanMatch = Nothing -- don't match any pattern
    , scanCount = Nothing -- don't set any requirements on number elements returned (works like value COUNT 10)
    }

Redis default SortOpts. Equivalent to omitting all optional parameters.

SortOpts
    { sortBy    = Nothing -- omit the BY option
    , sortLimit = (0,-1)  -- return entire collection
    , sortGet   = []      -- omit the GET option
    , sortOrder = Asc     -- sort in ascending order
    , sortAlpha = False   -- sort numerically, not lexicographically
    }
valueevalsha
  1. :: (RedisCtx m f, RedisResult a)
  2. => ByteString

    base16-encoded sha1 hash of the script

  3. -> [ByteString]

    keys

  4. -> [ByteString]

    args

  5. -> m (f a)
#

Works like eval, but sends the SHA1 hash of the script instead of the script itself. Fails if the server does not recognise the hash, in which case, eval should be used instead.

datadata DebugMode
#
Instances3Eq, Show, RedisArg
  • Eq DebugModeDefined in hedis-0.15.2 · Database.Redis.ManualCommands
  • Show DebugModeDefined in hedis-0.15.2 · Database.Redis.ManualCommands
  • RedisArg DebugModeDefined in hedis-0.15.2 · Database.Redis.ManualCommands
datadata ReplyMode
#
Instances3Eq, Show, RedisArg
  • Eq ReplyModeDefined in hedis-0.15.2 · Database.Redis.ManualCommands
  • Show ReplyModeDefined in hedis-0.15.2 · Database.Redis.ManualCommands
  • RedisArg ReplyModeDefined in hedis-0.15.2 · Database.Redis.ManualCommands
datadata Slowlog
#

A single entry from the slowlog.

Constructors

Instances3Eq, Show, RedisResult
  • Eq SlowlogDefined in hedis-0.15.2 · Database.Redis.ManualCommands
  • Show SlowlogDefined in hedis-0.15.2 · Database.Redis.ManualCommands
  • RedisResult SlowlogDefined in hedis-0.15.2 · Database.Redis.ManualCommands

Redis default ZaddOpts. Equivalent to omitting all optional parameters.

ZaddOpts
    { zaddCondition = Nothing -- omit NX and XX options
    , zaddChange    = False   -- don't modify the return value from the number of new elements added, to the total number of elements changed
    , zaddIncrement = False   -- don't add like ZINCRBY
    }
datadata RangeLex a
#

Constructors

Instances1RedisArg
  • RedisArg a => RedisArg (RangeLex a)Defined in hedis-0.15.2 · Database.Redis.ManualCommands
datadata Condition
#
Instances3Eq, Show, RedisArg
  • Eq ConditionDefined in hedis-0.15.2 · Database.Redis.ManualCommands
  • Show ConditionDefined in hedis-0.15.2 · Database.Redis.ManualCommands
  • RedisArg ConditionDefined in hedis-0.15.2 · Database.Redis.ManualCommands

Redis default XReadOpts. Equivalent to omitting all optional parameters.

XReadOpts
    { block = Nothing -- Don't block waiting for more records
    , recordCount    = Nothing   -- no record count
    }
Instances3Eq, Show, RedisResult
datadata XInfoStreamResponse
#
Instances3Eq, Show, RedisResult
Instances2Eq, Show

Transactions

6 declarations
valuemultiExec :: RedisTx (Queued a) -> Redis (TxResult a)
#

Run commands inside a transaction. For documentation on the semantics of Redis transaction see http://redis.io/topics/transactions.

Inside the transaction block, command functions return their result wrapped in a Queued. The Queued result is a proxy object for the actual command's result, which will only be available after EXECing the transaction.

Example usage (note how Queued 's Applicative instance is used to combine the two individual results):

 runRedis conn $ do
     set "hello" "hello"
     set "world" "world"
     helloworld <- multiExec $ do
         hello <- get "hello"
         world <- get "world"
         return $ (,) <$> hello <*> world
     liftIO (print helloworld)
 
datadata Queued a
#

A Queued value represents the result of a command inside a transaction. It is a proxy object for the actual result, which will only be available after returning from a multiExec transaction.

Queued values are composable by utilizing the Functor, Applicative or Monad interfaces.

Instances4Monad, Functor, Applicative, RedisCtx
datadata TxResult a
#

Result of a multiExec transaction.

Constructors

Instances5Eq, Show, Generic, NFData, Rep
newtypenewtype RedisTx a
#

Command-context inside of MULTI/EXEC transactions. Use multiExec to run actions of this type.

In the RedisTx context, all commands return a Queued value. It is a proxy object for the actual result, which will only be available after finishing the transaction.

Instances6Monad, Functor, Applicative, MonadIO, MonadRedis, RedisCtx

Pub/Sub

22 declarations
valuepubSub
  1. :: PubSub

    Initial subscriptions.

  2. -> (Message -> IO PubSub)

    Callback function.

  3. -> Redis ()
#

Listens to published messages on subscribed channels and channels matching the subscribed patterns. For documentation on the semantics of Redis Pub/Sub see http://redis.io/topics/pubsub.

The given callback function is called for each received message. Subscription changes are triggered by the returned PubSub. To keep subscriptions unchanged, the callback can return mempty.

Example: Subscribe to the "news" channel indefinitely.

 pubSub (subscribe ["news"]) $ \msg -> do
     putStrLn $ "Message from " ++ show (msgChannel msg)
     return mempty
 

Example: Receive a single message from the "chat" channel.

 pubSub (subscribe ["chat"]) $ \msg -> do
     putStrLn $ "Message from " ++ show (msgChannel msg)
     return $ unsubscribe ["chat"]
 

It should be noted that Redis Pub/Sub by its nature is asynchronous so returning unsubscribe does not mean that callback won't be able to receive any further messages. And to guarantee that you won't won't process messages after unsubscription and won't unsubscribe from the same channel more than once you need to use IORef or something similar

valuepubSubForever
  1. :: Connection

    The connection pool

  2. -> PubSubController

    The controller which keeps track of all subscriptions and handlers

  3. -> IO ()

    This action is executed once Redis acknowledges that all the subscriptions in the controller are now subscribed. You can use this after an exception (such as ConnectionLost) to signal that all subscriptions are now reactivated.

  4. -> IO ()
#

Open a connection to the Redis server, register to all channels in the PubSubController, and process messages and subscription change requests forever. The only way this will ever exit is if there is an exception from the network code or an unhandled exception in a MessageCallback or PMessageCallback. For example, if the network connection to Redis dies, pubSubForever will throw a ConnectionLost. When such an exception is thrown, you can recall pubSubForever with the same PubSubController which will open a new connection and resubscribe to all the channels which are tracked in the PubSubController.

The general pattern is therefore during program startup create a PubSubController and fork a thread which calls pubSubForever in a loop (using an exponential backoff algorithm such as the retry package to not hammer the Redis server if it does die). For example,

myhandler :: ByteString -> IO ()
myhandler msg = putStrLn $ unpack $ decodeUtf8 msg

onInitialComplete :: IO ()
onInitialComplete = putStrLn "Redis acknowledged that mychannel is now subscribed"

main :: IO ()
main = do
  conn <- connect defaultConnectInfo
  pubSubCtrl <- newPubSubController [("mychannel", myhandler)] []
  concurrently ( forever $
      pubSubForever conn pubSubCtrl onInitialComplete
        `catch` (\(e :: SomeException) -> do
          putStrLn $ "Got error: " ++ show e
          threadDelay $ 50*1000) -- TODO: use exponential backoff
       ) $ restOfYourProgram


  {- elsewhere in your program, use pubSubCtrl to change subscriptions -}

At most one active pubSubForever can be running against a single PubSubController at any time. If two active calls to pubSubForever share a single PubSubController there will be deadlocks. If you do want to process messages using multiple connections to Redis, you can create more than one PubSubController. For example, create one PubSubController for each getNumCapabilities and then create a Haskell thread bound to each capability each calling pubSubForever in a loop. This will create one network connection per controller/capability and allow you to register separate channels and callbacks for each controller, spreading the load across the capabilities.

typetype MessageCallback = ByteString -> IO ()
#

A handler for a message from a subscribed channel. The callback is passed the message content.

Messages are processed synchronously in the receiving thread, so if the callback takes a long time it will block other callbacks and other messages from being received. If you need to move long-running work to a different thread, we suggest you use TBQueue with a reasonable bound, so that if messages are arriving faster than you can process them, you do eventually block.

If the callback throws an exception, the exception will be thrown from pubSubForever which will cause the entire Redis connection for all subscriptions to be closed. As long as you call pubSubForever in a loop you will reconnect to your subscribed channels, but you should probably add an exception handler to each callback to prevent this.

datadata PubSubController
#

A controller that stores a set of channels, pattern channels, and callbacks. It allows you to manage Pub/Sub subscriptions and pattern subscriptions and alter them at any time throughout the life of your program. You should typically create the controller at the start of your program and then store it through the life of your program, using addChannels and removeChannels to update the current subscriptions.

valueaddChannels
  1. :: MonadIO m
  2. => PubSubController
  3. -> [(RedisChannel, MessageCallback)]

    the channels to subscribe to

  4. -> [(RedisPChannel, PMessageCallback)]

    the channels to pattern subscribe to

  5. -> m UnregisterCallbacksAction
#

Add channels into the PubSubController, and if there is an active pubSubForever, send the subscribe and psubscribe commands to Redis. The addChannels function is thread-safe. This function does not wait for Redis to acknowledge that the channels have actually been subscribed; use addChannelsAndWait for that.

You can subscribe to the same channel or pattern channel multiple times; the PubSubController keeps a list of callbacks and executes each callback in response to a message.

The return value is an action UnregisterCallbacksAction which will unregister the callbacks, which should typically used with bracket.

valueaddChannelsAndWait
  1. :: MonadIO m
  2. => PubSubController
  3. -> [(RedisChannel, MessageCallback)]

    the channels to subscribe to

  4. -> [(RedisPChannel, PMessageCallback)]

    the channels to psubscribe to

  5. -> m UnregisterCallbacksAction
#

Call addChannels and then wait for Redis to acknowledge that the channels are actually subscribed.

Note that this function waits for all pending subscription change requests, so if you for example call addChannelsAndWait from multiple threads simultaneously, they all will wait for all pending subscription changes to be acknowledged by Redis (this is due to the fact that we just track the total number of pending change requests sent to Redis and just wait until that count reaches zero).

This also correctly waits if the network connection dies during the subscription change. Say that the network connection dies right after we send a subscription change to Redis. pubSubForever will throw ConnectionLost and addChannelsAndWait will continue to wait. Once you recall pubSubForever with the same PubSubController, pubSubForever will open a new connection, send subscription commands for all channels in the PubSubController (which include the ones we are waiting for), and wait for the responses from Redis. Only once we receive the response from Redis that it has subscribed to all channels in PubSubController will addChannelsAndWait unblock and return.

Remove channels from the PubSubController, and if there is an active pubSubForever, send the unsubscribe commands to Redis. Note that as soon as this function returns, no more callbacks will be executed even if more messages arrive during the period when we request to unsubscribe from the channel and Redis actually processes the unsubscribe request. This function is thread-safe.

If you remove all channels, the connection in pubSubForever to redis will stay open and waiting for any new channels from a call to addChannels. If you really want to close the connection, use killThread or Control.Concurrent.Async.cancel to kill the thread running pubSubForever.

typetype UnregisterCallbacksAction = IO ()
#

An action that when executed will unregister the callbacks. It is returned from addChannels or addChannelsAndWait and typically you would use it in bracket to guarantee that you unsubscribe from channels. For example, if you are using websockets to distribute messages to clients, you could use something such as:

websocketConn <- Network.WebSockets.acceptRequest pending
let mycallback msg = Network.WebSockets.sendTextData websocketConn msg
bracket (addChannelsAndWait ctrl [("hello", mycallback)] []) id $ const $ do
  {- loop here calling Network.WebSockets.receiveData -}

Low-Level Command API

8 declarations
valuesendRequest :: (RedisCtx m f, RedisResult a) => [ByteString] -> m (f a)
#

sendRequest can be used to implement commands from experimental versions of Redis. An example of how to implement a command is given below.

-- |Redis DEBUG OBJECT command
debugObject :: ByteString -> Redis (Either Reply ByteString)
debugObject key = sendRequest ["DEBUG", "OBJECT", key]
datadata Reply
#

Low-level representation of replies from the Redis server.

Instances7Eq, Show, Generic, NFData, RedisResult, RedisCtx, …
datadata Status
#
Instances6Eq, Show, Generic, NFData, RedisResult, Rep
classclass RedisResult a where
#

Methods

Instances25RedisResult, …
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)
newtypenewtype HashSlot
#
Instances7Enum, Eq, Integral, Num, Ord, Real, …
  • Enum HashSlotDefined in hedis-0.15.2 · Database.Redis.Cluster.HashSlot
  • Eq HashSlotDefined in hedis-0.15.2 · Database.Redis.Cluster.HashSlot
  • Integral HashSlotDefined in hedis-0.15.2 · Database.Redis.Cluster.HashSlot
  • Num HashSlotDefined in hedis-0.15.2 · Database.Redis.Cluster.HashSlot
  • Ord HashSlotDefined in hedis-0.15.2 · Database.Redis.Cluster.HashSlot
  • Real HashSlotDefined in hedis-0.15.2 · Database.Redis.Cluster.HashSlot
  • Show HashSlotDefined in hedis-0.15.2 · Database.Redis.Cluster.HashSlot