haskakafka 1.1.0 → 1.2.0
raw patch · 9 files changed
+97/−54 lines, 9 files
Files
- haskakafka.cabal +5/−5
- src/Haskakafka.hs +29/−10
- src/Haskakafka/Consumer/Internal/Convert.hs +1/−20
- src/Haskakafka/ConsumerExample.hs +1/−1
- src/Haskakafka/InternalRdKafka.chs +24/−5
- src/Haskakafka/InternalRdKafkaEnum.chs +4/−1
- src/Haskakafka/InternalShared.hs +19/−0
- src/Haskakafka/InternalTypes.hs +1/−0
- tests/TestMain.hs +13/−12
haskakafka.cabal view
@@ -1,5 +1,5 @@ name: haskakafka-version: 1.1.0+version: 1.2.0 synopsis: Kafka bindings for Haskell description: Apache Kafka bindings backed by the librdkafka C library. This implies full consumer and producer@@ -26,8 +26,8 @@ , unix exposed-modules: Haskakafka- Haskakafka.InternalRdKafka Haskakafka.InternalRdKafkaEnum+ Haskakafka.InternalRdKafka Haskakafka.InternalSetup Haskakafka.InternalTypes Haskakafka.ConsumerExample@@ -39,9 +39,9 @@ hs-source-dirs: src default-language: Haskell2010 ghc-options: -Wall- include-dirs: /usr/local/include/librdkafka- extra-lib-dirs: /usr/local/lib- extra-libraries: rdkafka+ pkgconfig-depends: rdkafka+ if os(darwin)+ cpp-options: -D__attribute__(A)= -D_Nullable= -D_Nonnull= executable simple main-is: Simple.hs
src/Haskakafka.hs view
@@ -8,8 +8,13 @@ , produceKeyedMessage , produceMessageBatch , storeOffset+, seekToOffset , getAllMetadata , getTopicMetadata+, handleProduceErr+, producePartitionInteger+, pollEvents+, pollEventsSafe -- Internal objects , IS.newKafka@@ -22,8 +27,8 @@ , rdKafkaVersionStr -- Type re-exports-, IT.Kafka-, IT.KafkaTopic+, IT.Kafka(..)+, IT.KafkaTopic(..) , IT.KafkaOffset(..) , IT.KafkaMessage(..)@@ -38,7 +43,7 @@ , IT.KafkaLogLevel(..) , IT.KafkaError(..)-, RDE.RdKafkaRespErrT+, RDE.RdKafkaRespErrT(..) -- Pseudo-internal , addBrokers@@ -82,13 +87,7 @@ -- 'consumeMessage' won't work without it. This function is non-blocking. startConsuming :: KafkaTopic -> Int -> KafkaOffset -> IO () startConsuming (KafkaTopic topicPtr _ _) partition offset =- let trueOffset = case offset of- KafkaOffsetBeginning -> (- 2)- KafkaOffsetEnd -> (- 1)- KafkaOffsetStored -> (- 1000)- KafkaOffsetInvalid -> (- 1001)- KafkaOffset i -> i- in throwOnError $ rdKafkaConsumeStart topicPtr partition trueOffset+ throwOnError $ rdKafkaConsumeStart topicPtr partition $ offsetToInt64 offset -- | Stops consuming for a given topic. You probably do not need to call -- this directly (it is called automatically when 'withKafkaConsumer' terminates).@@ -134,6 +133,18 @@ [] -> return $ Right $ rights ms l -> return $ Left $ head l +seekToOffset :: KafkaTopic+ -> Int -- ^ partition number+ -> KafkaOffset -- ^ destination+ -> Int -- ^ timeout in milliseconds+ -> IO (Maybe KafkaError)+seekToOffset (KafkaTopic ptr _ _) p ofs timeout = do+ err <- rdKafkaSeek ptr (fromIntegral p)+ (fromIntegral $ offsetToInt64 ofs) timeout+ case err of+ RdKafkaRespErrNoError -> return Nothing+ e -> return $ Just $ KafkaResponseError e+ -- | Store a partition's offset in librdkafka's offset store. This function only needs to be called -- if auto.commit.enable is false. See <https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md> -- for information on how to configure the offset store.@@ -216,6 +227,7 @@ , offset'RdKafkaMessageT = 0 , keyLen'RdKafkaMessageT = 0 , key'RdKafkaMessageT = nullPtr+ , private'RdKafkaMessageT = nullPtr } produceMessageToMessage (KafkaProduceKeyedMessage kbs bs) = do let (payloadFPtr, payloadOffset, payloadLength) = BSI.toForeignPtr bs@@ -236,6 +248,7 @@ , offset'RdKafkaMessageT = 0 , keyLen'RdKafkaMessageT = keyLength , key'RdKafkaMessageT = passedKey+ , private'RdKafkaMessageT = nullPtr } -- | Connects to Kafka broker in producer mode for a given topic, taking a function@@ -394,6 +407,12 @@ pollEvents :: Kafka -> Int -> IO () pollEvents (Kafka kPtr _) timeout = rdKafkaPoll kPtr timeout >> return ()++pollEventsSafe :: Kafka -> Int -> IO ()+pollEventsSafe (Kafka kPtr _) timeout = do+ _ <- withForeignPtr kPtr $ \realPtr -> do+ rdKafkaPollSafe realPtr timeout+ return () outboundQueueLength :: Kafka -> IO (Int) outboundQueueLength (Kafka kPtr _) = rdKafkaOutqLen kPtr
src/Haskakafka/Consumer/Internal/Convert.hs view
@@ -3,12 +3,11 @@ where import Control.Monad-import Data.Int import Foreign import Foreign.C.String import Haskakafka.Consumer.Internal.Types import Haskakafka.InternalRdKafka-import Haskakafka.InternalTypes+import Haskakafka.InternalShared -- | Converts offsets sync policy to integer (the way Kafka understands it): --@@ -24,24 +23,6 @@ OffsetSyncImmediate -> 0 OffsetSyncInterval ms -> ms {-# INLINE offsetSyncToInt #-}--offsetToInt64 :: KafkaOffset -> Int64-offsetToInt64 o = case o of- KafkaOffsetBeginning -> -2- KafkaOffsetEnd -> -1- KafkaOffset off -> off- KafkaOffsetStored -> -1000- KafkaOffsetInvalid -> -1001-{-# INLINE offsetToInt64 #-}--int64ToOffset :: Int64 -> KafkaOffset-int64ToOffset o- | o == -2 = KafkaOffsetBeginning- | o == -1 = KafkaOffsetEnd- | o == -1000 = KafkaOffsetStored- | o >= 0 = KafkaOffset o- | otherwise = KafkaOffsetInvalid-{-# INLINE int64ToOffset #-} fromNativeTopicPartitionList :: RdKafkaTopicPartitionListT -> IO [KafkaTopicPartition] fromNativeTopicPartitionList pl =
src/Haskakafka/ConsumerExample.hs view
@@ -5,7 +5,7 @@ import Control.Arrow ((&&&)) import Haskakafka import Haskakafka.Consumer-import Haskakafka.InternalRdKafkaEnum+import Haskakafka.InternalRdKafkaEnum () iterator :: [Integer] iterator = [0 .. 20]
src/Haskakafka/InternalRdKafka.chs view
@@ -15,8 +15,10 @@ import System.Posix.IO import System.Posix.Types -#include "rdkafka.h"+#include "librdkafka/rdkafka.h" +{#import Haskakafka.InternalRdKafkaEnum#}+ type CInt64T = {#type int64_t #} type CInt32T = {#type int32_t #} @@ -129,6 +131,7 @@ , offset'RdKafkaMessageT :: Int64 , payload'RdKafkaMessageT :: Word8Ptr , key'RdKafkaMessageT :: Word8Ptr+ , private'RdKafkaMessageT :: Ptr () } deriving (Show, Eq) @@ -144,6 +147,7 @@ <*> liftM fromIntegral ({#get rd_kafka_message_t->offset #} p) <*> liftM castPtr ({#get rd_kafka_message_t->payload #} p) <*> liftM castPtr ({#get rd_kafka_message_t->key #} p)+ <*> liftM castPtr ({#get rd_kafka_message_t->_private #} p) poke p x = do {#set rd_kafka_message_t.err#} p (enumToCInt $ err'RdKafkaMessageT x) {#set rd_kafka_message_t.rkt#} p (castPtr $ topic'RdKafkaMessageT x)@@ -153,6 +157,7 @@ {#set rd_kafka_message_t.offset#} p (fromIntegral $ offset'RdKafkaMessageT x) {#set rd_kafka_message_t.payload#} p (castPtr $ payload'RdKafkaMessageT x) {#set rd_kafka_message_t.key#} p (castPtr $ key'RdKafkaMessageT x)+ {#set rd_kafka_message_t._private#} p (castPtr $ private'RdKafkaMessageT x) {#pointer *rd_kafka_message_t as RdKafkaMessageTPtr foreign -> RdKafkaMessageT #} @@ -167,7 +172,7 @@ instance Storable RdKafkaMetadataBrokerT where alignment _ = {#alignof rd_kafka_metadata_broker_t#}- sizeOf _ = 24+ sizeOf _ = {#sizeof rd_kafka_metadata_broker_t#} peek p = RdKafkaMetadataBrokerT <$> liftM fromIntegral ({#get rd_kafka_metadata_broker_t->id #} p) <*> liftM id ({#get rd_kafka_metadata_broker_t->host #} p)@@ -209,7 +214,7 @@ instance Storable RdKafkaMetadataTopicT where alignment _ = {#alignof rd_kafka_metadata_topic_t#}- sizeOf _ = 32+ sizeOf _ = {#sizeof rd_kafka_metadata_topic_t#} peek p = RdKafkaMetadataTopicT <$> liftM id ({#get rd_kafka_metadata_topic_t->topic #} p) <*> liftM fromIntegral ({#get rd_kafka_metadata_topic_t->partition_cnt #} p)@@ -514,9 +519,13 @@ {`RdKafkaTPtr', `RdKafkaMessageTPtr', `Int'} -> `RdKafkaRespErrT' cIntToEnum #} -{#fun unsafe rd_kafka_position as ^+{#fun unsafe rd_kafka_committed as ^ {`RdKafkaTPtr', `RdKafkaTopicPartitionListTPtr', `Int'} -> `RdKafkaRespErrT' cIntToEnum #}++{#fun unsafe rd_kafka_position as ^+ {`RdKafkaTPtr', `RdKafkaTopicPartitionListTPtr'}+ -> `RdKafkaRespErrT' cIntToEnum #} ------------------------------------------------------------------------------------------------- ---- Groups data RdKafkaGroupMemberInfoT = RdKafkaGroupMemberInfoT@@ -623,6 +632,9 @@ foreign import ccall unsafe "rdkafka.h rd_kafka_message_destroy" rdKafkaMessageDestroy :: Ptr RdKafkaMessageT -> IO () +{#fun unsafe rd_kafka_message_timestamp as ^+ {`RdKafkaMessageTPtr', `RdKafkaTimestampTypeTPtr'} -> `CInt64T' cIntConv #}+ -- rd_kafka_conf {#fun unsafe rd_kafka_conf_new as ^ {} -> `RdKafkaConfTPtr' #}@@ -720,6 +732,10 @@ {`RdKafkaTopicTPtr', cIntConv `CInt32T', `Int', castPtr `Ptr (Ptr RdKafkaMessageT)', cIntConv `CSize'} -> `CSize' cIntConv #} +{#fun rd_kafka_seek as ^+ {`RdKafkaTopicTPtr', cIntConv `CInt32T', cIntConv `CInt64T', `Int'}+ -> `RdKafkaRespErrT' cIntToEnum #}+ rdKafkaConsumeStop :: RdKafkaTopicTPtr -> Int -> IO (Maybe String) rdKafkaConsumeStop topicPtr partition = do i <- rdKafkaConsumeStopInternal topicPtr (fromIntegral partition)@@ -754,7 +770,10 @@ {# fun unsafe rd_kafka_metadata_destroy as ^ {castPtr `Ptr RdKafkaMetadataT'} -> `()' #} -{#fun unsafe rd_kafka_poll as ^+foreign import ccall safe "rd_kafka.h rd_kafka_poll"+ rdKafkaPollSafe :: Ptr RdKafkaT -> Int -> IO Int++{# fun unsafe rd_kafka_poll as ^ {`RdKafkaTPtr', `Int'} -> `Int' #} {#fun unsafe rd_kafka_outq_len as ^
src/Haskakafka/InternalRdKafkaEnum.chs view
@@ -3,8 +3,11 @@ module Haskakafka.InternalRdKafkaEnum where -#include "rdkafka.h"+#include "librdkafka/rdkafka.h" {#enum rd_kafka_type_t as ^ {underscoreToCase} deriving (Show, Eq) #} {#enum rd_kafka_conf_res_t as ^ {underscoreToCase} deriving (Show, Eq) #} {#enum rd_kafka_resp_err_t as ^ {underscoreToCase} deriving (Show, Eq) #}+{#enum rd_kafka_timestamp_type_t as ^ {underscoreToCase} deriving (Show, Eq) #}++{#pointer *rd_kafka_timestamp_type_t as RdKafkaTimestampTypeTPtr foreign -> RdKafkaTimestampTypeT #}
@@ -66,3 +66,22 @@ _ -> Left err {-# INLINE kafkaErrorToEither #-} +offsetToInt64 :: KafkaOffset -> Int64+offsetToInt64 o = case o of+ KafkaOffsetBeginning -> -2+ KafkaOffsetEnd -> -1+ KafkaOffset off -> off+ KafkaOffsetStored -> -1000+ KafkaOffsetInvalid -> -1001+ KafkaOffsetTail i -> -2000 - i+{-# INLINE offsetToInt64 #-}++int64ToOffset :: Int64 -> KafkaOffset+int64ToOffset o+ | o == -2 = KafkaOffsetBeginning+ | o == -1 = KafkaOffsetEnd+ | o == -1000 = KafkaOffsetStored+ | o >= 0 = KafkaOffset o+ | o <= -2000 = KafkaOffsetTail (-2000 - o)+ | otherwise = KafkaOffsetInvalid+{-# INLINE int64ToOffset #-}
src/Haskakafka/InternalTypes.hs view
@@ -49,6 +49,7 @@ -- <https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md librdkafka's documentation> -- for offset store configuration. | KafkaOffsetStored+ | KafkaOffsetTail Int64 | KafkaOffsetInvalid deriving (Eq, Show)
tests/TestMain.hs view
@@ -74,24 +74,25 @@ describe "Kafka topic configuration" $ do it "should allow dumping" $ do- kConf <- newKafkaTopicConf- kvs <- dumpKafkaTopicConf kConf+ kTopicConf <- newKafkaTopicConf+ kvs <- dumpKafkaTopicConf kTopicConf (Map.size kvs) `shouldSatisfy` (>0) it "should change when set is called" $ do- kConf <- newKafkaTopicConf- setKafkaTopicConfValue kConf "request.timeout.ms" "20000"- kvs <- dumpKafkaTopicConf kConf+ kTopicConf <- newKafkaTopicConf+ setKafkaTopicConfValue kTopicConf "request.timeout.ms" "20000"+ kvs <- dumpKafkaTopicConf kTopicConf (kvs Map.! "request.timeout.ms") `shouldBe` "20000" it "should throw an exception on unknown property" $ do- kConf <- newKafkaTopicConf- (setKafkaTopicConfValue kConf "blippity.blop.cosby" "120") `shouldThrow`+ kTopicConf <- newKafkaTopicConf+ (setKafkaTopicConfValue kTopicConf "blippity.blop.cosby" "120") `shouldThrow` (\(KafkaUnknownConfigurationKey str) -> (length str) > 0) it "should throw an exception on an invalid value" $ do- kConf <- newKafkaTopicConf- (setKafkaTopicConfValue kConf "request.timeout.ms" "mono...doh!") `shouldThrow`+ kTopicConf <- newKafkaTopicConf+ (setKafkaTopicConfValue kTopicConf "request.timeout.ms" "mono...doh!")+ `shouldThrow` (\(KafkaInvalidConfigurationValue str) -> (length str) > 0) describe "Logging" $ do@@ -150,7 +151,7 @@ produceMessageBatch producerTopic (KafkaSpecifiedPartition 0 ) sampleProduceMessages errs `shouldBe` [] - et <- consumeMessageBatch topic 0 (5000) 3+ et <- consumeMessageBatch topic 0 kafkaDelay 3 case et of (Left err) -> error $ show err (Right oms) -> do@@ -161,7 +162,7 @@ it "should not fail on batch consume when no messages are available #12" $ getAddressTopic $ \a t -> do withKafkaConsumer [] [] a t 0 KafkaOffsetEnd $ \_ topic -> do primeEOF topic- et <- consumeMessageBatch topic 0 (5000) 3+ et <- consumeMessageBatch topic 0 kafkaDelay 3 case et of (Left err) -> error $ show err (Right oms) -> do@@ -169,7 +170,7 @@ it "should return EOF on batch consume if necessary" $ getAddressTopic $ \a t -> do withKafkaConsumer [] [] a t 0 KafkaOffsetEnd $ \_ topic -> do- et <- consumeMessageBatch topic 0 (5000) 10+ et <- consumeMessageBatch topic 0 kafkaDelay 10 case et of (Left err) -> print err (Right _oms) -> error "should return EOF"