writerAction: drain, dedupe, and rate-limit outbound calls
This commit is contained in:
@@ -9,10 +9,12 @@ module HomeAssistant.Runtime.Connection
|
|||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Concurrent.STM
|
import Control.Concurrent.STM
|
||||||
( atomically
|
( TChan
|
||||||
|
, atomically
|
||||||
, readTChan
|
, readTChan
|
||||||
, readTVar
|
, readTVar
|
||||||
, retry
|
, retry
|
||||||
|
, tryReadTChan
|
||||||
, writeTChan
|
, writeTChan
|
||||||
, writeTVar
|
, writeTVar
|
||||||
)
|
)
|
||||||
@@ -103,16 +105,41 @@ receiveJSON conn = do
|
|||||||
Left err -> throw (Fatal $ "Invalid JSON from Home Assistant: " <> T.pack err)
|
Left err -> throw (Fatal $ "Invalid JSON from Home Assistant: " <> T.pack err)
|
||||||
Right x -> pure x
|
Right x -> pure x
|
||||||
|
|
||||||
writerAction :: Bus -> IO Void
|
-- | Floor between sends within a batch: 100ms, so a many-distinct-target
|
||||||
writerAction bus = forever $ do
|
-- flood still caps at ~10 sends/sec even after dedupe.
|
||||||
(request, svc) <- atomically $ readTChan (busOutbound bus)
|
minInterval :: Int
|
||||||
conn <- atomically $ readTVar (busConn bus) >>= maybe retry pure
|
minInterval = 100000
|
||||||
|
|
||||||
|
-- | Non-blocking drain of everything queued on a channel. Returns items
|
||||||
|
-- oldest-first (FIFO from the channel), so prepending the blocking
|
||||||
|
-- `readTChan` item keeps the whole batch oldest-first for `dedupeBatch`.
|
||||||
|
drainTry :: TChan a -> IO [a]
|
||||||
|
drainTry chan = go []
|
||||||
|
where
|
||||||
|
go acc = do
|
||||||
|
m <- atomically $ tryReadTChan chan
|
||||||
|
case m of
|
||||||
|
Nothing -> pure (reverse acc)
|
||||||
|
Just x -> go (x : acc)
|
||||||
|
|
||||||
|
sendWithId :: Bus -> WS.Connection -> Request -> Service -> IO ()
|
||||||
|
sendWithId bus conn request svc = do
|
||||||
callId <- generateCallId (busGen bus)
|
callId <- generateCallId (busGen bus)
|
||||||
let textData = encode $ encodeService callId svc
|
let textData = encode $ encodeService callId svc
|
||||||
runKatipContextT (busLogEnv bus) (sl "traceId" (toText (requestTraceId request))) "connection" $
|
runKatipContextT (busLogEnv bus) (sl "traceId" (toText (requestTraceId request))) "connection" $
|
||||||
logFM DebugS (ls textData)
|
logFM DebugS (ls textData)
|
||||||
WS.sendTextData conn textData
|
WS.sendTextData conn textData
|
||||||
|
|
||||||
|
writerAction :: Bus -> IO Void
|
||||||
|
writerAction bus = forever $ do
|
||||||
|
first <- atomically $ readTChan (busOutbound bus)
|
||||||
|
rest <- drainTry (busOutbound bus)
|
||||||
|
let deduped = dedupeBatch (first : rest)
|
||||||
|
conn <- atomically $ readTVar (busConn bus) >>= maybe retry pure
|
||||||
|
forM_ deduped $ \(request, svc) -> do
|
||||||
|
sendWithId bus conn request svc
|
||||||
|
threadDelay minInterval
|
||||||
|
|
||||||
encodeService :: Int -> Service -> Value
|
encodeService :: Int -> Service -> Value
|
||||||
encodeService callId Service{..} = object $
|
encodeService callId Service{..} = object $
|
||||||
[ "id" .= callId
|
[ "id" .= callId
|
||||||
|
|||||||
Reference in New Issue
Block a user