packages feed

zoovisitor 0.1.4.0 → 0.1.5.0

raw patch · 5 files changed

+122/−8 lines, 5 filesdep +asyncPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: async

API changes (from Hackage documentation)

- ZooKeeper.Recipe.Election: election :: ZHandle -> CBytes -> CBytes -> IO () -> (DataCompletion -> IO ()) -> IO ()
+ ZooKeeper.Recipe: election :: ZHandle -> CBytes -> CBytes -> IO () -> (DataCompletion -> IO ()) -> IO ()
+ ZooKeeper.Recipe: lock :: ZHandle -> CBytes -> CBytes -> IO CBytes
+ ZooKeeper.Recipe: unlock :: ZHandle -> CBytes -> IO ()

Files

+ src/ZooKeeper/Recipe.hs view
@@ -0,0 +1,7 @@+module ZooKeeper.Recipe+  ( module ZooKeeper.Recipe.Election+  , module ZooKeeper.Recipe.Lock+  ) where++import           ZooKeeper.Recipe.Election+import           ZooKeeper.Recipe.Lock
src/ZooKeeper/Recipe/Election.hs view
@@ -1,4 +1,6 @@-module ZooKeeper.Recipe.Election where+module ZooKeeper.Recipe.Election+  ( election+  ) where  import           Control.Monad import qualified Z.Data.Builder         as B
+ src/ZooKeeper/Recipe/Lock.hs view
@@ -0,0 +1,62 @@+module ZooKeeper.Recipe.Lock+  ( lock+  , unlock+  ) where++import           Control.Exception+import           Control.Monad+import           Z.Data.CBytes          (CBytes)++import           ZooKeeper+import           ZooKeeper.Exception+import           ZooKeeper.Recipe.Utils (SequenceNumWithGUID (..),+                                         createSeqEphemeralZNode,+                                         mkSequenceNumWithGUID)+import           ZooKeeper.Types++-- | To acquire a distributed lock.+lock :: ZHandle+     -- ^ The zookeeper handle obtained by a call to 'zookeeperResInit'+     -> CBytes+     -- ^ The path to get the lock. Ephemeral znodes will be put on it+     -> CBytes+     -- ^ The GUID for this zookeeper session. To handle recoverable execptions+     -- correctly, it should be distinct from different sessions+     -> IO CBytes+     -- ^ The real path of the lock that acquired. It will be used when unlocking+     -- the same lock+lock zk lockPath guid = do+  -- Check persistent paths+  electionExists <- zooExists zk lockPath+  case electionExists of+    Just _  -> return ()+    Nothing -> void (try $ zooCreate zk lockPath Nothing zooOpenAclUnsafe ZooPersistent :: IO (Either ZooException StringCompletion))++  -- Create Ephemeral and Sequece znode, and get the seq number i+  (StringCompletion this) <- createSeqEphemeralZNode zk lockPath guid+  let thisSeqNumWithGUID = mkSequenceNumWithGUID this++  callback zk thisSeqNumWithGUID+  where+    callback zk_ self = do+      (StringsCompletion (StringVector children)) <- zooGetChildren zk_ lockPath+      let childrenSeqNumWithGUID = mkSequenceNumWithGUID <$> children+      case filter (< self) childrenSeqNumWithGUID of+        [] -> do+          return $ unSequenceNumWithGUID self+        xs -> do+          let toWatch = lockPath <> "/" <> unSequenceNumWithGUID (maximum xs)+          zooWatchExists zk_ toWatch (\HsWatcherCtx{..} -> void $ callback watcherCtxZHandle self)+            (\_ -> return ())+          return $ unSequenceNumWithGUID self++-- | To release a distributed lock. Note that the real lock path+-- should be the one acquired by 'lock', otherwise, a 'ZooException'+-- will be thrown.+unlock :: ZHandle+       -- ^ The zookeeper handle obtained by a call to 'zookeeperResInit'+       -> CBytes+       -- ^ The real lock path acquired by 'lock'. An exception will be+       -- thrown if it is bad (for example, does not exist)+       -> IO ()+unlock zk thisLock = zooDelete zk thisLock Nothing
test/Spec.hs view
@@ -3,17 +3,18 @@ module Main where  import           Control.Concurrent-import           Control.Monad             (replicateM, void)-import qualified Data.UUID                 as UUID+import           Control.Concurrent.Async (concurrently_)+import           Control.Monad            (replicateM, void)+import qualified Data.UUID                as UUID import           Data.UUID.V4-import           Data.Version              (makeVersion)+import           Data.Version             (makeVersion) import           Foreign.C import           Test.Hspec-import qualified Z.Data.CBytes             as CB+import qualified Z.Data.CBytes            as CB  import           ZooKeeper import           ZooKeeper.Exception-import           ZooKeeper.Recipe.Election (election)+import           ZooKeeper.Recipe         (election, lock, unlock) import           ZooKeeper.Types  recvTimeout :: CInt@@ -29,6 +30,8 @@   hspec $ propSpec zh   hspec $ electionSpec1 zh   hspec $ electionSpec2 client+  hspec $ lockSpec1 zh+  hspec $ lockSpec2 client  opSpec :: ZHandle -> Spec opSpec zh = do@@ -174,6 +177,43 @@     killThread t1     takeMVar finished4     readMVar leader `shouldReturn` (2 :: Int)++lockSpec1 :: ZHandle -> Spec+lockSpec1 zh = describe "ZooKeeper.zooLock (single session)" $ do+  it "Test single client situation" $ do+    result <- newEmptyMVar+    uuid <- nextRandom+    thisLock <- lock zh "/lock1" (CB.pack . UUID.toString $ uuid)+    putMVar result 1+    unlock zh thisLock+    readMVar result `shouldReturn` (1 :: Int)++lockSpec2 :: Resource ZHandle -> Spec+lockSpec2 client_ = describe "ZooKeeper.zooLock (multi sessions)" $ do+  it "Test multi clients situation" $ do+    result1 <- newMVar 0+    result2 <- newMVar 0+    [uuid1, uuid2] <- replicateM 2 nextRandom++    concurrently_ (action1 result1) (action1 result1)+    readMVar result1 `shouldReturn` (1 :: Int)++    withResource client_ $ \zh1 -> do+      withResource client_ $ \zh2 -> do+        concurrently_ (action2 zh1 (CB.pack . UUID.toString $ uuid1) result2)+                      (action2 zh2 (CB.pack . UUID.toString $ uuid2) result2)+        readMVar result2 `shouldReturn` (2 :: Int)+  where+    action1 target = do+      x <- readMVar target+      threadDelay 100000+      swapMVar target (x + 1)+    action2 zh guid target = do+      thisLock <- lock zh "/lock2" guid+      x <- readMVar target+      threadDelay 100000+      _ <- swapMVar target (x + 1)+      unlock zh thisLock  ------------------------------------------------------------------------------- 
zoovisitor.cabal view
@@ -1,6 +1,6 @@ cabal-version:      2.2 name:               zoovisitor-version:            0.1.4.0+version:            0.1.5.0 synopsis:   A haskell binding to Apache Zookeeper C library(mt) using Haskell Z project. @@ -32,12 +32,14 @@   exposed-modules:     ZooKeeper     ZooKeeper.Exception-    ZooKeeper.Recipe.Election+    ZooKeeper.Recipe     ZooKeeper.Types    other-modules:     ZooKeeper.Internal.FFI     ZooKeeper.Internal.Types+    ZooKeeper.Recipe.Election+    ZooKeeper.Recipe.Lock     ZooKeeper.Recipe.Utils    build-depends:@@ -80,6 +82,7 @@   main-is:          Spec.hs   hs-source-dirs:   test   build-depends:+    , async     , base        >=4.12 && <5     , hspec     , uuid