packages feed

hedis 0.1 → 0.2

raw patch · 10 files changed

+200/−164 lines, 10 filesdep +resource-poolPVP ok

version bump matches the API change (PVP)

Dependencies added: resource-pool

API changes (from Hackage documentation)

- Database.Redis: class RedisResult a
- Database.Redis: data RedisConn
- Database.Redis: decode :: RedisResult a => Reply -> Either Reply a
- Database.Redis: defaultPort :: PortID
- Database.Redis: disconnect :: RedisConn -> IO ()
- Database.Redis: sendRequest :: RedisResult a => [ByteString] -> Redis (Either Reply a)
+ Database.Redis: ConnInfo :: HostName -> PortID -> Maybe ByteString -> ConnectInfo
+ Database.Redis: connectAuth :: ConnectInfo -> Maybe ByteString
+ Database.Redis: connectHost :: ConnectInfo -> HostName
+ Database.Redis: connectPort :: ConnectInfo -> PortID
+ Database.Redis: data ConnectInfo
+ Database.Redis: data Connection
+ Database.Redis: debugObject :: ByteString -> Redis (Either Reply ByteString)
+ Database.Redis: debugSegfault :: Redis (Either Reply Status)
+ Database.Redis: defaultConnectInfo :: ConnectInfo
- Database.Redis: connect :: HostName -> PortID -> IO RedisConn
+ Database.Redis: connect :: ConnectInfo -> IO Connection
- Database.Redis: runRedis :: RedisConn -> Redis a -> IO a
+ Database.Redis: runRedis :: Connection -> Redis a -> IO a

Files

