packages feed

montage-0.1: Network/Riak/Montage/Backend.hs

module Network.Riak.Montage.Backend where

import Prelude hiding (catch)
import Control.Monad (void, when)
import Control.Exception (catch, throw, SomeException(..))
import Control.Concurrent (threadDelay)
import Data.ByteString.Lazy as L
import Network.Riak.Types
import Network.Riak.Value.Resolvable (getWithLength, put, delete)

import Network.StatsWeb (Stats, incCounter)

import Network.Riak.Montage.Types
import Data.Conduit.Pool (withResource)

maxRetries :: Int
maxRetries = 3

-- We can't actually put in the type signatures below, see
-- http://www.haskell.org/haskellwiki/Type_families#Injectivity.2C_type_inference.2C_and_ambiguity
retryOperation :: IO a -> IO a
retryOperation op =
    retryOperation' 0
  where
    --retryOperation' :: IO a -> Int -> IO a
    retryOperation' retries = catch op (handleError retries)

    --handleError :: (Exception e) => Int -> e -> IO a
    handleError retries e = case retries > maxRetries of
        True -> throw (e :: SomeException)
        False -> do
            threadDelay $ 100000 * retries
            retryOperation' $ retries + 1

doGet :: (MontageRiakValue r) => Stats -> Bucket -> Key -> PoolChooser -> IO (RiakResponse r)
doGet stats buck key chooser' = do
  res <- retryOperation $ withResource (chooser' buck) $ \c -> getWithLength c buck key Default
  case res of
    Just ((resolved, siblings), v) -> do
        let resolvedLength = L.length $ riakSerialize resolved
        when (siblings > 10) $ incCounter "requests.many.siblings" stats
        when (resolvedLength > 1097152) $ incCounter "requests.big" stats
        return $ Just (resolved, v, Just siblings)
    Nothing -> return Nothing

doPut :: (MontageRiakValue r) => Bucket -> Key -> VectorClock -> RiakRecord r -> PoolChooser -> IO (RiakResponse r)
doPut buck key mvc rec chooser' = do
  let riakvc = fmap VClock mvc
  res <- retryOperation $ withResource (chooser' buck) $ \c -> (fmap Just $ put c buck key riakvc rec (Just 1) Default Default)
  return $ fmap (\(r, v) -> (r, v, Nothing)) res

doDelete :: (MontageRiakValue a) => Bucket -> Key -> PoolChooser -> IO (RiakResponse a)
doDelete buck key chooser' = do
  void $ retryOperation $ withResource (chooser' buck) $ (\c -> delete c buck key Default >> return Nothing)
  return Nothing