fix(avs): fix #7 by sequencing avs background jobs one after another

This commit is contained in:
Steffen Jost 2023-06-29 09:30:31 +00:00
parent aaa2d679fd
commit 6dc3d8d059
5 changed files with 70 additions and 51 deletions

View File

@ -35,8 +35,8 @@ UserAvsCard
deriving Generic deriving Generic
AvsSync AvsSync
personId AvsPersonId user UserId -- Note: we need to lookup UserAvs Entity anyway, so no benefit from storing AvsPersonId here
creationTime UTCTime creationTime UTCTime
pause Day Maybe pause Day Maybe
UniqueAvsSyncId personId UniqueAvsSyncUser user
deriving Generic deriving Generic

View File

@ -43,8 +43,7 @@ getAdminProblemsR = do
now <- liftIO getCurrentTime now <- liftIO getCurrentTime
let nowaday = utctDay now let nowaday = utctDay now
cutOffPrintDays = 7 cutOffPrintDays = 7
cutOffPrintJob = addLocalDays (-cutOffPrintDays) now cutOffPrintJob = addLocalDays (-cutOffPrintDays) now
cutOffAvsSynch = Just $ addUTCTime (-nominalHour) now -- update at most once per hour
(usersAreReachable, driversHaveAvsIds, rDriversHaveFs, noStalePrintJobs) <- runDB $ (,,,) (usersAreReachable, driversHaveAvsIds, rDriversHaveFs, noStalePrintJobs) <- runDB $ (,,,)
<$> areAllUsersReachable <$> areAllUsersReachable
@ -57,7 +56,7 @@ getAdminProblemsR = do
(Right AvsLicenceDifferences{..}) -> do (Right AvsLicenceDifferences{..}) -> do
let problemIds = avsLicenceDiffRevokeAll <> avsLicenceDiffGrantVorfeld <> avsLicenceDiffRevokeRollfeld <> avsLicenceDiffGrantRollfeld let problemIds = avsLicenceDiffRevokeAll <> avsLicenceDiffGrantVorfeld <> avsLicenceDiffRevokeRollfeld <> avsLicenceDiffGrantRollfeld
-- mapM_ (queueJob' . flip JobSynchroniseAvsId cutOffAvsSynch) problemIds -- mapM_ (queueJob' . flip JobSynchroniseAvsId cutOffAvsSynch) problemIds
runDBJobs . forM_ problemIds $ queueDBJob . flip JobSynchroniseAvsId cutOffAvsSynch runDBJobs . forM_ problemIds $ queueDBJob . flip JobSynchroniseAvsId (Just nowaday)
return $ Right return $ Right
( Set.size avsLicenceDiffRevokeAll ( Set.size avsLicenceDiffRevokeAll
, Set.size avsLicenceDiffGrantVorfeld , Set.size avsLicenceDiffGrantVorfeld

View File

@ -347,7 +347,7 @@ determineCrontab = execWriterT $ do
{ jEpoch = fromInteger nextEpoch { jEpoch = fromInteger nextEpoch
, jNumIterations = fromInteger numIntervals , jNumIterations = fromInteger numIntervals
, jIteration = fromInteger nextInterval , jIteration = fromInteger nextInterval
, jSynchAfter = Just $ toMidnight $ addDays (-7) nowaday -- at most once per week , jSynchAfter = Just $ addDays (-7) nowaday -- at most once per week
}) })
Cron Cron
{ cronInitial = CronTimestamp $ utcToLocalTimeTZ appTZ $ toTimeOfDay 22 0 0 $ utctDay nextIntervalTime { cronInitial = CronTimestamp $ utcToLocalTimeTZ appTZ $ toTimeOfDay 22 0 0 $ utctDay nextIntervalTime

View File

@ -1,16 +1,18 @@
-- SPDX-FileCopyrightText: 2022-23 Gregor Kleen <gregor.kleen@ifi.lmu.de>, Steffen Jost <s.jost@fraport.de> -- SPDX-FileCopyrightText: 2022-23 Steffen Jost <s.jost@fraport.de>
-- --
-- SPDX-License-Identifier: AGPL-3.0-or-later -- SPDX-License-Identifier: AGPL-3.0-or-later
module Jobs.Handler.SynchroniseAvs module Jobs.Handler.SynchroniseAvs
( dispatchJobSynchroniseAvs ( dispatchJobSynchroniseAvs
, dispatchJobSynchroniseAvsId , dispatchJobSynchroniseAvsId
, dispatchJobSynchroniseAvsUser , dispatchJobSynchroniseAvsUser
, dispatchJobSynchroniseAvsNext
, dispatchJobSynchroniseAvsQueue
) where ) where
import Import import Import
import qualified Database.Esqueleto.Legacy as E import qualified Database.Esqueleto.Legacy as E hiding (upsert)
import qualified Database.Esqueleto.PostgreSQL as E import qualified Database.Esqueleto.PostgreSQL as E
import qualified Database.Esqueleto.Utils as E import qualified Database.Esqueleto.Utils as E
@ -19,8 +21,9 @@ import Jobs.Queue
import Handler.Utils.Avs import Handler.Utils.Avs
dispatchJobSynchroniseAvs :: Natural -> Natural -> Natural -> Maybe UTCTime -> JobHandler UniWorX dispatchJobSynchroniseAvs :: Natural -> Natural -> Natural -> Maybe Day -> JobHandler UniWorX
dispatchJobSynchroniseAvs numIterations epoch iteration pause dispatchJobSynchroniseAvs numIterations epoch iteration pause
-- TODO: refactor so that the AvsIdLookup becomes obsolete
= JobHandlerAtomic . runConduit $ = JobHandlerAtomic . runConduit $
readUsers .| filterIteration .| sinkDBJobs readUsers .| filterIteration .| sinkDBJobs
where where
@ -33,52 +36,59 @@ dispatchJobSynchroniseAvs numIterations epoch iteration pause
userIteration, currentIteration :: Integer userIteration, currentIteration :: Integer
userIteration = toInteger (hash epoch `hashWithSalt` userId) `mod` toInteger numIterations userIteration = toInteger (hash epoch `hashWithSalt` userId) `mod` toInteger numIterations
currentIteration = toInteger iteration `mod` toInteger numIterations currentIteration = toInteger iteration `mod` toInteger numIterations
$logDebugS "SynchronisAvs" [st|User ##{tshow (fromSqlKey userId)}: sync on #{tshow userIteration}/#{tshow numIterations}, now #{tshow currentIteration}|] -- $logDebugS "SynchronisAvs" [st|User ##{tshow (fromSqlKey userId)}: sync on #{tshow userIteration}/#{tshow numIterations}, now #{tshow currentIteration}|]
guard $ userIteration == currentIteration guard $ userIteration == currentIteration
return $ JobSynchroniseAvsUser userId pause return $ JobSynchroniseAvsUser userId pause
workJobSynchroniseAvs :: Either AvsPersonId UserId -> Maybe Day -> JobHandler UniWorX -- dispatchJobSynchroniseAvs' :: Natural -> Natural -> Natural -> Maybe Day -> JobHandler UniWorX
workJobSynchroniseAvs eauid pause = JobHandlerAtomic $ do -- dispatchJobSynchroniseAvs' numIterations epoch iteration pause = JobHandlerAtomic $ do
either (return . Just) getAId eauid >>= \case ->
Nothing -> return ()
Just avsid -> do
now <- liftIO getCurrentTime
E.upsert
AvsSync { avsSyncPersonId = avsid
, avsSyncCreationTime = now
, avsSyncPause = pause
}
[ \oldSync -> AvsSyncPause E.=. E.least (E.val pause) (oldSync E.^. AvsSyncPause) ]
where
getAId uid = userAvsPersonId <<$>> getBy (UniqueUserAvsUser uid)
dispatchJobSynchroniseAvsId :: AvsPersonId -> Maybe Day -> JobHandler UniWorX dispatchJobSynchroniseAvsId :: AvsPersonId -> Maybe Day -> JobHandler UniWorX
dispatchJobSynchroniseAvsId = workJobSynchroniseAvs . Left dispatchJobSynchroniseAvsId apid pause = JobHandlerException $ do
ok <- runDBJobs $
getBy (UniqueUserAvsId apid) >>= \case
(Just Entity{entityVal=UserAvs{userAvsUser=uid}}) -> do -- known user
workJobSychronizeAvs uid pause
return True
Nothing -> -- unknown avsPersonId, attempt to create user
return False
-- flip (maybeM $ return False) (getBy $ UniqueUserAvsId apid) $ \Entity{entityVal=UserAvs{userAvsUser=uid}} -> do -- known user
-- workJobSychronizeAvs uid pause
-- return True
unless ok $ void $ maybeCatchAll $ upsertAvsUserById apid
dispatchJobSynchroniseAvsUser :: UserId -> Maybe Day -> JobHandler UniWorX dispatchJobSynchroniseAvsUser :: UserId -> Maybe Day -> JobHandler UniWorX
dispatchJobSynchroniseAvsUser = workJobSynchroniseAvs . Right dispatchJobSynchroniseAvsUser uid pause = JobHandlerException $ runDBJobs $ workJobSychronizeAvs uid pause
dispatchJobSynchroniseAvsNext :: JobHandler UniWorX workJobSychronizeAvs :: UserId -> Maybe Day -> JobDB ()
dispatchJobSynchroniseAvsNext = JobHandlerException $ do workJobSychronizeAvs uid pause = do
liftIO $ yield >> threadDelay 100000 now <- liftIO getCurrentTime
queueJob' JobSynchroniseAvs void $ E.upsert
AvsSync { avsSyncUser = uid
, avsSyncCreationTime = now
, avsSyncPause = pause
}
[ \oldSync -> (AvsSyncPause E.=. E.greatest (E.val pause) (oldSync E.^. AvsSyncPause)) oldSync ]
queueDBJob JobSynchroniseAvsQueue
dispatchJobSynchroniseAvs :: JobHandler UniWorX dispatchJobSynchroniseAvsQueue :: JobHandler UniWorX
dispatchJobSynchroniseAvs = JobHandlerException $ do dispatchJobSynchroniseAvsQueue = JobHandlerException $ do
syncJob <- runDB $ syncJob <- runDB $
selectFirst [] [Asc AvsSyncCreationTime] >>= \case selectFirst [] [Asc AvsSyncCreationTime] >>= \case
Nothing -> return Nothing -- nothing more to do Nothing -> return Nothing -- nothing more to do
Just Entity{entityKey=asid, entityVal=AvsSync{..}} -> do Just Entity{entityKey=asid, entityVal=AvsSync{..}} -> do
res <- getBy (UniqueUserAvsId avsSyncPersonId) >>= \case delete asid
Just Entity{entityKey=avsKey, entityVal=UserAvs{..} } getBy (UniqueUserAvsUser avsSyncUser) >>= \case
| maybe True (utctDay userAvsLastSynch <) avsSyncPause -> return $ Just avsSyncPersonId Just uae@Entity{entityVal=UserAvs{userAvsLastSynch} }
| maybe True (utctDay userAvsLastSynch <) avsSyncPause -> return $ Just uae
_other -> return Nothing -- we just updated this one within the given limit or the entity does not exist _other -> return Nothing -- we just updated this one within the given limit or the entity does not exist
queueDBJob JobSynchroniseAvsNext
delete asid ifMaybeM syncJob () $ \Entity{entityKey=avsKey, entityVal=UserAvs{userAvsPersonId=apid}} -> do
return res void $ queueJob JobSynchroniseAvsNext
ifMaybeM syncJob () $ \avsSyncPersonId -> do catch (void $ upsertAvsUserById apid) -- already updates UserAvsLastSynch
catch (void $ upsertAvsUserById avsSyncPersonId) -- already updates UserAvsLastSynch
(\exc -> do (\exc -> do
now <- liftIO getCurrentTime now <- liftIO getCurrentTime
let excMsg = tshow exc <> " at " <> tshow now let excMsg = tshow exc <> " at " <> tshow now
@ -87,4 +97,8 @@ dispatchJobSynchroniseAvs = JobHandlerException $ do
AvsInterfaceUnavailable -> return () -- ignore and retry later AvsInterfaceUnavailable -> return () -- ignore and retry later
AvsUserUnknownByAvs _ -> return () -- ignore for users no longer listed in AVS AvsUserUnknownByAvs _ -> return () -- ignore for users no longer listed in AVS
otherExc -> throwM otherExc otherExc -> throwM otherExc
) )
-- needed, since JobSynchroniseAvsQueue cannot requeue itself due to JobNoQueueSame (and having no parameters)
dispatchJobSynchroniseAvsNext :: JobHandler UniWorX
dispatchJobSynchroniseAvsNext = JobHandlerException $ void $ queueJob JobSynchroniseAvsQueue

View File

@ -93,14 +93,16 @@ data Job
| JobSynchroniseAvs { jNumIterations | JobSynchroniseAvs { jNumIterations
, jEpoch , jEpoch
, jIteration :: Natural , jIteration :: Natural
, jSynchAfter :: Maybe UTCTime , jSynchAfter :: Maybe Day
} }
| JobSynchroniseAvsUser { jUser :: UserId | JobSynchroniseAvsUser { jUser :: UserId
, jSynchAfter :: Maybe UTCTime , jSynchAfter :: Maybe Day
}
| JobSynchroniseAvsId { jAvsId:: AvsPersonId
, jSynchAfter :: Maybe UTCTime
} }
| JobSynchroniseAvsId { jAvsId :: AvsPersonId
, jSynchAfter :: Maybe Day
}
| JobSynchroniseAvsQueue
| JobSynchroniseAvsNext
| JobChangeUserDisplayEmail { jUser :: UserId | JobChangeUserDisplayEmail { jUser :: UserId
, jDisplayEmail :: UserEmail , jDisplayEmail :: UserEmail
} }
@ -337,9 +339,13 @@ jobNoQueueSame = \case
JobTruncateTransactionLog{} -> Just JobNoQueueSame JobTruncateTransactionLog{} -> Just JobNoQueueSame
JobPruneInvitations{} -> Just JobNoQueueSame JobPruneInvitations{} -> Just JobNoQueueSame
JobDeleteTransactionLogIPs{} -> Just JobNoQueueSame JobDeleteTransactionLogIPs{} -> Just JobNoQueueSame
JobSynchroniseLdap{} -> Just JobNoQueueSame
JobSynchroniseLdapUser{} -> Just JobNoQueueSame JobSynchroniseLdapUser{} -> Just JobNoQueueSame
JobSynchroniseAvsUser{} -> Just JobNoQueueSameTag JobSynchroniseAvs{} -> Just JobNoQueueSame
JobSynchroniseAvsUser{} -> Just JobNoQueueSame
JobSynchroniseAvsId{} -> Just JobNoQueueSame JobSynchroniseAvsId{} -> Just JobNoQueueSame
JobSynchroniseAvsQueue{} -> Just JobNoQueueSame
JobSynchroniseAvsNext{} -> Just JobNoQueueSame
JobChangeUserDisplayEmail{} -> Just JobNoQueueSame JobChangeUserDisplayEmail{} -> Just JobNoQueueSame
JobPruneSessionFiles{} -> Just JobNoQueueSameTag JobPruneSessionFiles{} -> Just JobNoQueueSameTag
JobPruneUnreferencedFiles{} -> Just JobNoQueueSameTag JobPruneUnreferencedFiles{} -> Just JobNoQueueSameTag