memcache 0.1.0.0 → 0.1.0.1
raw patch · 11 files changed
+154/−71 lines, 11 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Database.Memcache.Client: optsCmdFailure :: Options -> !FailureMode
- Database.Memcache.Client: optsServerFailure :: Options -> !FailureMode
- Database.Memcache.Client: optsServerRetries :: Options -> !Int
- Database.Memcache.Client: password :: Authentication -> !Password
- Database.Memcache.Client: ssAuth :: ServerSpec -> Authentication
- Database.Memcache.Client: ssHost :: ServerSpec -> HostName
- Database.Memcache.Client: ssPort :: ServerSpec -> PortNumber
- Database.Memcache.Client: username :: Authentication -> !Username
- Database.Memcache.Cluster: instance Eq FailureMode
- Database.Memcache.Cluster: instance Show Cluster
- Database.Memcache.Cluster: instance Show FailureMode
- Database.Memcache.Cluster: optsCmdFailure :: Options -> !FailureMode
- Database.Memcache.Cluster: optsServerFailure :: Options -> !FailureMode
- Database.Memcache.Cluster: optsServerRetries :: Options -> !Int
- Database.Memcache.Cluster: ssAuth :: ServerSpec -> Authentication
- Database.Memcache.Cluster: ssHost :: ServerSpec -> HostName
- Database.Memcache.Cluster: ssPort :: ServerSpec -> PortNumber
- Database.Memcache.Errors: instance Eq ClientError
- Database.Memcache.Errors: instance Eq MemcacheError
- Database.Memcache.Errors: instance Exception ClientError
- Database.Memcache.Errors: instance Exception MemcacheError
- Database.Memcache.Errors: instance Show ClientError
- Database.Memcache.Errors: instance Show MemcacheError
- Database.Memcache.Errors: instance Typeable ClientError
- Database.Memcache.Errors: instance Typeable MemcacheError
- Database.Memcache.SASL: password :: Authentication -> !Password
- Database.Memcache.SASL: username :: Authentication -> !Username
- Database.Memcache.Server: instance Eq Server
- Database.Memcache.Server: instance Ord Server
- Database.Memcache.Server: instance Show Server
- Database.Memcache.Types: bodyLen :: Header -> Word32
- Database.Memcache.Types: cas :: Header -> Version
- Database.Memcache.Types: extraLen :: Header -> Word8
- Database.Memcache.Types: increspActual :: IncorrectResponse -> Response
- Database.Memcache.Types: increspMessage :: IncorrectResponse -> String
- Database.Memcache.Types: instance Eq Authentication
- Database.Memcache.Types: instance Eq Header
- Database.Memcache.Types: instance Eq IncorrectResponse
- Database.Memcache.Types: instance Eq K
- Database.Memcache.Types: instance Eq OpRequest
- Database.Memcache.Types: instance Eq OpResponse
- Database.Memcache.Types: instance Eq ProtocolError
- Database.Memcache.Types: instance Eq Q
- Database.Memcache.Types: instance Eq Request
- Database.Memcache.Types: instance Eq Response
- Database.Memcache.Types: instance Eq SEIncr
- Database.Memcache.Types: instance Eq SESet
- Database.Memcache.Types: instance Eq SETouch
- Database.Memcache.Types: instance Eq Status
- Database.Memcache.Types: instance Exception IncorrectResponse
- Database.Memcache.Types: instance Exception ProtocolError
- Database.Memcache.Types: instance Show Authentication
- Database.Memcache.Types: instance Show Header
- Database.Memcache.Types: instance Show IncorrectResponse
- Database.Memcache.Types: instance Show K
- Database.Memcache.Types: instance Show OpRequest
- Database.Memcache.Types: instance Show OpResponse
- Database.Memcache.Types: instance Show ProtocolError
- Database.Memcache.Types: instance Show Q
- Database.Memcache.Types: instance Show Request
- Database.Memcache.Types: instance Show Response
- Database.Memcache.Types: instance Show SEIncr
- Database.Memcache.Types: instance Show SESet
- Database.Memcache.Types: instance Show SETouch
- Database.Memcache.Types: instance Show Status
- Database.Memcache.Types: instance Typeable Header
- Database.Memcache.Types: instance Typeable IncorrectResponse
- Database.Memcache.Types: instance Typeable K
- Database.Memcache.Types: instance Typeable OpRequest
- Database.Memcache.Types: instance Typeable OpResponse
- Database.Memcache.Types: instance Typeable ProtocolError
- Database.Memcache.Types: instance Typeable Q
- Database.Memcache.Types: instance Typeable Request
- Database.Memcache.Types: instance Typeable Response
- Database.Memcache.Types: instance Typeable SEIncr
- Database.Memcache.Types: instance Typeable SESet
- Database.Memcache.Types: instance Typeable SETouch
- Database.Memcache.Types: instance Typeable Status
- Database.Memcache.Types: keyLen :: Header -> Word16
- Database.Memcache.Types: op :: Header -> Word8
- Database.Memcache.Types: opaque :: Header -> Word32
- Database.Memcache.Types: password :: Authentication -> !Password
- Database.Memcache.Types: protocolHeader :: ProtocolError -> Maybe Header
- Database.Memcache.Types: protocolMessage :: ProtocolError -> String
- Database.Memcache.Types: protocolParams :: ProtocolError -> [String]
- Database.Memcache.Types: reqCas :: Request -> Version
- Database.Memcache.Types: reqOp :: Request -> OpRequest
- Database.Memcache.Types: reqOpaque :: Request -> Word32
- Database.Memcache.Types: resCas :: Response -> Version
- Database.Memcache.Types: resOp :: Response -> OpResponse
- Database.Memcache.Types: resOpaque :: Response -> Word32
- Database.Memcache.Types: resStatus :: Response -> Status
- Database.Memcache.Types: status :: Header -> Status
- Database.Memcache.Types: username :: Authentication -> !Username
+ Database.Memcache.Client: [optsCmdFailure] :: Options -> !FailureMode
+ Database.Memcache.Client: [optsServerFailure] :: Options -> !FailureMode
+ Database.Memcache.Client: [optsServerRetries] :: Options -> !Int
+ Database.Memcache.Client: [password] :: Authentication -> !Password
+ Database.Memcache.Client: [ssAuth] :: ServerSpec -> Authentication
+ Database.Memcache.Client: [ssHost] :: ServerSpec -> HostName
+ Database.Memcache.Client: [ssPort] :: ServerSpec -> PortNumber
+ Database.Memcache.Client: [username] :: Authentication -> !Username
+ Database.Memcache.Cluster: [optsCmdFailure] :: Options -> !FailureMode
+ Database.Memcache.Cluster: [optsServerFailure] :: Options -> !FailureMode
+ Database.Memcache.Cluster: [optsServerRetries] :: Options -> !Int
+ Database.Memcache.Cluster: [ssAuth] :: ServerSpec -> Authentication
+ Database.Memcache.Cluster: [ssHost] :: ServerSpec -> HostName
+ Database.Memcache.Cluster: [ssPort] :: ServerSpec -> PortNumber
+ Database.Memcache.Cluster: instance GHC.Classes.Eq Database.Memcache.Cluster.FailureMode
+ Database.Memcache.Cluster: instance GHC.Show.Show Database.Memcache.Cluster.Cluster
+ Database.Memcache.Cluster: instance GHC.Show.Show Database.Memcache.Cluster.FailureMode
+ Database.Memcache.Errors: instance GHC.Classes.Eq Database.Memcache.Errors.ClientError
+ Database.Memcache.Errors: instance GHC.Classes.Eq Database.Memcache.Errors.MemcacheError
+ Database.Memcache.Errors: instance GHC.Exception.Exception Database.Memcache.Errors.ClientError
+ Database.Memcache.Errors: instance GHC.Exception.Exception Database.Memcache.Errors.MemcacheError
+ Database.Memcache.Errors: instance GHC.Show.Show Database.Memcache.Errors.ClientError
+ Database.Memcache.Errors: instance GHC.Show.Show Database.Memcache.Errors.MemcacheError
+ Database.Memcache.SASL: [password] :: Authentication -> !Password
+ Database.Memcache.SASL: [username] :: Authentication -> !Username
+ Database.Memcache.Server: instance GHC.Classes.Eq Database.Memcache.Server.Server
+ Database.Memcache.Server: instance GHC.Classes.Ord Database.Memcache.Server.Server
+ Database.Memcache.Server: instance GHC.Show.Show Database.Memcache.Server.Server
+ Database.Memcache.Types: [bodyLen] :: Header -> Word32
+ Database.Memcache.Types: [cas] :: Header -> Version
+ Database.Memcache.Types: [extraLen] :: Header -> Word8
+ Database.Memcache.Types: [increspActual] :: IncorrectResponse -> Response
+ Database.Memcache.Types: [increspMessage] :: IncorrectResponse -> String
+ Database.Memcache.Types: [keyLen] :: Header -> Word16
+ Database.Memcache.Types: [op] :: Header -> Word8
+ Database.Memcache.Types: [opaque] :: Header -> Word32
+ Database.Memcache.Types: [password] :: Authentication -> !Password
+ Database.Memcache.Types: [protocolHeader] :: ProtocolError -> Maybe Header
+ Database.Memcache.Types: [protocolMessage] :: ProtocolError -> String
+ Database.Memcache.Types: [protocolParams] :: ProtocolError -> [String]
+ Database.Memcache.Types: [reqCas] :: Request -> Version
+ Database.Memcache.Types: [reqOp] :: Request -> OpRequest
+ Database.Memcache.Types: [reqOpaque] :: Request -> Word32
+ Database.Memcache.Types: [resCas] :: Response -> Version
+ Database.Memcache.Types: [resOp] :: Response -> OpResponse
+ Database.Memcache.Types: [resOpaque] :: Response -> Word32
+ Database.Memcache.Types: [resStatus] :: Response -> Status
+ Database.Memcache.Types: [status] :: Header -> Status
+ Database.Memcache.Types: [username] :: Authentication -> !Username
+ Database.Memcache.Types: instance GHC.Classes.Eq Database.Memcache.Types.Authentication
+ Database.Memcache.Types: instance GHC.Classes.Eq Database.Memcache.Types.Header
+ Database.Memcache.Types: instance GHC.Classes.Eq Database.Memcache.Types.IncorrectResponse
+ Database.Memcache.Types: instance GHC.Classes.Eq Database.Memcache.Types.K
+ Database.Memcache.Types: instance GHC.Classes.Eq Database.Memcache.Types.OpRequest
+ Database.Memcache.Types: instance GHC.Classes.Eq Database.Memcache.Types.OpResponse
+ Database.Memcache.Types: instance GHC.Classes.Eq Database.Memcache.Types.ProtocolError
+ Database.Memcache.Types: instance GHC.Classes.Eq Database.Memcache.Types.Q
+ Database.Memcache.Types: instance GHC.Classes.Eq Database.Memcache.Types.Request
+ Database.Memcache.Types: instance GHC.Classes.Eq Database.Memcache.Types.Response
+ Database.Memcache.Types: instance GHC.Classes.Eq Database.Memcache.Types.SEIncr
+ Database.Memcache.Types: instance GHC.Classes.Eq Database.Memcache.Types.SESet
+ Database.Memcache.Types: instance GHC.Classes.Eq Database.Memcache.Types.SETouch
+ Database.Memcache.Types: instance GHC.Classes.Eq Database.Memcache.Types.Status
+ Database.Memcache.Types: instance GHC.Exception.Exception Database.Memcache.Types.IncorrectResponse
+ Database.Memcache.Types: instance GHC.Exception.Exception Database.Memcache.Types.ProtocolError
+ Database.Memcache.Types: instance GHC.Show.Show Database.Memcache.Types.Authentication
+ Database.Memcache.Types: instance GHC.Show.Show Database.Memcache.Types.Header
+ Database.Memcache.Types: instance GHC.Show.Show Database.Memcache.Types.IncorrectResponse
+ Database.Memcache.Types: instance GHC.Show.Show Database.Memcache.Types.K
+ Database.Memcache.Types: instance GHC.Show.Show Database.Memcache.Types.OpRequest
+ Database.Memcache.Types: instance GHC.Show.Show Database.Memcache.Types.OpResponse
+ Database.Memcache.Types: instance GHC.Show.Show Database.Memcache.Types.ProtocolError
+ Database.Memcache.Types: instance GHC.Show.Show Database.Memcache.Types.Q
+ Database.Memcache.Types: instance GHC.Show.Show Database.Memcache.Types.Request
+ Database.Memcache.Types: instance GHC.Show.Show Database.Memcache.Types.Response
+ Database.Memcache.Types: instance GHC.Show.Show Database.Memcache.Types.SEIncr
+ Database.Memcache.Types: instance GHC.Show.Show Database.Memcache.Types.SESet
+ Database.Memcache.Types: instance GHC.Show.Show Database.Memcache.Types.SETouch
+ Database.Memcache.Types: instance GHC.Show.Show Database.Memcache.Types.Status
Files
- CHANGELOG.md +6/−0
- Database/Memcache/Client.hs +55/−9
- Database/Memcache/Cluster.hs +6/−6
- Database/Memcache/Errors.hs +1/−1
- Database/Memcache/Protocol.hs +1/−1
- Database/Memcache/SASL.hs +1/−1
- Database/Memcache/Server.hs +6/−7
- Database/Memcache/Types.hs +1/−1
- Database/Memcache/Wire.hs +35/−16
- README.md +33/−23
- memcache.cabal +9/−6
CHANGELOG.md view
@@ -1,3 +1,9 @@+# 0.1.X.X++* Consistent usage of 'memcached' instead of 'memcache'.+* Document `Database.Memcache.Client`+* Add inline pragmas in appropriate places.+ # 0.1.0.0 * First proper release (although still lots of rough edges!)
Database/Memcache/Client.hs view
@@ -1,16 +1,16 @@--- | A memcache client. Supports the binary protocol (only) and SASL+-- | A memcached client. Supports the binary protocol (only) and SASL -- authentication. ----- A client can connect to a single memcache server or a cluster of them. In+-- A client can connect to a single memcached server or a cluster of them. In -- the later case, consistent hashing is used to route requests to the -- appropriate server. ----- Expected return values (like misses) are returned as part of--- the return type, while unexpected errors are thrown as exceptions.+-- Expected return values (like misses) are returned as part of the return+-- type, while unexpected errors are thrown as exceptions. module Database.Memcache.Client ( -- * Cluster and connection handling newClient, Client, ServerSpec(..), defaultServerSpec, Options(..),- defaultOptions, Authentication(..), Username, Password,+ defaultOptions, Authentication(..), Username, Password, quit, -- * Get operations get, gat, touch,@@ -25,7 +25,7 @@ increment, decrement, append, prepend, -- * Information operations- version, stats, P.StatResults, quit,+ version, stats, P.StatResults, -- * Error handling MemcacheError(..), ClientError(..)@@ -41,61 +41,107 @@ import Data.Word import Data.ByteString (ByteString) +-- | A memcached client, connected to a collection of memcached servers. type Client = Cluster +-- | Establish a new connection to a group of memcached servers. newClient :: [ServerSpec] -> Options -> IO Client newClient = newCluster +-- | Gracefully close a connection to a memcached cluster.+quit :: Cluster -> IO ()+quit c = void $ allOp (Just ()) c $ \s -> P.quit s+ keyedOp' :: Cluster -> Key -> (Server -> IO (Maybe a)) -> IO (Maybe a) keyedOp' = keyedOp (Just Nothing)+{-# INLINE keyedOp' #-} +-- | Retrieve the value for the given key from memcache. get :: Cluster -> Key -> IO (Maybe (Value, Flags, Version)) get c k = keyedOp' c k $ \s -> P.get s k+{-# INLINE get #-} +-- | Get-and-touch: Retrieve the value for the given key from memcache, and+-- also update the stored key-value pairs expiration time at the server. gat :: Cluster -> Key -> Expiration -> IO (Maybe (Value, Flags, Version)) gat c k e = keyedOp' c k $ \s -> P.gat s k e+{-# INLINE gat #-} +-- | Update the expiration time of a stored key-value pair, returning its+-- version identifier. touch :: Cluster -> Key -> Expiration -> IO (Maybe Version) touch c k e = keyedOp' c k $ \s -> P.touch s k e+{-# INLINE touch #-} +-- | Store a new (or overwrite exisiting) key-value pair, returning its version+-- identifier. set :: Cluster -> Key -> Value -> Flags -> Expiration -> IO Version set c k v f e = keyedOp (Just 0) c k $ \s -> P.set s k v f e+{-# INLINE set #-} +-- | Store a key-value pair, but only if the version specified by the client+-- matches the version of the key-value pair at the server. The version+-- identifier of the stored key-value pair is returned, or if the version match+-- fails, 'Nothing' is returned. set' :: Cluster -> Key -> Value -> Flags -> Expiration -> Version -> IO (Maybe Version) set' c k v f e ver = keyedOp' c k $ \s -> P.set' s k v f e ver+{-# INLINE set' #-} +-- | Store a new key-value pair, returning it's version identifier. If the+-- key-value pair already exists, then fail (return 'Nothing'). add :: Cluster -> Key -> Value -> Flags -> Expiration -> IO (Maybe Version) add c k v f e = keyedOp' c k $ \s -> P.add s k v f e+{-# INLINE add #-} +-- | Update the value of an existing key-value pair, returning it's new version+-- identifier. If the key doesn't already exist, the fail and return Nothing. replace :: Cluster -> Key -> Value -> Flags -> Expiration -> Version -> IO (Maybe Version) replace c k v f e ver = keyedOp' c k $ \s -> P.replace s k v f e ver+{-# INLINE replace #-} +-- | Delete a key-value pair at the server, returning true if successful. delete :: Cluster -> Key -> Version -> IO Bool delete c k ver = keyedOp (Just False) c k $ \s -> P.delete s k ver+{-# INLINE delete #-} +-- | Increment a numeric value stored against a key, returning the incremented+-- value and the version identifier of the key-value pair. increment :: Cluster -> Key -> Initial -> Delta -> Expiration -> Version -> IO (Maybe (Word64, Version)) increment c k i d e ver = keyedOp' c k $ \s -> P.increment s k i d e ver+{-# INLINE increment #-} +-- | Decrement a numeric value stored against a key, returning the decremented+-- value and the version identifier of the key-value pair. decrement :: Cluster -> Key -> Initial -> Delta -> Expiration -> Version -> IO (Maybe (Word64, Version)) decrement c k i d e ver = keyedOp' c k $ \s -> P.decrement s k i d e ver+{-# INLINE decrement #-} +-- | Append a value to an existing key-value pair, returning the new version+-- identifier of the key-value pair when successful. append :: Cluster -> Key -> Value -> Version -> IO (Maybe Version) append c k v ver = keyedOp' c k $ \s -> P.append s k v ver+{-# INLINE append #-} +-- | Prepend a value to an existing key-value pair, returning the new version+-- identifier of the key-value pair when successful. prepend :: Cluster -> Key -> Value -> Version -> IO (Maybe Version) prepend c k v ver = keyedOp' c k $ \s -> P.prepend s k v ver+{-# INLINE prepend #-} +-- | Remove (delete) all currently stored key-value pairs from the cluster. flush :: Cluster -> Maybe Expiration -> IO () flush c e = void $ allOp (Just ()) c $ \s -> P.flush s e+{-# INLINE flush #-} +-- | Return statistics on the stored key-value pairs at each server in the+-- cluster. stats :: Cluster -> Maybe Key -> IO [(Server, Maybe P.StatResults)] stats c key = allOp Nothing c $ \s -> P.stats s key--quit :: Cluster -> IO ()-quit c = void $ allOp (Just ()) c $ \s -> P.quit s+{-# INLINE stats #-} -- | Version returns the version string of the memcached cluster. We just query -- one server and assume all servers in the cluster are the same version. version :: Cluster -> IO ByteString version c = anyOp Nothing c $ \s -> P.version s+{-# INLINE version #-}
Database/Memcache/Cluster.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE RecordWildCards #-} --- | Handles a group of connections to different memcache servers.+-- | Handles a group of connections to different memcached servers. module Database.Memcache.Cluster ( Cluster, newCluster, ServerSpec(..), defaultServerSpec,@@ -38,7 +38,7 @@ ssAuth = NoAuth } --- | Options specifies how a memcache cluster should be configured.+-- | Options specifies how a memcached cluster should be configured. data Options = Options { optsCmdFailure :: !FailureMode, optsServerFailure :: !FailureMode,@@ -76,16 +76,16 @@ searchFun svr = sid svr < hashedKey in fromMaybe (V.last $ servers c) $ V.find searchFun (servers c) --- | Run a memcache operation against a server that maps to the key given in+-- | Run a memcached operation against a server that maps to the key given in -- the cluster. keyedOp :: forall a. Maybe a -> Cluster -> Key -> (Server -> IO a) -> IO a keyedOp def c k = serverOp def c (getServerForKey c k) --- | Run a memcache operation against any single server in the cluster.+-- | Run a memcached operation against any single server in the cluster. anyOp :: forall a. Maybe a -> Cluster -> (Server -> IO a) -> IO a anyOp def c = serverOp def c (V.head . servers $ c) --- | Run a memcache operation against all servers in the cluster.+-- | Run a memcached operation against all servers in the cluster. allOp :: forall a. Maybe a -> Cluster -> (Server -> IO a) -> IO [(Server, a)] allOp def c m = do res <- V.forM (servers c) (\s -> serverOp def c s m)@@ -118,7 +118,7 @@ data FailureMode = FailSilent | FailToBackup | FailToError deriving (Eq, Show) --- | Run a memcache operation against a particular server, handling any+-- | Run a memcached operation against a particular server, handling any -- failures that occur. serverOp :: forall a. Maybe a -> Cluster -> Server -> (Server -> IO a) -> IO a serverOp def c s m = go $ serverRetries c
Database/Memcache/Errors.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE DeriveDataTypeable #-} --- | Memcache related errors and exception handling.+-- | Memcached related errors and exception handling. module Database.Memcache.Errors ( MemcacheError(..), statusToError,
Database/Memcache/Protocol.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE ScopedTypeVariables #-} --- | A raw, low level interface to the memcache protocol.+-- | A raw, low level interface to the memcached protocol. -- -- The various operations are represented in full as they appear at the -- protocol level and so aren't generally well suited for application use.
Database/Memcache/SASL.hs view
@@ -1,6 +1,6 @@ {-# OPTIONS_GHC -fno-warn-unused-binds #-} --- | SASL authentication support for memcached.+-- | SASL authentication support for memcache. module Database.Memcache.SASL ( authenticate, Authentication(..), Username, Password ) where
Database/Memcache/Server.hs view
@@ -1,4 +1,4 @@--- | Handles the connections between a memcache client and a single server.+-- | Handles the connections between a memcached client and a single server. module Database.Memcache.Server ( Server(sid), newServer, sendRecv, withSocket, close ) where@@ -40,7 +40,6 @@ -- cnxnBuf :: IORef ByteString } deriving Show - instance Eq Server where (==) x y = sid x == sid y @@ -53,11 +52,11 @@ pSock <- createPool connectSocket releaseSocket sSTRIPES sKEEPALIVE sCONNECTIONS return Server- { sid = serverHash- , pool = pSock- , _addr = host- , _port = port- , _auth = auth+ { sid = serverHash+ , pool = pSock+ , _addr = host+ , _port = port+ , _auth = auth , failed = False } where
Database/Memcache/Types.hs view
@@ -28,7 +28,7 @@ -- | Password for authentication. type Password = ByteString -{- MEMCACHE MESSAGE:+{- MEMCACHED MESSAGE: header { magic :: Word8
Database/Memcache/Wire.hs view
@@ -1,3 +1,6 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE BangPatterns #-}+ -- | Deals with serializing and parsing memcached requests and responses. module Database.Memcache.Wire ( send, recv,@@ -8,18 +11,21 @@ -- XXX: Wire works with lazy bytestrings but we receive strict bytestrings from -- the network... -import Database.Memcache.Errors-import Database.Memcache.Types+import Database.Memcache.Errors+import Database.Memcache.Types -import Control.Exception-import Control.Monad-import Blaze.ByteString.Builder-import Data.Binary.Get+import Blaze.ByteString.Builder+#if __GLASGOW_HASKELL__ < 710+import Control.Applicative+#endif+import Control.Exception+import Control.Monad+import Data.Binary.Get import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as L-import Data.Monoid-import Data.Word-import Network.Socket (Socket)+import Data.Monoid+import Data.Word+import Network.Socket (Socket, isConnected, isReadable) import qualified Network.Socket.ByteString as N -- | Send a request to the memcached server.@@ -30,18 +36,31 @@ -- TODO: read into buffer to minimize read syscalls recv :: Socket -> IO Response recv s = do- header <- recvAll mEMCACHE_HEADER_SIZE+ header <- recvAll mEMCACHE_HEADER_SIZE mempty let h = dzHeader' (L.fromChunks [header]) if bodyLen h > 0- then do body <- recvAll (fromIntegral $ bodyLen h)- return $ dzBody' h (L.fromChunks [body])+ then do+ let bytesToRead = fromIntegral $ bodyLen h+ body <- recvAll bytesToRead mempty+ return $ dzBody' h (L.fromChunks [body]) else return $ dzBody' h L.empty where- recvAll n = do- buf <- N.recv s n- if B.length buf == n- then return buf+ recvAll :: Int -> Builder -> IO B.ByteString+ recvAll 0 !acc = return $! toByteString acc+ recvAll !n !acc = do+ canRead <- isSocketActive s+ if canRead+ then do+ buf <- N.recv s n+ let bufLen = B.length buf+ if bufLen == n+ then return $! (toByteString $! acc <> fromByteString buf)+ else recvAll (max 0 (n - bufLen)) (acc <> fromByteString buf) else throwIO NotEnoughBytes++-- | Check whether we can still operate on this socket or not.+isSocketActive :: Socket -> IO Bool+isSocketActive s = (&&) <$> isConnected s <*> isReadable s -- | Serialize a request to a ByteString. szRequest' :: Request -> L.ByteString
README.md view
@@ -1,7 +1,15 @@-# memcache: Haskell Memcache Client+# memcache: Haskell Memcached Client -[](https://hackage.haskell.org/package/memcache) [](https://travis-ci.org/dterei/memcache-hs) +[](https://hackage.haskell.org/package/memcache)+[](http://packdeps.haskellers.com/reverse/memcache)+[][tl;dr Legal: BSD3]+[](https://travis-ci.org/dterei/memcache-hs)++[tl;dr Legal: BSD3]:+ https://tldrlegal.com/license/bsd-3-clause-license-(revised)+ "BSD3 License"+ A client library for a memcached cluster. It supports the binary memcached protocol and SASL authentication. No support@@ -18,28 +26,26 @@ ## Tools -This library also includes a few tools for manipulating and-experimenting with memcached servers.+This library also includes a few tools for manipulating and experimenting with+memcached servers. -* `OpGen` -- A load generator for memcached. Doesn't collect timing- statistics, other tools like- [mutilate](https://github.com/leverich/mutilate) already do that- very well. This tool is useful in conjunction with mutilate.-* `Loader` -- A tool to load random data of a certain size into a- memcache server. Useful for priming a server for testing.+* `OpGen` -- A load generator for memcached. Doesn't collect timing statistics,+ other tools like [mutilate](https://github.com/leverich/mutilate) already do+ that very well. This tool is useful in conjunction with mutilate.+* `Loader` -- A tool to load random data of a certain size into a memcached+ server. Useful for priming a server for testing. ## Architecture Notes -We're relying on `Data.Pool` for thread safety right now, which is-fine but is a blocking API in that when we grab a socket-(`withResource`) we are blocking any other requests being sent over-that connection until we get a response. That is, we can't pipeline.+We're relying on `Data.Pool` for thread safety right now, which is fine but is+a blocking API in that when we grab a socket (`withResource`) we are blocking+any other requests being sent over that connection until we get a response.+That is, we can't pipeline. -Now, use of multiple connections through the pool abstraction is an-easy way to solve this and perhaps the right approach. But, could also-implement own pool abstraction that allowed pipelining. This wouldn't-be a pool abstraction so much as just round-robbining over multiple-connections for performance.+Now, use of multiple connections through the pool abstraction is an easy way to+solve this and perhaps the right approach. But, could also implement own pool+abstraction that allowed pipelining. This wouldn't be a pool abstraction so+much as just round-robbining over multiple connections for performance. Either way, a pool is fine for now. @@ -51,8 +57,8 @@ ## Get involved! -We are happy to receive bug reports, fixes, documentation enhancements,-and other improvements.+We are happy to receive bug reports, fixes, documentation enhancements, and+other improvements. Please report bugs via the [github issue tracker](http://github.com/dterei/memcache-hs/issues).@@ -63,6 +69,10 @@ ## Authors -This library is written and maintained by David Terei,-<code@davidterei.com>.+This library is written and maintained by David Terei (<code@davidterei.com>).++Contributions have been made by the following great people:++* Alfredo Di Napoli (<alfredo.dinapoli@gmail.com>)+* Amit Levy
memcache.cabal view
@@ -1,5 +1,5 @@ name: memcache-version: 0.1.0.0+version: 0.1.0.1 homepage: https://github.com/dterei/memcache-hs bug-reports: https://github.com/dterei/memcache-hs/issues synopsis: A memcached client library.@@ -15,6 +15,7 @@ and other pipelined operations. . Basic usage is:+ . > import qualified Database.Memcache.Client as M > > mc <- M.newClient [M.ServerSpec "localhost" 11211 M.NoAuth] M.defaultOptions@@ -27,7 +28,7 @@ license-file: LICENSE author: David Terei <code@davidterei.com> maintainer: David Terei <code@davidterei.com>-copyright: 2015 David Terei.+copyright: 2016 David Terei. category: Database build-type: Simple cabal-version: >= 1.10@@ -37,7 +38,7 @@ Source-repository this type: git location: https://github.com/dterei/memcache-hs.git- tag: 0.1.0.0+ tag: 0.1.0.1 source-repository head type: git@@ -66,11 +67,13 @@ time >= 1.4 default-language: Haskell2010 other-extensions:- RecordWildCards,- ScopedTypeVariables,+ BangPatterns,+ CPP, DeriveDataTypeable, FlexibleInstances,- OverloadedStrings+ OverloadedStrings,+ RecordWildCards,+ ScopedTypeVariables ghc-options: -Wall -fwarn-tabs -- executable opgen