vaultaire-common-2.9.1: lib/Vaultaire/Types/Telemetry.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Vaultaire.Types.Telemetry
( TeleResp(..)
, TeleMsg(..)
, TeleMsgType(..)
, TeleMsgUOM(..)
, msgTypeUOM
, AgentID, agentIDLength, agentID )
where
import Control.Applicative
import Control.Exception
import Control.Monad
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as S
import Data.Monoid
import Data.Packer
import Data.Word
import Test.QuickCheck
import Vaultaire.Classes.WireFormat
import Vaultaire.Types.Common
import Vaultaire.Types.TimeStamp
-- | ID string associated with a running daemon, so telemetry messages
-- can be associated with the processes which sent them.
newtype AgentID = AgentID String
deriving (Eq, Ord, Monoid)
-- | Response for a telemetry request, sent by the profiler to clients.
data TeleResp = TeleResp
{ _timestamp :: TimeStamp
, _aid :: AgentID
, _msg :: TeleMsg
} deriving Eq
-- | The actual telemetric data, reported by Vaultaire worker threads
-- to their profiler.
--
data TeleMsg = TeleMsg
{ _origin :: Origin
, _type :: TeleMsgType
, _payload :: Word64
} deriving Eq
-- | Telemetry types. All counts are absolute and all latencies are in milliseconds.
data TeleMsgType
= WriterSimplePoints -- ^ Total number of simple points written since last message
| WriterExtendedPoints -- ^ Total number of extended points written since last message
| WriterRequest -- ^ Total number of write requests received since last message
| WriterRequestLatency -- ^ Mean latency for one request
| WriterCephLatency -- ^ Mean Ceph latency for one request
| ReaderSimplePoints -- ^ Total number of simple points read since last message
| ReaderExtendedPoints -- ^ Total number of extended points read since last message
| ReaderRequest -- ^ Total number of read requests received since last message
| ReaderRequestLatency -- ^ Mean latency for one request
| ReaderCephLatency -- ^ Mean Ceph latency for one request
| ContentsEnumerate -- ^ Total number of enumerate requests received since last message
| ContentsUpdate -- ^ Total number of update requests received since last message
| ContentsEnumerateLatency -- ^ Mean latency for one enumerate request
| ContentsUpdateLatency -- ^ Mean latency for one update request
| ContentsEnumerateCeph -- ^ Mean Ceph latency for one enumerate request
| ContentsUpdateCeph -- ^ Mean Ceph latency for one update request
deriving (Enum, Bounded, Eq, Ord)
data TeleMsgUOM
= Points
| Requests
| Milliseconds
deriving (Enum, Bounded, Eq, Ord)
instance Show TeleMsgUOM where
show Points = "points"
show Requests = "requests"
show Milliseconds = "ms"
-- | Map a telemetry message type onto its associated UOM.
msgTypeUOM :: TeleMsgType -> TeleMsgUOM
msgTypeUOM WriterSimplePoints = Points
msgTypeUOM WriterExtendedPoints = Points
msgTypeUOM WriterRequest = Requests
msgTypeUOM WriterRequestLatency = Milliseconds
msgTypeUOM WriterCephLatency = Milliseconds
msgTypeUOM ReaderSimplePoints = Points
msgTypeUOM ReaderExtendedPoints = Points
msgTypeUOM ReaderRequest = Requests
msgTypeUOM ReaderRequestLatency = Milliseconds
msgTypeUOM ReaderCephLatency = Milliseconds
msgTypeUOM ContentsEnumerate = Requests
msgTypeUOM ContentsUpdate = Requests
msgTypeUOM ContentsEnumerateLatency = Milliseconds
msgTypeUOM ContentsUpdateLatency = Milliseconds
msgTypeUOM ContentsEnumerateCeph = Milliseconds
msgTypeUOM ContentsUpdateCeph = Milliseconds
-- | Return (possibly empty) prefix component of a ByteString terminated
-- by one or more null bytes.
chomp :: ByteString -> ByteString
chomp = S.takeWhile (/='\0')
-- | Agent IDs are a maximum of 64 bytes.
agentIDLength :: Int
agentIDLength = 64
-- | An agent ID has to fit in 64 characters and does not contain \NUL.
agentID :: String -> Maybe AgentID
agentID s | length s <= agentIDLength && notElem '\0' s
= Just $ AgentID s
| otherwise = Nothing
putAgentID :: AgentID -> Packing ()
putAgentID (AgentID x)
= putBytes $ S.pack $ x ++ replicate (agentIDLength - length x) '\0'
getAgentID :: Unpacking AgentID
getAgentID = AgentID . S.unpack . chomp <$> getBytes agentIDLength
-- | Pack a telemetry message. Assumes the origin is no longer than
-- eight bytes.
putTeleMsg :: TeleMsg -> Packing ()
putTeleMsg x = do
-- 8 bytes for the origin.
let o = unOrigin $ _origin x
putBytes $ S.append o $ S.pack $ replicate (8 - S.length o) '\0'
-- 8 bytes for the message type.
putWord64LE $ fromIntegral $ fromEnum $ _type x
-- 8 bytes for the payload
putWord64LE $ _payload x
getTeleMsg :: Unpacking (Either SomeException TeleMsg)
getTeleMsg = do
o <- makeOrigin . chomp <$> getBytes 8
t <- toEnum . fromIntegral <$> getWord64LE
p <- getWord64LE
return $ fmap (\org -> TeleMsg org t p) o
instance WireFormat AgentID where
toWire = runPacking agentIDLength . putAgentID
fromWire = tryUnpacking getAgentID
instance WireFormat TeleMsg where
toWire = runPacking 24 . putTeleMsg
fromWire = runUnpacking getTeleMsg
instance WireFormat TeleResp where
toWire x = runPacking 96 $ do
-- 8 bytes for the timestamp
putWord64LE $ unTimeStamp $ _timestamp x
-- 64 bytes for the agent ID, padded out with nuls
putAgentID $ _aid x
-- 24 bytes for the message (origin, type and payload)
putTeleMsg $ _msg x
fromWire x = join $ flip tryUnpacking x $ do
s <- TimeStamp <$> getWord64LE
a <- getAgentID
m <- getTeleMsg
return $ TeleResp s a <$> m
instance Arbitrary TeleMsg where
arbitrary = TeleMsg
<$> arbitrary
<*> arbitrary
<*> arbitrary
instance Arbitrary TeleResp where
arbitrary = TeleResp
<$> arbitrary
<*> arbitrary
<*> arbitrary
instance Arbitrary TeleMsgType where
arbitrary = arbitraryBoundedEnum
instance Arbitrary AgentID where
arbitrary = untilG agentID arbitrary
where untilG :: (Arbitrary a, Arbitrary b) => (a -> Maybe b) -> Gen a -> Gen b
untilG f a = a >>= maybe arbitrary return . f
instance Show AgentID where
show (AgentID s) = s
instance Show TeleMsgType where
show WriterSimplePoints = "writer-count-simple-point "
show WriterExtendedPoints = "writer-count-extended-point "
show WriterRequest = "writer-count-request "
show WriterRequestLatency = "writer-latency-request "
show WriterCephLatency = "writer-latency-ceph "
show ReaderSimplePoints = "reader-count-simple-point "
show ReaderExtendedPoints = "reader-count-extended-point "
show ReaderRequest = "reader-count-request "
show ReaderRequestLatency = "reader-latency-request "
show ReaderCephLatency = "reader-latency-ceph "
show ContentsEnumerate = "contents-count-enumerate "
show ContentsUpdate = "contents-count-update "
show ContentsEnumerateLatency = "contents-latency-enumerate "
show ContentsUpdateLatency = "contents-latency-update "
show ContentsEnumerateCeph = "contents-latency-ceph-enumerate"
show ContentsUpdateCeph = "contents-latency-ceph-update "
instance Show TeleResp where
show r = concat [ show $ _timestamp r, " "
, show $ _aid r, " "
, show $ _msg r]
instance Show TeleMsg where
show m = concat [ show $ _origin m, " "
, show $ _type m, " "
, let s = show (fromIntegral $ _payload m :: Int)
in replicate (8 - length s) ' ' ++ s
, " "
, show $ msgTypeUOM $ _type m ]