benchmark/Benchmark.hs view
@@ -19,13 +19,13 @@     ----------------------------------------------------------------------     -- Preparation     ---    conn <- connect "localhost" (PortNumber 6379)+    conn <- connect defaultConnectInfo     runRedis conn $ do+        _ <- flushall         Right _ <- mset [ ("k1","v1"), ("k2","v2"), ("k3","v3")                         , ("k4","v4"), ("k5","v5") ]-        return ()     -    disconnect conn+        return ()          ----------------------------------------------------------------------     -- Spawn clients@@ -33,7 +33,7 @@     start <- newEmptyMVar     done  <- newEmptyMVar     replicateM_ nClients $ forkIO $ do-        c <- connect "localhost" (PortNumber 6379)+        c <- connect defaultConnectInfo         runRedis c $ forever $ do             action <- liftIO $ takeMVar start             replicateM_ (nRequests `div` nClients) $ action@@ -64,4 +64,3 @@         let expected = map Just ["v1","v2","v3","v4","v5"]         True <- return $ vs == expected         return ()-        
hedis.cabal view
@@ -1,5 +1,5 @@ name:               hedis-version:            0.1+version:            0.2 synopsis:     Client library for the Redis datastore: supports full command set,       pipelining.@@ -12,10 +12,7 @@     .     [Complete Redis 2.4 command set:] All Redis commands          (<http://redis.io/commands>) are available as haskell functions. The -        exceptions to the rule are a handfull of internal and debugging-        commands: MONITOR, DEBUG OBJECT, DEBUG SEGFAULT, SYNC. If needed, these-        commands can easily be implemented by the library user with the-        'sendRequest' function.+        exceptions to the rule are the MONITOR and SYNC commands.     .     [Pipelining \"Just Works\":] Commands are pipelined          (<http://redis.io/topics/pipelining>) as much as possible without any@@ -35,8 +32,12 @@ category:           Database build-type:         Simple cabal-version:      >=1.8+homepage:           https://github.com/informatikr/hedis+bug-reports:        https://github.com/informatikr/hedis/issues -bug-reports: https://github.com/informatikr/hedis/issues+source-repository head+  type:     git+  location: https://github.com/informatikr/hedis  flag benchmark     description: Build the benchmark executable.@@ -52,9 +53,11 @@                     bytestring == 0.9.*,                     bytestring-lexing == 0.2.*,                     mtl == 2.*,-                    network == 2.*+                    network == 2.*,+                    resource-pool == 0.2.0.* -  other-modules:    Database.Redis.Internal,+  other-modules:    Database.Redis.Core,+                    Database.Redis.Connection                     Database.Redis.PubSub,                     Database.Redis.Reply,                     Database.Redis.Request,@@ -64,17 +67,13 @@  executable hedis-benchmark     main-is: benchmark/Benchmark.hs-    ghc-options: -Wall -rtsopts-    ghc-prof-options: -auto-all-    if ! flag(benchmark)-        buildable: False-    else+    if flag(benchmark)         build-depends:             base >= 4,             mtl == 2.0.*,             hedis,-            time >= 1.2--source-repository head-  type:     git-  location: https://github.com/informatikr/hedis+            time >= 1.2        +    else+        buildable: False+    ghc-options: -Wall -rtsopts+    ghc-prof-options: -auto-all
src/Database/Redis.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE OverloadedStrings, CPP #-}+{-# LANGUAGE OverloadedStrings #-}  module Database.Redis (     @@ -6,8 +6,9 @@     Redis(), runRedis,          -- * Connection-    RedisConn, connect, disconnect,-    HostName,PortID(..),defaultPort,+    Connection, connect,+    ConnectInfo(..),defaultConnectInfo,+    HostName,PortID(..),          -- * Commands 	module Database.Redis.Commands,@@ -15,22 +16,13 @@     -- * Pub\/Sub     module Database.Redis.PubSub, -    -- * Low-Level Requests and Replies-    sendRequest,-    -- |'sendRequest' can be used to implement one of the unimplemented -    --  commands, as shown below.-    ---    -- @-    -- -- |Redis DEBUG OBJECT command-    -- debugObject :: ByteString -> 'Redis' (Either 'Reply' ByteString)-    -- debugObject key = 'sendRequest' [\"DEBUG\", \"OBJECT\", 'encode' key]-    -- @-    ---    Reply(..),Status(..),RedisResult(..)+    -- * Redis Return Types+    Reply(..),Status(..)      ) where -import Database.Redis.Internal+import Database.Redis.Core+import Database.Redis.Connection import Database.Redis.PubSub import Database.Redis.Reply import Database.Redis.Types
src/Database/Redis/Commands.hs view
@@ -74,6 +74,8 @@ configResetstat, -- |Reset the stats returned by INFO (<http://redis.io/commands/config-resetstat>). configSet, -- |Set a configuration parameter to the given value (<http://redis.io/commands/config-set>). dbsize, -- |Return the number of keys in the selected database (<http://redis.io/commands/dbsize>).+debugObject, -- |Get debugging information about a key (<http://redis.io/commands/debug-object>).+debugSegfault, -- |Make the server crash (<http://redis.io/commands/debug-segfault>). flushall, -- |Remove all keys from all databases (<http://redis.io/commands/flushall>). flushdb, -- |Remove all keys from the current database (<http://redis.io/commands/flushdb>). info, -- |Get information and statistics about the server (<http://redis.io/commands/info>).@@ -158,7 +160,7 @@ watch, -- |Watch the given keys to determine execution of the MULTI/EXEC block (<http://redis.io/commands/watch>).  -- * Unimplemented Commands--- |These commands are not implemented, as of now. Library users can implement them with the 'sendRequest' function.+-- |These commands are not implemented, as of now. -- -- * EVAL (<http://redis.io/commands/eval>) --@@ -166,12 +168,6 @@ -- * MONITOR (<http://redis.io/commands/monitor>) -- ----- * DEBUG OBJECT (<http://redis.io/commands/debug-object>)--------- * DEBUG SEGFAULT (<http://redis.io/commands/debug-segfault>)------ -- * SYNC (<http://redis.io/commands/sync>) -- ) where@@ -180,7 +176,7 @@ import Data.ByteString (ByteString) import Database.Redis.ManualCommands import Database.Redis.Types-import Database.Redis.Internal+import Database.Redis.Core import Database.Redis.Reply  flushall@@ -464,6 +460,11 @@     -> Redis (Either Reply Status) slaveof host port = sendRequest (["SLAVEOF"] ++ [encode host] ++ [encode port] ) +debugObject+    :: ByteString -- ^ key+    -> Redis (Either Reply ByteString)+debugObject key = sendRequest (["DEBUG","OBJECT"] ++ [encode key] )+ getset     :: ByteString -- ^ key     -> ByteString -- ^ value@@ -748,6 +749,10 @@     :: Redis (Either Reply Status) discard  = sendRequest (["DISCARD"] ) +debugSegfault+    :: Redis (Either Reply Status)+debugSegfault  = sendRequest (["DEBUG","SEGFAULT"] )+ srandmember     :: ByteString -- ^ key     -> Redis (Either Reply ByteString)@@ -760,18 +765,12 @@   -- * Unimplemented Commands--- |These commands are not implemented, as of now. Library users can implement them with the 'sendRequest' function.+-- |These commands are not implemented, as of now. -- -- * EVAL (<http://redis.io/commands/eval>) -- -- -- * MONITOR (<http://redis.io/commands/monitor>)--------- * DEBUG OBJECT (<http://redis.io/commands/debug-object>)--------- * DEBUG SEGFAULT (<http://redis.io/commands/debug-segfault>) -- -- -- * SYNC (<http://redis.io/commands/sync>)
+ src/Database/Redis/Connection.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE RecordWildCards #-}++module Database.Redis.Connection (+    HostName,PortID(..),+    ConnectInfo(..),defaultConnectInfo,+    Connection(), connect+) where++import Control.Applicative+import Control.Monad.Reader+import Control.Concurrent+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy.Char8 as LB+import Data.IORef+import Data.Pool+import Network (HostName, PortID(..), connectTo)+import System.IO (hClose, hIsOpen, hSetBinaryMode)++import Database.Redis.Core+import Database.Redis.Commands (auth)+import Database.Redis.Reply++-- |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\"}+-- @+--+data ConnectInfo = ConnInfo+    { connectHost :: HostName+    , connectPort :: PortID+    , connectAuth :: Maybe B.ByteString+    }++-- |Default information for connecting:+--+-- @+--  connectHost = \"localhost\"+--  connectPort = PortNumber 6379 -- Redis default port+--  connectAuth = Nothing         -- No password+-- @+--+defaultConnectInfo :: ConnectInfo+defaultConnectInfo = ConnInfo+    { connectHost = "localhost"+    , connectPort = PortNumber 6379+    , connectAuth = Nothing+    }++-- |Opens a connection to a Redis server designated by the given 'ConnectInfo'.+connect :: ConnectInfo -> IO Connection+connect ConnInfo{..} = do+    let maxIdleTime    = 10+        maxConnections = 50+    Conn <$> createPool create destroy 1 maxIdleTime maxConnections+  where+    create = do+        h   <- connectTo connectHost connectPort+        rs' <- parseReply <$> {-# SCC "LB.hgetContents" #-} LB.hGetContents h+        rs  <- newIORef rs'+        let conn = (h,rs)+        maybe (return ())+            (\pass -> runRedisInternal conn (auth pass) >> return ())+            connectAuth+        hSetBinaryMode h True+        newMVar conn++    destroy conn = withMVar conn $ \(h,_) -> do+        open <- hIsOpen h+        when open (hClose h)
+ src/Database/Redis/Core.hs view
@@ -0,0 +1,69 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+module Database.Redis.Core (+    Connection(..),+    Redis(),runRedis,runRedisInternal,+    send,+    recv,+    sendRequest+) where++import Control.Applicative+import Control.Arrow+import Control.Monad.Reader+import Control.Concurrent+import qualified Data.ByteString as B+import Data.IORef+import Data.Pool+import System.IO (Handle, hFlush)++import Database.Redis.Reply+import Database.Redis.Request+import Database.Redis.Types++-- |Connection to a Redis server. Use the 'connect' function to create one.+--+--  A 'Connection' is actually a pool of network connections.+newtype Connection = Conn (Pool (MVar (Handle, IORef [Reply])))++-- |All Redis commands run in the 'Redis' monad.+newtype Redis a = Redis (ReaderT RedisEnv IO a)+    deriving (Monad, MonadIO, Functor, Applicative)++type RedisEnv = (Handle, IORef [Reply])++askHandle :: ReaderT RedisEnv IO Handle+askHandle = asks fst++askReplies :: ReaderT RedisEnv IO (IORef [Reply])+askReplies = asks snd++-- |Interact with a Redis datastore specified by the given 'Connection'.+--+--  Each call of 'runRedis' takes a network connection from the 'Connection'+--  pool and runs the given 'Redis' action. Calls to 'runRedis' may thus block, --  while all connections from the pool are in use.+runRedis :: Connection -> Redis a -> IO a+runRedis (Conn pool) redis =+    withResource pool $ \conn ->+    withMVar conn $ \conn' -> runRedisInternal conn' redis++-- |Internal version of 'runRedis' that does not depend on the 'Connection'+--  abstraction. Used to run the AUTH command when connecting. +runRedisInternal :: RedisEnv -> Redis a -> IO a+runRedisInternal env (Redis redis) = runReaderT redis env++send :: [B.ByteString] -> Redis ()+send req = Redis $ do+    h <- askHandle+    liftIO $ do+        {-# SCC "send.hPut" #-} B.hPut h $ renderRequest req+        {-# SCC "send.hFlush" #-} hFlush h++recv :: Redis Reply+recv = Redis $ do+    -- head/tail avoids forcing the ':' constructor, enabling automatic+    -- pipelining.+    rs <- askReplies+    liftIO $ atomicModifyIORef rs (tail &&& head)++sendRequest :: (RedisResult a) => [B.ByteString] -> Redis (Either Reply a)+sendRequest req = decode <$> (send req >> recv)
− src/Database/Redis/Internal.hs
@@ -1,99 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-module Database.Redis.Internal (-    HostName,PortID(..), defaultPort,-    RedisConn(), connect, disconnect,-    Redis(),runRedis,-    send,-    recv,-    sendRequest-) where--import Control.Applicative-import Control.Monad.RWS-import Control.Concurrent-import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy.Char8 as LB-import Network (HostName, PortID(..), connectTo)-import System.IO (Handle, hFlush, hClose, hIsOpen)--import Database.Redis.Reply-import Database.Redis.Request-import Database.Redis.Types----------------------------------------------------------------------------------- Connection------- |Connection to a Redis server. Use the 'connect' function to create one.------  A 'RedisConn' can only be used by a single thread at a time. This means that---  calls to 'runRedis' or 'disconnet' may block when the 'RedisConn' is shared---  between multiple threads.-data RedisConn = Conn (MVar (Handle, [Reply]))--withConn :: RedisConn-         -> (Handle -> [Reply] -> IO ([Reply], a))-         -> IO a-withConn (Conn conn) f = do-    (h,rs)     <- takeMVar conn-    (rs',a) <- f h rs-    putMVar conn (h,rs')-    return a---- |Opens a connection to a Redis server at the given host and port.-connect :: HostName -> PortID -> IO RedisConn-connect host port = do-    h       <- connectTo host port-    replies <- parseReply <$> LB.hGetContents h-    Conn <$> newMVar (h, replies)---- |Close the given connection.------  May block when the given 'RedisConn' is shared between multiple threads. The--- 'RedisConn' can not be re-used.-disconnect :: RedisConn -> IO ()-disconnect conn = withConn conn $ \h rs -> do-    open <- hIsOpen h-    when open (hClose h)-    return (rs, ())---- | The Redis default port 6379. Equivalent to @'PortNumber' 6379@.-defaultPort :: PortID-defaultPort = PortNumber 6379----------------------------------------------------------------------------------- The Redis Monad----newtype Redis a = Redis (RWST Handle () [Reply] IO a)-    deriving (Monad, MonadIO, Functor, Applicative)---- |Interact with a Redis datastore specified by the given 'RedisConn'.------  May block when the given 'RedisConn' is shared between multiple threads.-runRedis :: RedisConn -> Redis a -> IO a-runRedis conn (Redis redis) = withConn conn $ \h rs -> do-    open <- hIsOpen h-    if open-        then do-            (a,rs',_) <- runRWST redis h rs-            return (rs',a)-        else error "Redis: disconnected"--send :: [B.ByteString] -> Redis ()-send req = Redis $ do-    h <- ask-    liftIO $ do-        B.hPut h $ renderRequest req-        hFlush h--recv :: Redis Reply-recv = Redis $ do-    -- head/tail avoids forcing the ':' constructor, enabling automatic-    -- pipelining.-    rs <- get-    put (tail rs)-    return (head rs)---- |Sends a request to the Redis server, returning the 'decode'd reply.-sendRequest :: (RedisResult a) => [B.ByteString] -> Redis (Either Reply a)-sendRequest req = decode <$> (send req >> recv)
src/Database/Redis/ManualCommands.hs view
@@ -4,7 +4,7 @@  import Prelude hiding (min,max) import Data.ByteString (ByteString)-import Database.Redis.Internal+import Database.Redis.Core import Database.Redis.Reply import Database.Redis.Types 
src/Database/Redis/PubSub.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-} module Database.Redis.PubSub (     publish,     pubSub,@@ -13,8 +12,7 @@ import Control.Monad.Writer import Data.ByteString.Char8 (ByteString) import Data.Maybe-import Database.Redis.Internal (Redis)-import qualified Database.Redis.Internal as Internal+import qualified Database.Redis.Core as Core import Database.Redis.Reply import Database.Redis.Types @@ -37,9 +35,9 @@ publish     :: ByteString -- ^ channel     -> ByteString -- ^ message-    -> Redis (Either Reply Integer)+    -> Core.Redis (Either Reply Integer) publish channel message =-    Internal.sendRequest ["PUBLISH", channel, message]+    Core.sendRequest ["PUBLISH", channel, message]  -- |Listen for messages published to the given channels --  (<http://redis.io/commands/subscribe>).@@ -94,15 +92,15 @@ pubSub     :: PubSub                 -- ^ Initial subscriptions.     -> (Message -> IO PubSub) -- ^ Callback function.-    -> Redis ()+    -> Core.Redis () pubSub p callback = send p 0   where     send (PubSub cmds) pending = do-        mapM_ Internal.send cmds+        mapM_ Core.send cmds         recv (pending + length cmds)      recv pending = do-        reply <- Internal.recv        +        reply <- Core.recv                 case decodeMsg reply of             Left cnt                 | cnt == 0 && pending == 0
src/Database/Redis/Types.hs view
@@ -1,5 +1,5 @@-{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances,-        TypeSynonymInstances, OverloadedStrings #-}+{-# LANGUAGE FlexibleInstances, OverlappingInstances, TypeSynonymInstances,+    OverloadedStrings #-}  module Database.Redis.Types where @@ -8,8 +8,10 @@ import Data.ByteString.Char8 (ByteString, pack) import Data.ByteString.Lex.Double (readDouble) import Data.Maybe+ import Database.Redis.Reply + ------------------------------------------------------------------------------ -- Classes of types Redis understands --@@ -41,6 +43,7 @@     decode = Right  instance RedisResult ByteString where+    decode (SingleLine s)  = Right s     decode (Bulk (Just s)) = Right s     decode r               = Left r