@@ -47,6 +47,7 @@ import Data.Maybe (fromMaybe)
47import qualified OddJobs.ConfigBuilder as Job
47import qualified OddJobs.ConfigBuilder as Job
48import UnliftIO
48import UnliftIO
49import Control.Exception (ArithException)
49import Control.Exception (ArithException)
50+import Data.Bifunctor(first)
51$(Aeson.deriveJSON Aeson.defaultOptions ''Seconds)
52$(Aeson.deriveJSON Aeson.defaultOptions ''Seconds)
@@ -137,7 +138,7 @@ jobRunner Job{jobPayload, jobAttempts} = case fromJSON jobPayload of
137 Success (j :: JobPayload) ->
138 Success (j :: JobPayload) ->
138 let recur pload idx = case pload of
139 let recur pload idx = case pload of
139 PayloadAlwaysFail delay -> delaySeconds delay >> error ("Forced error after " <> show delay <> " seconds")
140 PayloadAlwaysFail delay -> delaySeconds delay >> error ("Forced error after " <> show delay <> " seconds")
140− PayloadSucceed delay -> void $ delaySeconds delay
141+ PayloadSucceed delay -> delaySeconds delay
141 PayloadFail delay innerpload -> if idx<jobAttempts
142 PayloadFail delay innerpload -> if idx<jobAttempts
142 then recur innerpload (idx + 1)
143 then recur innerpload (idx + 1)
143 else delaySeconds delay >> error ("Forced error after " <> show delay <> " seconds. step=" <> show idx)
144 else delaySeconds delay >> error ("Forced error after " <> show delay <> " seconds. step=" <> show idx)
@@ -235,8 +236,35 @@ withRandomTable jobPool action = do
235 (liftIO $ Pool.withResource jobPool (\conn -> liftIO $ Migrations.createJobTable conn tname) >> runInIO (action tname))
236 (liftIO $ Pool.withResource jobPool (\conn -> liftIO $ Migrations.createJobTable conn tname) >> runInIO (action tname))
236 (liftIO $ Pool.withResource jobPool $ \conn -> liftIO $ void $ PGS.execute conn "drop table if exists ?" (Only tname))
237 (liftIO $ Pool.withResource jobPool $ \conn -> liftIO $ void $ PGS.execute conn "drop table if exists ?" (Only tname))
238−withRandomResourceTables :: (MonadBaseControl
239+runSingleJobFromQueueBlock :: Job.Config -> IO (Maybe (Either SomeException ()))
240+runSingleJobFromQueueBlock cfg =
241+ traverse waitCatch =<< runSingleJobFromQueue cfg
243+-- | waits untill the job has started
244+runSingleJobFromQueue :: Job.Config -> IO (Maybe (Async ()))
245+runSingleJobFromQueue config' = do
246+ r <- liftIO $ newIORef mempty
247+ waitTillJobStart <- newEmptyMVar
248+ let config = config' {
249+ Job.cfgPollingInterval = 0
250+ , Job.cfgJobRunner = \job -> do
251+ putMVar waitTillJobStart ()
254+ let monitorEnv = Job.RunnerEnv
255+ { Job.envConfig = config
256+ , Job.envJobThreadsRef = r
258+ result <- runReaderT (Job.pollRunJob "runSingleJobFromQueue" $ readResourceConfig config) monitorEnv
259+ forM_ result $ const $ readMVar waitTillJobStart
262+readResourceConfig :: Job.Config -> Maybe Job.ResourceCfg
263+readResourceConfig cfg = case Job.cfgConcurrencyControl cfg of
264+ Job.ResourceLimits res -> Just res
239− IO m, MonadUnliftIO m) => Int -> Pool Connection -> Job.TableName -> (Job.ResourceCfg -> m a) -> m a
267+withRandomResourceTables :: (MonadBaseControl IO m, MonadUnliftIO m) => Int -> Pool Connection -> Job.TableName -> (Job.ResourceCfg -> m a) -> m a
240withRandomResourceTables defaultLimit jobPool tname action = do
268withRandomResourceTables defaultLimit jobPool tname action = do
241 resCfgResourceTable <- liftIO $ fromString . ("resources_" <>) <$> replicateM 10 (R.randomRIO ('a', 'z'))
269 resCfgResourceTable <- liftIO $ fromString . ("resources_" <>) <$> replicateM 10 (R.randomRIO ('a', 'z'))
242 resCfgUsageTable <- liftIO $ fromString . ("usage_" <>) <$> replicateM 10 (R.randomRIO ('a', 'z'))
270 resCfgUsageTable <- liftIO $ fromString . ("usage_" <>) <$> replicateM 10 (R.randomRIO ('a', 'z'))
@@ -262,7 +290,13 @@ withNewJobMonitor jobPool actualTest = do
262 withRandomTable jobPool $ \tname -> do
290 withRandomTable jobPool $ \tname -> do
263 withNamedJobMonitor tname jobPool Prelude.id (actualTest tname)
291 withNamedJobMonitor tname jobPool Prelude.id (actualTest tname)
293+withNamedJobMonitor :: Job.TableName -> Pool Connection -> (Job.Config -> Job.Config) -> (IORef [Job.LogEvent] -> IO a) -> IO a
265−withNamedJobMonitor tname jobPool cfgFn actualTest = do
294+withNamedJobMonitor tname jobPool cfgFn actualTest =
295+ withConfig tname jobPool cfgFn $ \logRef cfg -> do
296+ withAsync (Job.startJobRunner cfg) (const $ actualTest logRef)
298+withConfig :: Job.TableName -> Pool Connection -> (Job.Config -> Job.Config) -> (IORef [Job.LogEvent] -> Job.Config -> IO a) -> IO a
299+withConfig tname jobPool cfgFn configConsumer = do
266 logRef :: IORef [Job.LogEvent] <- newIORef []
300 logRef :: IORef [Job.LogEvent] <- newIORef []
267 tcache <- newTimeCache simpleTimeFormat'
301 tcache <- newTimeCache simpleTimeFormat'
268 withTimedFastLogger tcache LogNone $ \tlogger -> do
302 withTimedFastLogger tcache LogNone $ \tlogger -> do
@@ -270,7 +304,7 @@ withNamedJobMonitor tname jobPool cfgFn actualTest = do
270 tlogger $ \t -> toLogStr t <> " | " <> Job.defaultLogStr Job.defaultJobType logLevel logEvent
304 tlogger $ \t -> toLogStr t <> " | " <> Job.defaultLogStr Job.defaultJobType logLevel logEvent
271 atomicModifyIORef' logRef (\logs -> (logEvent:logs, ()))
305 atomicModifyIORef' logRef (\logs -> (logEvent:logs, ()))
272 cfg = Job.mkConfig flogger tname jobPool Job.UnlimitedConcurrentJobs jobRunner (\cfg' -> cfgFn $ cfg'{Job.cfgDefaultMaxAttempts=testMaxAttempts})
306 cfg = Job.mkConfig flogger tname jobPool Job.UnlimitedConcurrentJobs jobRunner (\cfg' -> cfgFn $ cfg'{Job.cfgDefaultMaxAttempts=testMaxAttempts})
273− withAsync (Job.startJobRunner cfg) (const $ actualTest logRef)
307+ configConsumer logRef cfg
275payloadGen :: MonadGen m => m JobPayload
309payloadGen :: MonadGen m => m JobPayload
276payloadGen = Gen.recursive Gen.choice nonRecursive recursive
310payloadGen = Gen.recursive Gen.choice nonRecursive recursive
@@ -300,43 +334,22 @@ testEnsureShutdown appPool jobPool = testCase "ensure shutdown" $ do
300 assertJobIdStatus conn tname logRef "Job is scheduled in future, should still be queueud" Job.Queued jobId
334 assertJobIdStatus conn tname logRef "Job is scheduled in future, should still be queueud" Job.Queued jobId
301 pure (jobId, logRef)
335 pure (jobId, logRef)
303−testGracefulShutdown appPool jobPool = testCase "ensure graceful shutdown" $ do
337+testGracefulShutdown :: Pool Connection -> Pool Connection -> TestTree
338+testGracefulShutdown appPool jobPool =
339+ testCase "ensure wait for all jobs is correct" $
304− withRandomTable jobPool $ \tname -> do
340+ withRandomTable jobPool $ \tname -> do
305− setupPreconditions 3 tname >>= \case
341+ waitTillJobStart <- newEmptyMVar
306− Nothing -> fail "Unable to setup preconditions for this test, even after trying thrice"
307− Just (j1, j2, logRef) -> do
308 Pool.withResource appPool $ \conn -> do
342 Pool.withResource appPool $ \conn -> do
343+ j1 <- Job.createJob conn tname (PayloadSucceed $ 4 * Job.defaultPollingInterval)
344+ time<- getCurrentTime
345+ j2 <- Job.scheduleJob conn tname (PayloadSucceed 0) (addUTCTime (fromIntegral $ unSeconds $ Job.defaultPollingInterval * 2) time)
346+ logRef <- withNamedJobMonitor tname jobPool (\cfg -> cfg { Job.cfgOnJobStart = \ _ -> do
347+ putMVar waitTillJobStart ()
349+ readMVar waitTillJobStart -- as soon as the job has started we quit the monitor:w
350+ pure logRef -- eg closing the withNamedJobMonitor closure shutsdwon the monitor
309 assertJobIdStatus conn tname logRef "Expecting the first job to be completed successfully if graceful shutdown is implemented correctly" Job.Success (jobId j1)
351 assertJobIdStatus conn tname logRef "Expecting the first job to be completed successfully if graceful shutdown is implemented correctly" Job.Success (jobId j1)
310 assertJobIdStatus conn tname logRef "Expecting the second job to be queued because no new job should be picked up during graceful shutdown" Job.Queued (jobId j2)
352 assertJobIdStatus conn tname logRef "Expecting the second job to be queued because no new job should be picked up during graceful shutdown" Job.Queued (jobId j2)
312− -- When this block completes execution, the graceful shutdown should have happened.
313− setupPreconditions i tname =
314− if i == (0 :: Integer)
317− withNamedJobMonitor tname jobPool Prelude.id $ \logRef -> do
318− Pool.withResource appPool $ \conn -> do
320− j1 <- Job.createJob conn tname (PayloadSucceed $ 4 * Job.defaultPollingInterval)
321− j2 <- Job.scheduleJob conn tname (PayloadSucceed 0) (addUTCTime (fromIntegral $ unSeconds $ Job.defaultPollingInterval * 2) t)
322− let waitForPrecondition = do
323− k1 <- Job.findJobByIdIO conn tname (jobId j1)
324− k2 <- Job.findJobByIdIO conn tname (jobId j2)
325− case (jobStatus <$> k1, jobStatus <$> k2) of
327− traceM "Not expecting first job to be completed so quickly. Trying again."
328− setupPreconditions (i-1) tname
330− traceM "Not expecting second job to be completed so quickly. Trying again."
331− setupPreconditions (i-1) tname
332− (Just Job.Locked, Just Job.Queued) -> pure $ Just (j1, j2, logRef) -- precondition met
333− (Just Job.Success, _) -> setupPreconditions (i-1) tname
334− (_, Just Job.Success) -> setupPreconditions (i-1) tname
335− (Just Job.Queued, Just Job.Queued) -> do
338− x -> fail $ "Unexpeted job statuses while setting up the test: " <> show x
341testJobScheduling appPool jobPool = testCase "job scheduling" $ do
354testJobScheduling appPool jobPool = testCase "job scheduling" $ do
342 withNewJobMonitor jobPool $ \tname logRef -> do
355 withNewJobMonitor jobPool $ \tname logRef -> do
@@ -447,8 +460,8 @@ testRetryBackoff appPool jobPool = testCase "retry backoff" $ do
447 modifyRetryBackoff cfg
460 modifyRetryBackoff cfg
448 = cfg { Job.cfgDefaultRetryBackoff = pure . backoff }
461 = cfg { Job.cfgDefaultRetryBackoff = pure . backoff }
450−testResourceLimitedScheduling appPool jobPool = testGroup "concurrency control by resources"
463+testResourceLimitedScheduling _appPool jobPool = testGroup "concurrency control by resources"
451− [ testCase "resources control how jobs run" $ setup $ \tname resCfg logRef conn -> do
464+ [ testCase "resources control how jobs run" $ setup jobPool $ \tname resCfg logRef conn cfg -> do
452 let firstResource = [(Job.ResourceId "first", 1)]
465 let firstResource = [(Job.ResourceId "first", 1)]
453 secondResource = [(Job.ResourceId "second", 1)]
466 secondResource = [(Job.ResourceId "second", 1)]
@@ -456,22 +469,25 @@ testResourceLimitedScheduling appPool jobPool = testGroup "concurrency control b
456 Job{jobId = secondJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) firstResource
469 Job{jobId = secondJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) firstResource
457 Job{jobId = thirdJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) secondResource
470 Job{jobId = thirdJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) secondResource
459− delaySeconds $ Job.defaultPollingInterval + Seconds 2
472+ _jone <- runSingleJobFromQueue cfg
473+ _jtwo <- runSingleJobFromQueue cfg
474+ _jthree <- runSingleJobFromQueue cfg
461 assertJobIdStatus conn tname logRef "Job was created first - it should be running" Job.Locked firstJob
476 assertJobIdStatus conn tname logRef "Job was created first - it should be running" Job.Locked firstJob
462 assertJobIdStatus conn tname logRef "Job uses same resources as first - it shouldn't be running" Job.Queued secondJob
477 assertJobIdStatus conn tname logRef "Job uses same resources as first - it shouldn't be running" Job.Queued secondJob
463 assertJobIdStatus conn tname logRef "Job uses different resources - it should be running" Job.Locked thirdJob
478 assertJobIdStatus conn tname logRef "Job uses different resources - it should be running" Job.Locked thirdJob
465− , testCase "resource-less jobs run normally" $ setup $ \tname resCfg logRef conn -> do
480+ , testCase "resource-less jobs run normally" $ setup jobPool $ \tname resCfg logRef conn cfg -> do
466 Job{jobId = firstJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) []
481 Job{jobId = firstJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) []
467 Job{jobId = secondJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) []
482 Job{jobId = secondJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) []
469− delaySeconds $ Job.defaultPollingInterval + Seconds 2
484+ _jone <- runSingleJobFromQueue cfg
485+ _jtwo <- runSingleJobFromQueue cfg
471 assertJobIdStatus conn tname logRef "First job uses no resources - it should be running" Job.Locked firstJob
487 assertJobIdStatus conn tname logRef "First job uses no resources - it should be running" Job.Locked firstJob
472 assertJobIdStatus conn tname logRef "Second job uses no resources - it should be running" Job.Locked secondJob
488 assertJobIdStatus conn tname logRef "Second job uses no resources - it should be running" Job.Locked secondJob
474− , testCase "jobs can hold multiple resources" $ setup $ \tname resCfg logRef conn -> do
490+ , testCase "jobs can hold multiple resources" $ setup jobPool $ \tname resCfg logRef conn cfg -> do
475 let firstResource = [(Job.ResourceId "first", 1)]
491 let firstResource = [(Job.ResourceId "first", 1)]
476 secondResource = [(Job.ResourceId "second", 1)]
492 secondResource = [(Job.ResourceId "second", 1)]
@@ -479,57 +495,70 @@ testResourceLimitedScheduling appPool jobPool = testGroup "concurrency control b
479 Job{jobId = secondJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) firstResource
495 Job{jobId = secondJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) firstResource
480 Job{jobId = thirdJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) secondResource
496 Job{jobId = thirdJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) secondResource
482− delaySeconds $ Job.defaultPollingInterval + Seconds 2
498+ jone <- runSingleJobFromQueue cfg
499+ jtwo <- runSingleJobFromQueue cfg
500+ jthree <- runSingleJobFromQueue cfg
501+ assertEqual "first job claimed the work" (void jone) (Just ())
502+ assertEqual "second thread was blocked" (void jtwo) Nothing
503+ assertEqual "third thread was blocked " (void jthree) Nothing
484 assertJobIdStatus conn tname logRef "Job was created first - it should be running" Job.Locked firstJob
505 assertJobIdStatus conn tname logRef "Job was created first - it should be running" Job.Locked firstJob
485 assertJobIdStatus conn tname logRef "Job uses same resources as first - it shouldn't be running" Job.Queued secondJob
506 assertJobIdStatus conn tname logRef "Job uses same resources as first - it shouldn't be running" Job.Queued secondJob
486 assertJobIdStatus conn tname logRef "Job uses same resources as first - it shouldn't be running" Job.Queued thirdJob
507 assertJobIdStatus conn tname logRef "Job uses same resources as first - it shouldn't be running" Job.Queued thirdJob
488− , testCase "resources are freed when jobs succeed" $ setup $ \tname resCfg logRef conn -> do
509+ , testCase "resources are freed when jobs succeed" $ setup jobPool $ \tname resCfg logRef conn cfg -> do
489 let firstResource = [(Job.ResourceId "first", 1)]
510 let firstResource = [(Job.ResourceId "first", 1)]
491 Job{jobId = firstJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) firstResource
512 Job{jobId = firstJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) firstResource
492 Job{jobId = secondJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) firstResource
513 Job{jobId = secondJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) firstResource
494− delaySeconds $ Job.defaultPollingInterval + Seconds 2
515+ jone <- runSingleJobFromQueue cfg
516+ _jtwo <- runSingleJobFromQueue cfg
496 assertJobIdStatus conn tname logRef "Job was created first - it should be running" Job.Locked firstJob
518 assertJobIdStatus conn tname logRef "Job was created first - it should be running" Job.Locked firstJob
497 assertJobIdStatus conn tname logRef "Job uses same resources as first - it shouldn't be running" Job.Queued secondJob
519 assertJobIdStatus conn tname logRef "Job uses same resources as first - it shouldn't be running" Job.Queued secondJob
499− delaySeconds $ (Job.defaultPollingInterval * 2) + Seconds 8
521+ forM_ jone wait
523+ _three <- runSingleJobFromQueue cfg
501 assertJobIdStatus conn tname logRef "First job should have succeeded by now" Job.Success firstJob
525 assertJobIdStatus conn tname logRef "First job should have succeeded by now" Job.Success firstJob
502 assertJobIdStatus conn tname logRef "Second job should be running after first job suceeded" Job.Locked secondJob
526 assertJobIdStatus conn tname logRef "Second job should be running after first job suceeded" Job.Locked secondJob
504− , testCase "resources are freed when jobs fail" $ setup $ \tname resCfg logRef conn -> do
528+ , testCase "resources are freed when jobs fail" $ setup jobPool $ \tname resCfg logRef conn cfg -> do
505 let firstResource = [(Job.ResourceId "first", 1)]
529 let firstResource = [(Job.ResourceId "first", 1)]
507 Job{jobId = firstJob} <- Job.createJobWithResources conn tname resCfg (PayloadAlwaysFail 10) firstResource
531 Job{jobId = firstJob} <- Job.createJobWithResources conn tname resCfg (PayloadAlwaysFail 10) firstResource
508 Job{jobId = secondJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) firstResource
532 Job{jobId = secondJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) firstResource
510− delaySeconds $ Job.defaultPollingInterval + Seconds 2
534+ jone <- runSingleJobFromQueue cfg
535+ _jtwo <- runSingleJobFromQueue cfg
512 assertJobIdStatus conn tname logRef "Job was created first - it should be running" Job.Locked firstJob
537 assertJobIdStatus conn tname logRef "Job was created first - it should be running" Job.Locked firstJob
513 assertJobIdStatus conn tname logRef "Job uses same resources as first - it shouldn't be running" Job.Queued secondJob
538 assertJobIdStatus conn tname logRef "Job uses same resources as first - it shouldn't be running" Job.Queued secondJob
515− delaySeconds $ (Job.defaultPollingInterval * 2) + Seconds 8
540+ jobRes <- forM jone waitCatch
541+ assertEqual "should've thrown an exception" (Just (Left ())) (first (const ()) <$> jobRes)
543+ _three <- runSingleJobFromQueue cfg
517 assertJobIdStatus conn tname logRef "First job should have failed by now" Job.Failed firstJob
545 assertJobIdStatus conn tname logRef "First job should have failed by now" Job.Failed firstJob
518 assertJobIdStatus conn tname logRef "Second job should be running after first job failed" Job.Locked secondJob
546 assertJobIdStatus conn tname logRef "Second job should be running after first job failed" Job.Locked secondJob
520− , testCase "resources allow for multiple usage" $ setup $ \tname resCfg logRef conn -> do
548+ , testCase "resources allow for multiple usage" $ setup jobPool $ \tname resCfg logRef conn cfg -> do
521 let firstResource = [(Job.ResourceId "first", 1)]
549 let firstResource = [(Job.ResourceId "first", 1)]
522 resCfg' = resCfg { Job.resCfgDefaultLimit = 2 }
550 resCfg' = resCfg { Job.resCfgDefaultLimit = 2 }
524 Job{jobId = firstJob} <- Job.createJobWithResources conn tname resCfg' (PayloadSucceed 10) firstResource
552 Job{jobId = firstJob} <- Job.createJobWithResources conn tname resCfg' (PayloadSucceed 10) firstResource
525 Job{jobId = secondJob} <- Job.createJobWithResources conn tname resCfg' (PayloadSucceed 10) firstResource
553 Job{jobId = secondJob} <- Job.createJobWithResources conn tname resCfg' (PayloadSucceed 10) firstResource
527− delaySeconds $ Job.defaultPollingInterval + Seconds 2
555+ _jone <- runSingleJobFromQueue cfg
556+ _jtwo <- runSingleJobFromQueue cfg
529 assertJobIdStatus conn tname logRef "First job uses some resources - it should be running" Job.Locked firstJob
558 assertJobIdStatus conn tname logRef "First job uses some resources - it should be running" Job.Locked firstJob
530 assertJobIdStatus conn tname logRef "Second job uses some resources - it should be running" Job.Locked secondJob
559 assertJobIdStatus conn tname logRef "Second job uses some resources - it should be running" Job.Locked secondJob
532− , testCase "resource usage can differ by job" $ setup $ \tname resCfg logRef conn -> do
561+ , testCase "resource usage can differ by job" $ setup jobPool $ \tname resCfg logRef conn cfg -> do
533 let firstResource n = [(Job.ResourceId "first", n)]
562 let firstResource n = [(Job.ResourceId "first", n)]
534 resCfg' = resCfg { Job.resCfgDefaultLimit = 5 }
563 resCfg' = resCfg { Job.resCfgDefaultLimit = 5 }
@@ -537,57 +566,65 @@ testResourceLimitedScheduling appPool jobPool = testGroup "concurrency control b
537 Job{jobId = secondJob} <- Job.createJobWithResources conn tname resCfg' (PayloadSucceed 10) (firstResource 3)
566 Job{jobId = secondJob} <- Job.createJobWithResources conn tname resCfg' (PayloadSucceed 10) (firstResource 3)
538 Job{jobId = thirdJob} <- Job.createJobWithResources conn tname resCfg' (PayloadSucceed 10) (firstResource 2)
567 Job{jobId = thirdJob} <- Job.createJobWithResources conn tname resCfg' (PayloadSucceed 10) (firstResource 2)
540− delaySeconds $ Job.defaultPollingInterval + Seconds 2
569+ jone <- runSingleJobFromQueue cfg
570+ _jtwo <- runSingleJobFromQueue cfg
571+ _jthree <- runSingleJobFromQueue cfg
573+ -- TODO, so it's not locked but already succeeded, which is werid, but the rest is valid?
542 assertJobIdStatus conn tname logRef "Job was created first - it should be running" Job.Locked firstJob
574 assertJobIdStatus conn tname logRef "Job was created first - it should be running" Job.Locked firstJob
543 assertJobIdStatus conn tname logRef "Job doesn't fit alongside first job - it shouldn't be running" Job.Queued secondJob
575 assertJobIdStatus conn tname logRef "Job doesn't fit alongside first job - it shouldn't be running" Job.Queued secondJob
544 assertJobIdStatus conn tname logRef "Job doesn't fit alongside first job - it shouldn't be running" Job.Queued thirdJob
576 assertJobIdStatus conn tname logRef "Job doesn't fit alongside first job - it shouldn't be running" Job.Queued thirdJob
546− delaySeconds $ (Job.defaultPollingInterval * 2) + Seconds 8
578+ forM_ jone wait
580+ _jfour <- runSingleJobFromQueue cfg
581+ _jfive <- runSingleJobFromQueue cfg
548 assertJobIdStatus conn tname logRef "First job should have succeeded by now" Job.Success firstJob
583 assertJobIdStatus conn tname logRef "First job should have succeeded by now" Job.Success firstJob
549 assertJobIdStatus conn tname logRef "Second job should be running after first job suceeded" Job.Locked secondJob
584 assertJobIdStatus conn tname logRef "Second job should be running after first job suceeded" Job.Locked secondJob
550 assertJobIdStatus conn tname logRef "Third job fits alongside second job - it should be running after first job suceeded" Job.Locked thirdJob
585 assertJobIdStatus conn tname logRef "Third job fits alongside second job - it should be running after first job suceeded" Job.Locked thirdJob
552− , testCase "resource limits permanently block too-big jobs" $ setup $ \tname resCfg logRef conn -> do
587+ , testCase "resource limits permanently block too-big jobs" $ setup jobPool $ \tname resCfg logRef conn cfg -> do
553 let firstResource = [(Job.ResourceId "first", 2)]
588 let firstResource = [(Job.ResourceId "first", 2)]
555 Job{jobId = firstJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) firstResource
590 Job{jobId = firstJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) firstResource
557− delaySeconds $ Job.defaultPollingInterval + Seconds 2
592+ void $ runSingleJobFromQueueBlock cfg
559 assertJobIdStatus conn tname logRef "Job requires too many resources - shouldn't be running" Job.Queued firstJob
594 assertJobIdStatus conn tname logRef "Job requires too many resources - shouldn't be running" Job.Queued firstJob
561− , testCase "resources with 0 limit block jobs" $ setup' 0 $ \tname resCfg logRef conn -> do
596+ , testCase "resources with 0 limit block jobs" $ setup' jobPool 0 $ \tname resCfg logRef conn cfg -> do
562 let firstResource = [(Job.ResourceId "first", 1)]
597 let firstResource = [(Job.ResourceId "first", 1)]
564 Job{jobId = firstJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) firstResource
599 Job{jobId = firstJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) firstResource
566− delaySeconds $ Job.defaultPollingInterval + Seconds 2
601+ void $ runSingleJobFromQueueBlock cfg
568 assertJobIdStatus conn tname logRef "Job can't access 0-limited resources - shouldn't be running" Job.Queued firstJob
603 assertJobIdStatus conn tname logRef "Job can't access 0-limited resources - shouldn't be running" Job.Queued firstJob
570− , testCase "resources with negative limit block jobs" $ setup' (-5) $ \tname resCfg logRef conn -> do
605+ , testCase "resources with negative limit block jobs" $ setup' jobPool (-5) $ \tname resCfg logRef conn cfg -> do
571 let firstResource = [(Job.ResourceId "first", 1)]
606 let firstResource = [(Job.ResourceId "first", 1)]
573 Job{jobId = firstJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) firstResource
608 Job{jobId = firstJob} <- Job.createJobWithResources conn tname resCfg (PayloadSucceed 10) firstResource
575− delaySeconds $ Job.defaultPollingInterval + Seconds 2
610+ void $ runSingleJobFromQueueBlock cfg
577 assertJobIdStatus conn tname logRef "Job can't access negative-limited resources - shouldn't be running" Job.Queued firstJob
612 assertJobIdStatus conn tname logRef "Job can't access negative-limited resources - shouldn't be running" Job.Queued firstJob
582− setup' defaultLimit action =
615+setup jobPool = setup' jobPool 1
583− withRandomTable jobPool $ \tname ->
584− withRandomResourceTables defaultLimit jobPool tname $ \resCfg ->
585− withNamedJobMonitor tname jobPool (cfgFn resCfg) $ \logRef -> do
586− Pool.withResource appPool $ \conn -> action tname resCfg logRef conn
617+setup' jobPool defaultLimit action =
618+ withRandomTable jobPool $ \tname ->
619+ withRandomResourceTables defaultLimit jobPool tname $ \resCfg ->
620+ withConfig tname jobPool (cfgFn resCfg) $ \logRef cfg -> do
621+ Pool.withResource jobPool $ \conn -> action tname resCfg logRef conn cfg
588 cfgFn resCfg cfg = cfg
624 cfgFn resCfg cfg = cfg
589 { Job.cfgDefaultMaxAttempts = 1 -- Simplifies some tests where we have a failing job
625 { Job.cfgDefaultMaxAttempts = 1 -- Simplifies some tests where we have a failing job
590 , Job.cfgConcurrencyControl = Job.ResourceLimits resCfg
626 , Job.cfgConcurrencyControl = Job.ResourceLimits resCfg
627+ , Job.cfgDeleteSuccessfulJobs = False
593data JobEvent = JobStart
630data JobEvent = JobStart