packages feed

montage-0.1: examples/basic_proxy_pools.hs

{-# LANGUAGE OverloadedStrings #-}
import Data.Conduit.Pool (Pool, createPool)
import Network.Riak (defaultClient, connect, disconnect,
                    Client(port), Connection)

import qualified Data.ByteString.Lazy as BW
import qualified Data.ByteString.Lazy.Char8 as B
import Data.Aeson (Value, (.=), object, encode)
import Data.Word (Word8)

import Text.ProtocolBuffers.WireMessage (messageGet, messagePut, Wire)
import Text.ProtocolBuffers.Reflections (ReflectDescriptor)
import Text.ProtocolBuffers.Basic (Utf8, toUtf8, uFromString)

import Network.Riak.Montage
import Network.Riak.Montage.Util

import User.UserInfo as UI
import User.UserEvent as UE
import User.UserName as UN

-- Pool setup

generatePool :: String -> Int -> IO (Pool Connection)
generatePool port count =
    createPool
        (connect $ defaultClient {port = port})
        disconnect
        1  -- stripes
        10 -- timeout
        count -- max connections

-- Resolution logic
{-
  Here your data can be one of two simple protocol buffers:
    message UserInfo {
      required uint32 uid = 1;
    }

    message UserEvent {
      required uint32 eid = 1;
    }
  .
  You always wrap it in a resolution data type so you can create a
  unique BucketSpec by bucket name.

  A simple last-write-wins resolution uses something like the
  lastWriteWins resolver (below). Don't forget to define a
    1. constructor (ByteString -> a) function
    2. deconstructor (a -> ByteString) function
  for each bucket
-}

data ResObject = ResObjectUserInfo UserInfo
               | ResObjectUserEvent UserEvent
               | ResObjectUserName UserName
  deriving (Show)

data Pools = Pools {
      poolA :: Pool Connection
    , poolB :: Pool Connection
  }

-- multiple pools
instance Poolable Pools where
  chooser ps "u-info" = poolA ps
  chooser ps "u-event" = poolA ps
  chooser ps "u-name" = poolB ps

instance MontageRiakValue ResObject where
  getPB "u-info" = BucketSpec
                   (ResObjectUserInfo . messageGetError "UserInfo")
                   lastWriteWins
                   (\(ResObjectUserInfo o) -> messagePut o)
  getPB "u-event" = BucketSpec
                   (ResObjectUserEvent . messageGetError "UserEvent")
                   lastWriteWins
                   (\(ResObjectUserEvent o) -> messagePut o)
  getPB "u-name" = BucketSpec
                   (ResObjectUserName . messageGetError "UserName")
                   lastWriteWins
                   (\(ResObjectUserName o) -> messagePut o)
  getPB bucket = error $ B.unpack $ B.concat [ "No resolution function defined for bucket: ", bucket ]

  referenceKey (ResObjectUserInfo pb) = Just $ (putDecimal . fromIntegral . UI.uid) $ pb
  referenceKey (ResObjectUserEvent pb) = Just $ (putDecimal . fromIntegral . UE.eid) $ pb
  referenceKey (ResObjectUserName pb) = Nothing -- no reference key for this string value
  referenceKey _ = Nothing

putDecimal' :: Int -> [Word8]
putDecimal' 0 = []
putDecimal' i = ((fromIntegral f) + 48) : putDecimal' (fromIntegral r)
  where
    (r, f) = i `divMod` 10
{-# INLINE putDecimal' #-}

putDecimal :: Int -> B.ByteString
putDecimal i | i < 0     = BW.pack $ (45 :: Word8) : (reverse $ putDecimal' $ abs i)
             | otherwise = BW.pack $ reverse $ putDecimal' i
{-# INLINE putDecimal #-}

lastWriteWins a b = b

messageGetError :: (ReflectDescriptor a, Wire a) => B.ByteString -> B.ByteString -> a
messageGetError cls v =
    case messageGet v of
        Right (msg, x) | B.length x == 0 ->
            msg
        Right (_, x) | B.length x /= 0 ->
            error ("error decoding " ++ B.unpack cls ++ ": did not consume all of input")
        Left e ->
            error ("error decoding " ++ B.unpack cls ++ ": " ++ e)

-- Run it

main :: IO ()
main = do
  a <- generatePool "8087" 300
  b <- generatePool "8187" 300
  let pools = Pools { poolA = a, poolB = b }

  runDaemon (cfg :: Config ResObject) pools