Fix notification rate limiting
This commit is contained in:
parent
c1e6f699e0
commit
d08166420d
@ -20,6 +20,7 @@ job-workers: "_env:JOB_WORKERS:10"
|
|||||||
job-flush-interval: "_env:JOB_FLUSH:30"
|
job-flush-interval: "_env:JOB_FLUSH:30"
|
||||||
job-cron-interval: "_env:CRON_INTERVAL:60"
|
job-cron-interval: "_env:CRON_INTERVAL:60"
|
||||||
job-stale-threshold: 300
|
job-stale-threshold: 300
|
||||||
|
notification-rate-limit: 3600
|
||||||
|
|
||||||
detailed-logging: "_env:DETAILED_LOGGING:false"
|
detailed-logging: "_env:DETAILED_LOGGING:false"
|
||||||
should-log-all: "_env:LOG_ALL:false"
|
should-log-all: "_env:LOG_ALL:false"
|
||||||
|
|||||||
49
src/Cron.hs
49
src/Cron.hs
@ -168,21 +168,28 @@ nextCronMatch :: TZ -- ^ Timezone of the `Cron`-Entry
|
|||||||
nextCronMatch tz mPrev now c@Cron{..}
|
nextCronMatch tz mPrev now c@Cron{..}
|
||||||
| isNothing mPrev
|
| isNothing mPrev
|
||||||
= execRef now False cronInitial
|
= execRef now False cronInitial
|
||||||
| isJust mPrev
|
|
||||||
, isNothing cronRepeat
|
|
||||||
= MatchNone
|
|
||||||
| Just prevT <- mPrev
|
| Just prevT <- mPrev
|
||||||
, Just CronPeriod{..} <- cronRepeat
|
= case cronRepeat of
|
||||||
= case cronNext of
|
CronRepeatOnChange
|
||||||
CronAsap
|
| not $ matchesCron tz Nothing prevT c
|
||||||
| addUTCTime cronMinInterval prevT <= now
|
-> let
|
||||||
-> MatchAsap
|
cutoffTime = addUTCTime cronRateLimit prevT
|
||||||
| otherwise
|
in case execRef now False cronInitial of
|
||||||
-> MatchAt $ addUTCTime cronMinInterval prevT
|
MatchAsap
|
||||||
CronNotScheduled
|
| now < cutoffTime -> MatchAt cutoffTime
|
||||||
| CronTimestamp{ cronTimestamp = localTimeToUTCTZ tz -> ts } <- cronNext
|
MatchAt ts
|
||||||
, now <= ts -> MatchAt ts
|
| ts < cutoffTime -> MatchAt cutoffTime
|
||||||
cronNext -> execRef (addUTCTime cronMinInterval prevT) True cronNext
|
other -> other
|
||||||
|
CronRepeatScheduled cronNext
|
||||||
|
-> case cronNext of
|
||||||
|
CronAsap
|
||||||
|
| addUTCTime cronRateLimit prevT <= now
|
||||||
|
-> MatchAsap
|
||||||
|
| otherwise
|
||||||
|
-> MatchAt $ addUTCTime cronRateLimit prevT
|
||||||
|
cronNext
|
||||||
|
-> execRef (addUTCTime cronRateLimit prevT) True cronNext
|
||||||
|
_other -> MatchNone
|
||||||
where
|
where
|
||||||
execRef ref wasExecd cronAbsolute = case cronAbsolute of
|
execRef ref wasExecd cronAbsolute = case cronAbsolute of
|
||||||
CronAsap -> MatchAsap
|
CronAsap -> MatchAsap
|
||||||
@ -206,4 +213,16 @@ nextCronMatch tz mPrev now c@Cron{..}
|
|||||||
localDay <- maybeToList $ fromGregorianValid (fromIntegral cronYear) (fromIntegral cronMonth) (fromIntegral cronDayOfMonth)
|
localDay <- maybeToList $ fromGregorianValid (fromIntegral cronYear) (fromIntegral cronMonth) (fromIntegral cronDayOfMonth)
|
||||||
let localTimeOfDay = TimeOfDay (fromIntegral cronHour) (fromIntegral cronMinute) (fromIntegral cronSecond)
|
let localTimeOfDay = TimeOfDay (fromIntegral cronHour) (fromIntegral cronMinute) (fromIntegral cronSecond)
|
||||||
return $ localTimeToUTCTZ tz LocalTime{..}
|
return $ localTimeToUTCTZ tz LocalTime{..}
|
||||||
CronNotScheduled -> MatchNone
|
|
||||||
|
matchesCron :: TZ -- ^ Timezone of the `Cron`-Entry
|
||||||
|
-> Maybe UTCTime -- ^ Previous execution of the job
|
||||||
|
-> UTCTime -- ^ "Current" time
|
||||||
|
-> Cron
|
||||||
|
-> Bool
|
||||||
|
-- ^ @matchesCron tz prev prec now c@ determines whether the given `Cron`
|
||||||
|
-- specification @c@ should match @now@, under the assumption that the next
|
||||||
|
-- check will occur no earlier than @now + prec@.
|
||||||
|
matchesCron tz mPrev now cron = case nextCronMatch tz mPrev now cron of
|
||||||
|
MatchAsap -> True
|
||||||
|
MatchNone -> False
|
||||||
|
MatchAt ts -> ts <= now
|
||||||
|
|||||||
@ -7,13 +7,12 @@ module Cron.Types
|
|||||||
( Cron(..), Crontab
|
( Cron(..), Crontab
|
||||||
, CronMatch(..)
|
, CronMatch(..)
|
||||||
, CronAbsolute(..)
|
, CronAbsolute(..)
|
||||||
, CronPeriod(..)
|
, CronRepeat(..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import ClassyPrelude
|
import ClassyPrelude
|
||||||
|
|
||||||
import Utils.Lens.TH
|
import Utils.Lens.TH
|
||||||
import Control.Lens
|
|
||||||
|
|
||||||
import Data.Time
|
import Data.Time
|
||||||
|
|
||||||
@ -48,17 +47,16 @@ data CronAbsolute
|
|||||||
|
|
||||||
makeLenses_ ''CronAbsolute
|
makeLenses_ ''CronAbsolute
|
||||||
|
|
||||||
data CronPeriod = CronPeriod
|
data CronRepeat
|
||||||
{ cronMinInterval :: NominalDiffTime
|
= CronRepeatOnChange
|
||||||
, cronNext :: CronAbsolute
|
| CronRepeatScheduled CronAbsolute
|
||||||
}
|
| CronRepeatNever
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show, Read)
|
||||||
|
|
||||||
makeLenses_ ''CronPeriod
|
|
||||||
|
|
||||||
data Cron = Cron
|
data Cron = Cron
|
||||||
{ cronInitial :: CronAbsolute
|
{ cronInitial :: CronAbsolute
|
||||||
, cronRepeat :: Maybe CronPeriod
|
, cronRepeat :: CronRepeat
|
||||||
|
, cronRateLimit :: NominalDiffTime
|
||||||
}
|
}
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
|||||||
30
src/Jobs.hs
30
src/Jobs.hs
@ -197,8 +197,10 @@ execCrontab = flip evalStateT HashMap.empty . forever $ do
|
|||||||
waitUntil crontabTV crontab nextTime = runResourceT $ do
|
waitUntil crontabTV crontab nextTime = runResourceT $ do
|
||||||
diffT <- diffUTCTime nextTime <$> liftIO getCurrentTime
|
diffT <- diffUTCTime nextTime <$> liftIO getCurrentTime
|
||||||
let waitTime = fromInteger (truncate $ diffT / acc) * toRational acc
|
let waitTime = fromInteger (truncate $ diffT / acc) * toRational acc
|
||||||
waitTime' = realToFrac waitTime :: NominalDiffTime
|
waitTime'
|
||||||
$logDebugS "waitUntil" [st|#{tshow diffT} (#{tshow waitTime'})|]
|
| diffT < acc = "Done"
|
||||||
|
| otherwise = tshow (realToFrac waitTime :: NominalDiffTime)
|
||||||
|
$logDebugS "waitUntil" [st|#{tshow diffT} (#{waitTime'})|]
|
||||||
if
|
if
|
||||||
| diffT < acc -> return True
|
| diffT < acc -> return True
|
||||||
| otherwise -> do
|
| otherwise -> do
|
||||||
@ -360,10 +362,8 @@ determineCrontab = (\ct -> ct <$ pruneLastExecs ct) <=< execWriterT $ do
|
|||||||
JobCtlFlush
|
JobCtlFlush
|
||||||
Cron
|
Cron
|
||||||
{ cronInitial = CronAsap
|
{ cronInitial = CronAsap
|
||||||
, cronRepeat = Just CronPeriod
|
, cronRepeat = CronRepeatScheduled CronAsap
|
||||||
{ cronMinInterval = interval
|
, cronRateLimit = interval
|
||||||
, cronNext = CronAsap
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Nothing -> return ()
|
Nothing -> return ()
|
||||||
|
|
||||||
@ -372,10 +372,8 @@ determineCrontab = (\ct -> ct <$ pruneLastExecs ct) <=< execWriterT $ do
|
|||||||
JobCtlDetermineCrontab
|
JobCtlDetermineCrontab
|
||||||
Cron
|
Cron
|
||||||
{ cronInitial = CronAsap
|
{ cronInitial = CronAsap
|
||||||
, cronRepeat = Just CronPeriod
|
, cronRepeat = CronRepeatScheduled CronAsap
|
||||||
{ cronMinInterval = appJobCronInterval
|
, cronRateLimit = appJobCronInterval
|
||||||
, cronNext = CronAsap
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let
|
let
|
||||||
@ -384,19 +382,15 @@ determineCrontab = (\ct -> ct <$ pruneLastExecs ct) <=< execWriterT $ do
|
|||||||
(JobCtlQueue $ JobQueueNotification NotificationSheetActive{..})
|
(JobCtlQueue $ JobQueueNotification NotificationSheetActive{..})
|
||||||
Cron
|
Cron
|
||||||
{ cronInitial = CronTimestamp $ utcToLocalTimeTZ appTZ sheetActiveFrom
|
{ cronInitial = CronTimestamp $ utcToLocalTimeTZ appTZ sheetActiveFrom
|
||||||
, cronRepeat = Just CronPeriod
|
, cronRepeat = CronRepeatOnChange -- Allow repetition of the notification (if something changes), but wait at least an hour
|
||||||
{ cronMinInterval = 3600
|
, cronRateLimit = appNotificationRateLimit
|
||||||
, cronNext = CronNotScheduled -- Allow repetition of the notification (if something changes), but wait at least an hour
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
tell $ HashMap.singleton
|
tell $ HashMap.singleton
|
||||||
(JobCtlQueue $ JobQueueNotification NotificationSheetInactive{..})
|
(JobCtlQueue $ JobQueueNotification NotificationSheetInactive{..})
|
||||||
Cron
|
Cron
|
||||||
{ cronInitial = CronTimestamp . utcToLocalTimeTZ appTZ . max sheetActiveFrom $ addUTCTime (-nominalDay) sheetActiveTo
|
{ cronInitial = CronTimestamp . utcToLocalTimeTZ appTZ . max sheetActiveFrom $ addUTCTime (-nominalDay) sheetActiveTo
|
||||||
, cronRepeat = Just CronPeriod
|
, cronRepeat = CronRepeatOnChange
|
||||||
{ cronMinInterval = 3600
|
, cronRateLimit = appNotificationRateLimit
|
||||||
, cronNext = CronNotScheduled
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
runConduit $ transPipe lift (selectSource [] []) .| C.mapM_ sheetJobs
|
runConduit $ transPipe lift (selectSource [] []) .| C.mapM_ sheetJobs
|
||||||
|
|
||||||
|
|||||||
@ -86,6 +86,7 @@ data AppSettings = AppSettings
|
|||||||
, appJobFlushInterval :: Maybe NominalDiffTime
|
, appJobFlushInterval :: Maybe NominalDiffTime
|
||||||
, appJobCronInterval :: NominalDiffTime
|
, appJobCronInterval :: NominalDiffTime
|
||||||
, appJobStaleThreshold :: NominalDiffTime
|
, appJobStaleThreshold :: NominalDiffTime
|
||||||
|
, appNotificationRateLimit :: NominalDiffTime
|
||||||
|
|
||||||
, appDetailedRequestLogging :: Bool
|
, appDetailedRequestLogging :: Bool
|
||||||
-- ^ Use detailed request logging system
|
-- ^ Use detailed request logging system
|
||||||
@ -278,6 +279,7 @@ instance FromJSON AppSettings where
|
|||||||
appJobFlushInterval <- o .:? "job-flush-interval"
|
appJobFlushInterval <- o .:? "job-flush-interval"
|
||||||
appJobCronInterval <- o .: "job-cron-interval"
|
appJobCronInterval <- o .: "job-cron-interval"
|
||||||
appJobStaleThreshold <- o .: "job-stale-threshold"
|
appJobStaleThreshold <- o .: "job-stale-threshold"
|
||||||
|
appNotificationRateLimit <- o .: "notification-rate-limit"
|
||||||
|
|
||||||
appDetailedRequestLogging <- o .:? "detailed-logging" .!= defaultDev
|
appDetailedRequestLogging <- o .:? "detailed-logging" .!= defaultDev
|
||||||
appShouldLogAll <- o .:? "should-log-all" .!= defaultDev
|
appShouldLogAll <- o .:? "should-log-all" .!= defaultDev
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user