fix(jobs): adjust job handling to hopefully reduce load
This commit is contained in:
parent
271696c617
commit
ed38f93537
@ -144,14 +144,14 @@ yesodMiddleware = cacheControlMiddleware . storeBearerMiddleware . csrfMiddlewar
|
|||||||
approotHost <- hoistMaybe $ approotScopeHost rApproot app
|
approotHost <- hoistMaybe $ approotScopeHost rApproot app
|
||||||
let doRedirect = do
|
let doRedirect = do
|
||||||
url <- approotRender rApproot route
|
url <- approotRender rApproot route
|
||||||
$logErrorS "normalizeApprootMiddleware" url
|
$logDebugS "normalizeApprootMiddleware" url
|
||||||
redirect url
|
redirect url
|
||||||
if | approotHost /= reqHost
|
if | approotHost /= reqHost
|
||||||
, rApproot /= ApprootUserGenerated
|
, rApproot /= ApprootUserGenerated
|
||||||
-> doRedirect
|
-> doRedirect
|
||||||
| approotHost /= reqHost -> do
|
| approotHost /= reqHost -> do
|
||||||
resp <- try $ lift handler
|
resp <- try $ lift handler
|
||||||
$logErrorS "normalizeApprootMiddleware" $ tshow (is _Right resp, preview _Left resp)
|
$logDebugS "normalizeApprootMiddleware" $ tshow (is _Right resp, preview _Left resp)
|
||||||
case resp of
|
case resp of
|
||||||
Right _ -> doRedirect
|
Right _ -> doRedirect
|
||||||
Left sc | is _HCRedirect sc -> throwM sc
|
Left sc | is _HCRedirect sc -> throwM sc
|
||||||
|
|||||||
@ -6,7 +6,10 @@ import Import
|
|||||||
import Jobs
|
import Jobs
|
||||||
import Handler.Utils.DateTime
|
import Handler.Utils.DateTime
|
||||||
|
|
||||||
import Data.Aeson.Encode.Pretty (encodePrettyToTextBuilder)
|
import qualified Data.Aeson.Encode.Pretty as Pretty
|
||||||
|
import Data.Aeson.Encode.Pretty (encodePrettyToTextBuilder')
|
||||||
|
|
||||||
|
import qualified Data.Text as Text
|
||||||
|
|
||||||
|
|
||||||
getAdminCrontabR :: Handler Html
|
getAdminCrontabR :: Handler Html
|
||||||
@ -16,7 +19,7 @@ getAdminCrontabR = do
|
|||||||
JobState{jobCurrentCrontab} <- MaybeT $ tryReadTMVar jState
|
JobState{jobCurrentCrontab} <- MaybeT $ tryReadTMVar jState
|
||||||
MaybeT $ readTVar jobCurrentCrontab
|
MaybeT $ readTVar jobCurrentCrontab
|
||||||
|
|
||||||
let mCrontab = mCrontab' <&> _2 %~ filter (hasn't $ _1 . _MatchNone)
|
let mCrontab = mCrontab' <&> _2 %~ filter (hasn't $ _3 . _MatchNone)
|
||||||
|
|
||||||
siteLayoutMsg MsgMenuAdminCrontab $ do
|
siteLayoutMsg MsgMenuAdminCrontab $ do
|
||||||
setTitleI MsgMenuAdminCrontab
|
setTitleI MsgMenuAdminCrontab
|
||||||
@ -26,7 +29,7 @@ getAdminCrontabR = do
|
|||||||
<p>
|
<p>
|
||||||
^{formatTimeW SelFormatDateTime genTime}
|
^{formatTimeW SelFormatDateTime genTime}
|
||||||
<table .table .table--striped .table--hover>
|
<table .table .table--striped .table--hover>
|
||||||
$forall (match, job) <- crontab
|
$forall (job, lExec, match) <- crontab
|
||||||
<tr .table__row>
|
<tr .table__row>
|
||||||
<td .table__td>
|
<td .table__td>
|
||||||
$case match
|
$case match
|
||||||
@ -36,9 +39,18 @@ getAdminCrontabR = do
|
|||||||
_{MsgCronMatchNone}
|
_{MsgCronMatchNone}
|
||||||
$of MatchAt t
|
$of MatchAt t
|
||||||
^{formatTimeW SelFormatDateTime t}
|
^{formatTimeW SelFormatDateTime t}
|
||||||
|
<td .table__td>
|
||||||
|
$maybe lT <- lExec
|
||||||
|
^{formatTimeW SelFormatDateTime lT}
|
||||||
<td .table__td>
|
<td .table__td>
|
||||||
<pre>
|
<pre>
|
||||||
#{encodePrettyToTextBuilder job}
|
#{doEnc job}
|
||||||
$nothing
|
$nothing
|
||||||
_{MsgAdminCrontabNotGenerated}
|
_{MsgAdminCrontabNotGenerated}
|
||||||
|]
|
|]
|
||||||
|
where doEnc = encodePrettyToTextBuilder' Pretty.defConfig
|
||||||
|
{ Pretty.confIndent = Pretty.Spaces 2
|
||||||
|
, Pretty.confCompare = comparing $ \t -> ( t `elem` ["instruction", "job", "notification"]
|
||||||
|
, Text.splitOn "-" t
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@ -123,7 +123,7 @@ ensureApprootUserGeneratedMaybe'
|
|||||||
-> m ()
|
-> m ()
|
||||||
ensureApprootUserGeneratedMaybe' source = maybeT (return ()) $ do
|
ensureApprootUserGeneratedMaybe' source = maybeT (return ()) $ do
|
||||||
route <- (,) <$> MaybeT getCurrentRoute <*> fmap reqGetParams getRequest
|
route <- (,) <$> MaybeT getCurrentRoute <*> fmap reqGetParams getRequest
|
||||||
$logErrorS "ensureApproot" $ tshow route
|
$logDebugS "ensureApproot" $ tshow route
|
||||||
rApproot <- hoistMaybe <=< lift . runMaybeT $ do
|
rApproot <- hoistMaybe <=< lift . runMaybeT $ do
|
||||||
reqHost <- MaybeT $ W.requestHeaderHost <$> waiRequest
|
reqHost <- MaybeT $ W.requestHeaderHost <$> waiRequest
|
||||||
let rApproot = authoritiveApproot $ urlRoute route
|
let rApproot = authoritiveApproot $ urlRoute route
|
||||||
@ -131,10 +131,10 @@ ensureApprootUserGeneratedMaybe' source = maybeT (return ()) $ do
|
|||||||
approotHost <- MaybeT . getsYesod $ approotScopeHost rApproot
|
approotHost <- MaybeT . getsYesod $ approotScopeHost rApproot
|
||||||
guard $ approotHost /= reqHost
|
guard $ approotHost /= reqHost
|
||||||
return rApproot
|
return rApproot
|
||||||
$logErrorS "ensureApproot" $ tshow rApproot
|
$logDebugS "ensureApproot" $ tshow rApproot
|
||||||
route' <- lift $ withFileDownloadTokenMaybe' source route
|
route' <- lift $ withFileDownloadTokenMaybe' source route
|
||||||
url <- approotRender rApproot route'
|
url <- approotRender rApproot route'
|
||||||
$logErrorS "ensureApprootUserGenerated" url
|
$logDebugS "ensureApprootUserGenerated" url
|
||||||
redirect url
|
redirect url
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
18
src/Jobs.hs
18
src/Jobs.hs
@ -333,7 +333,8 @@ execCrontab = do
|
|||||||
do
|
do
|
||||||
lastTimes <- State.get
|
lastTimes <- State.get
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
let currentCrontab' = sortOn fst . flip map (HashMap.toList currentCrontab) $ \(job, cron) -> (,job) $ nextCronMatch appTZ (getMax <$> HashMap.lookup job lastTimes) (debouncingAcc settings job) now cron
|
let currentCrontab' = sortOn cmpProj . flip map (HashMap.toList currentCrontab) $ \(job, cron) -> (job, getMax <$> HashMap.lookup job lastTimes, ) $ nextCronMatch appTZ (getMax <$> HashMap.lookup job lastTimes) (debouncingAcc settings job) now cron
|
||||||
|
where cmpProj (j, lT, qT) = (qT, lT, j)
|
||||||
crontabTVar <- asks jobCurrentCrontab
|
crontabTVar <- asks jobCurrentCrontab
|
||||||
atomically . writeTVar crontabTVar $ Just (now, currentCrontab')
|
atomically . writeTVar crontabTVar $ Just (now, currentCrontab')
|
||||||
$logDebugS "Crontab" . intercalate "\n" $ "Current crontab:" : map tshow currentCrontab'
|
$logDebugS "Crontab" . intercalate "\n" $ "Current crontab:" : map tshow currentCrontab'
|
||||||
@ -342,7 +343,7 @@ execCrontab = do
|
|||||||
newCrontab <- lift $ hoist lift determineCrontab'
|
newCrontab <- lift $ hoist lift determineCrontab'
|
||||||
when (newCrontab /= currentCrontab) $
|
when (newCrontab /= currentCrontab) $
|
||||||
mapRWST (liftIO . atomically) $
|
mapRWST (liftIO . atomically) $
|
||||||
liftBase . void . flip swapTVar newCrontab =<< asks (jobCrontab . jobContext)
|
liftBase . flip writeTVar newCrontab =<< asks (jobCrontab . jobContext)
|
||||||
|
|
||||||
mergeState
|
mergeState
|
||||||
newState <- State.get
|
newState <- State.get
|
||||||
@ -390,14 +391,13 @@ execCrontab = do
|
|||||||
earliestJob :: AppSettings -> HashMap JobCtl (Max UTCTime) -> Crontab JobCtl -> UTCTime -> Maybe (JobCtl, CronNextMatch UTCTime)
|
earliestJob :: AppSettings -> HashMap JobCtl (Max UTCTime) -> Crontab JobCtl -> UTCTime -> Maybe (JobCtl, CronNextMatch UTCTime)
|
||||||
earliestJob settings lastTimes crontab now = foldr go' Nothing $ HashMap.toList crontab
|
earliestJob settings lastTimes crontab now = foldr go' Nothing $ HashMap.toList crontab
|
||||||
where
|
where
|
||||||
go' (jobCtl, cron) mbPrev
|
go' (jobCtl, cron) = Just . ($ (jobCtl, t)) . maybe id (minOn cmpProj)
|
||||||
| Just (_, t') <- mbPrev
|
|
||||||
, t' < t
|
|
||||||
= mbPrev
|
|
||||||
| otherwise
|
|
||||||
= Just (jobCtl, t)
|
|
||||||
where
|
where
|
||||||
t = nextCronMatch appTZ (getMax <$> HashMap.lookup jobCtl lastTimes) (debouncingAcc settings jobCtl) now cron
|
t = nextCronMatch appTZ (getMax <$> HashMap.lookup jobCtl lastTimes) (debouncingAcc settings jobCtl) now cron
|
||||||
|
cmpProj (j, qT) = ( qT
|
||||||
|
, getMax <$> HashMap.lookup j lastTimes
|
||||||
|
, j
|
||||||
|
)
|
||||||
|
|
||||||
waitUntil :: (Eq a, MonadUnliftIO m, MonadLogger m) => TVar a -> a -> UTCTime -> m Bool
|
waitUntil :: (Eq a, MonadUnliftIO m, MonadLogger m) => TVar a -> a -> UTCTime -> m Bool
|
||||||
waitUntil crontabTV crontab nextTime = do
|
waitUntil crontabTV crontab nextTime = do
|
||||||
@ -513,7 +513,7 @@ handleJobs' wNum = C.mapM_ $ \jctl -> hoist delimitInternalState . withJobWorker
|
|||||||
$logInfoS logIdent "DetermineCrontab"
|
$logInfoS logIdent "DetermineCrontab"
|
||||||
-- logDebugS logIdent $ tshow newCTab
|
-- logDebugS logIdent $ tshow newCTab
|
||||||
mapReaderT (liftIO . atomically) $
|
mapReaderT (liftIO . atomically) $
|
||||||
lift . void . flip swapTVar newCTab =<< asks jobCrontab
|
lift . flip writeTVar newCTab =<< asks jobCrontab
|
||||||
handleCmd (JobCtlGenerateHealthReport kind) = do
|
handleCmd (JobCtlGenerateHealthReport kind) = do
|
||||||
hrStorage <- getsYesod appHealthReport
|
hrStorage <- getsYesod appHealthReport
|
||||||
$logDebugS logIdent [st|#{tshow kind}...|]
|
$logDebugS logIdent [st|#{tshow kind}...|]
|
||||||
|
|||||||
@ -315,17 +315,13 @@ dispatchJobInjectFiles = JobHandlerException . maybeT (return ()) $ do
|
|||||||
sz = fromIntegral $ Minio.oiSize objInfo
|
sz = fromIntegral $ Minio.oiSize objInfo
|
||||||
|
|
||||||
fRef' <- runDB $ do
|
fRef' <- runDB $ do
|
||||||
|
logger <- askLoggerIO
|
||||||
|
|
||||||
chunkVar <- newEmptyTMVarIO
|
chunkVar <- newEmptyTMVarIO
|
||||||
dbAsync <- allocateLinkedAsync $ do
|
dbAsync <- allocateLinkedAsync $ do
|
||||||
atomically $ isEmptyTMVar chunkVar >>= guard . not
|
let report = go 0 0 Nothing =<< liftIO (getTime Monotonic)
|
||||||
sinkFileDB False $ C.unfoldM (\x -> fmap (, x) <$> atomically (takeTMVar chunkVar)) ()
|
|
||||||
|
|
||||||
logger <- askLoggerIO
|
|
||||||
didSend <- maybeT (return False) . hoistMaybeM . runAppMinio . runMaybeT $ do
|
|
||||||
objRes <- catchIfMaybeT minioIsDoesNotExist $ Minio.getObject uploadBucket obj Minio.defaultGetObjectOptions
|
|
||||||
let sendChunks = go 0 0 Nothing =<< liftIO (getTime Monotonic)
|
|
||||||
where
|
where
|
||||||
go :: forall m. MonadIO m => Natural -> Int64 -> Maybe TimeSpec -> TimeSpec -> ConduitT ByteString Void m ()
|
go :: forall m. MonadIO m => Natural -> Int64 -> Maybe TimeSpec -> TimeSpec -> ConduitT ByteString ByteString m ()
|
||||||
go c accsz lastReport startT = do
|
go c accsz lastReport startT = do
|
||||||
currT <- liftIO $ getTime Monotonic
|
currT <- liftIO $ getTime Monotonic
|
||||||
chunk' <- await
|
chunk' <- await
|
||||||
@ -345,13 +341,18 @@ dispatchJobInjectFiles = JobHandlerException . maybeT (return ()) $ do
|
|||||||
return . ceiling $ (toRational currT - toRational startT) / fromIntegral accsz' * (fromIntegral sz - fromIntegral accsz)
|
return . ceiling $ (toRational currT - toRational startT) / fromIntegral accsz' * (fromIntegral sz - fromIntegral accsz)
|
||||||
when (lastReport' /= lastReport || sz' >= fromIntegral sz) $
|
when (lastReport' /= lastReport || sz' >= fromIntegral sz) $
|
||||||
flip runLoggingT logger . $logInfoS "InjectFiles" . mconcat $ catMaybes
|
flip runLoggingT logger . $logInfoS "InjectFiles" . mconcat $ catMaybes
|
||||||
[ pure [st|Sinking chunk ##{tshow c} (#{textBytes csz}): #{textBytes sz'}/#{textBytes sz} (#{tshow p}%)|]
|
[ pure [st|Sinking chunk ##{tshow c}: #{textBytes sz'}/#{textBytes sz} (#{tshow p}%)|]
|
||||||
, eta <&> \eta' -> [st| ETA #{textDuration eta'}|]
|
, eta <&> \eta' -> [st| ETA #{textDuration eta'}|]
|
||||||
, pure "..."
|
, pure "..."
|
||||||
]
|
]
|
||||||
atomically . putTMVar chunkVar $ Just chunk
|
yield chunk
|
||||||
go c' sz' lastReport' startT
|
go c' sz' lastReport' startT
|
||||||
lift . runConduit $ Minio.gorObjectStream objRes .| sendChunks
|
atomically $ isEmptyTMVar chunkVar >>= guard . not
|
||||||
|
sinkFileDB False $ C.unfoldM (\x -> fmap (, x) <$> atomically (takeTMVar chunkVar)) () .| persistentTokenBucketRateLimit' TokenBucketInjectFiles olength .| report
|
||||||
|
|
||||||
|
didSend <- maybeT (return False) . hoistMaybeM . runAppMinio . runMaybeT $ do
|
||||||
|
objRes <- catchIfMaybeT minioIsDoesNotExist $ Minio.getObject uploadBucket obj Minio.defaultGetObjectOptions
|
||||||
|
lift . runConduit $ Minio.gorObjectStream objRes .| C.mapM_ (atomically . putTMVar chunkVar . Just)
|
||||||
return True
|
return True
|
||||||
if
|
if
|
||||||
| not didSend -> Nothing <$ cancel dbAsync
|
| not didSend -> Nothing <$ cancel dbAsync
|
||||||
|
|||||||
@ -313,7 +313,7 @@ data JobState = JobState
|
|||||||
, jobPoolManager :: Async ()
|
, jobPoolManager :: Async ()
|
||||||
, jobCron :: Async ()
|
, jobCron :: Async ()
|
||||||
, jobShutdown :: TMVar ()
|
, jobShutdown :: TMVar ()
|
||||||
, jobCurrentCrontab :: TVar (Maybe (UTCTime, [(CronNextMatch UTCTime, JobCtl)]))
|
, jobCurrentCrontab :: TVar (Maybe (UTCTime, [(JobCtl, Maybe UTCTime, CronNextMatch UTCTime)]))
|
||||||
}
|
}
|
||||||
|
|
||||||
jobWorkerNames :: JobState -> Set JobWorkerId
|
jobWorkerNames :: JobState -> Set JobWorkerId
|
||||||
|
|||||||
12
src/Utils.hs
12
src/Utils.hs
@ -1318,6 +1318,18 @@ clampMin, clampMax :: Ord a
|
|||||||
clampMin = max
|
clampMin = max
|
||||||
clampMax = min
|
clampMax = min
|
||||||
|
|
||||||
|
minBy,maxBy :: (a -> a -> Ordering) -> a -> a -> a
|
||||||
|
minBy cmp a b = case a `cmp` b of
|
||||||
|
GT -> b
|
||||||
|
_ -> a
|
||||||
|
maxBy cmp a b = case a `cmp` b of
|
||||||
|
LT -> b
|
||||||
|
_ -> a
|
||||||
|
|
||||||
|
minOn,maxOn :: Ord b => (a -> b) -> a -> a -> a
|
||||||
|
minOn = minBy . comparing
|
||||||
|
maxOn = maxBy . comparing
|
||||||
|
|
||||||
------------
|
------------
|
||||||
-- Random --
|
-- Random --
|
||||||
------------
|
------------
|
||||||
|
|||||||
@ -2,12 +2,15 @@ module Utils.PersistentTokenBucket
|
|||||||
( TokenBucketSettings(..)
|
( TokenBucketSettings(..)
|
||||||
, persistentTokenBucketTryAlloc', persistentTokenBucketTakeC'
|
, persistentTokenBucketTryAlloc', persistentTokenBucketTakeC'
|
||||||
, persistentTokenBucketTryAlloc, persistentTokenBucketTakeC
|
, persistentTokenBucketTryAlloc, persistentTokenBucketTakeC
|
||||||
|
, persistentTokenBucketRateLimit', persistentTokenBucketRateLimit
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Import.NoFoundation
|
import Import.NoFoundation
|
||||||
|
|
||||||
import qualified Data.Conduit.Combinators as C
|
import qualified Data.Conduit.Combinators as C
|
||||||
|
|
||||||
|
import Control.Concurrent.STM.Delay
|
||||||
|
|
||||||
|
|
||||||
data TokenBucketSettings = TokenBucketSettings
|
data TokenBucketSettings = TokenBucketSettings
|
||||||
{ tbsIdent :: TokenBucketIdent
|
{ tbsIdent :: TokenBucketIdent
|
||||||
@ -83,3 +86,29 @@ persistentTokenBucketTakeC tbs cTokens = C.mapAccumWhileM tbAccum ()
|
|||||||
-> SqlPersistT m (Either () ((), i))
|
-> SqlPersistT m (Either () ((), i))
|
||||||
tbAccum x ()
|
tbAccum x ()
|
||||||
= bool (Left ()) (Right ((), x)) <$> persistentTokenBucketTryAlloc tbs (cTokens x)
|
= bool (Left ()) (Right ((), x)) <$> persistentTokenBucketTryAlloc tbs (cTokens x)
|
||||||
|
|
||||||
|
|
||||||
|
persistentTokenBucketRateLimit :: forall i m a.
|
||||||
|
( MonadIO m, Integral a )
|
||||||
|
=> TokenBucketSettings
|
||||||
|
-> (i -> a)
|
||||||
|
-> ConduitT i i m ()
|
||||||
|
persistentTokenBucketRateLimit TokenBucketSettings{tbsInvRate} cTokens = awaitForever $ \x@(cTokens -> s) -> do
|
||||||
|
yield x
|
||||||
|
let
|
||||||
|
MkFixed (fromIntegral -> dTime) = (realToFrac $ fromIntegral s * tbsInvRate :: Micro)
|
||||||
|
liftIO $ atomically . waitDelay =<< newDelay dTime
|
||||||
|
|
||||||
|
persistentTokenBucketRateLimit' :: forall i m a.
|
||||||
|
(MonadHandler m, HasAppSettings (HandlerSite m), Integral a)
|
||||||
|
=> TokenBucketIdent
|
||||||
|
-> (i -> a)
|
||||||
|
-> ConduitT i i m ()
|
||||||
|
persistentTokenBucketRateLimit' tbsIdent cTokens = do
|
||||||
|
TokenBucketConf{..} <- getsYesod $ views _appPersistentTokenBuckets ($ tbsIdent)
|
||||||
|
persistentTokenBucketRateLimit TokenBucketSettings
|
||||||
|
{ tbsIdent
|
||||||
|
, tbsDepth = tokenBucketDepth
|
||||||
|
, tbsInvRate = tokenBucketInvRate
|
||||||
|
, tbsInitialValue = tokenBucketInitialValue
|
||||||
|
} cTokens
|
||||||
|
|||||||
Reference in New Issue
Block a user