Time management of background jobs. Time output, if not completed in x seconds,
I would like my background jobs to (start with start-job
) and time them out after x
seconds. I find it difficult, however, to keep track of the running time on each individual job (I am running aprox 400 jobs).
I wish there was a way to disable the job and set it to failed
if not completed
in X seconds, but I couldn't find the timeout parameter.
What would be a good way to track individual assignment times?
I guess I could create a hash table with each job start time and job id and check the status running
and do a manual timeout, but that sounds like reinventing the wheel. Any ideas?
Edit Thanks everyone for the fruitful discussion and great inspiration on this topic!
source to share
Just go through the list of jobs in progress and stop any that missed your timeout, for example:
$ timeout = [timespan] :: FromMinutes (1) $ now = Get-Date Get-Job | Where {$ _. State -eq 'Running' -and (($ now - $ _. PSBeginTime) -gt $ timeout)} | Stop-Job
By the way, there are more properties for the job object than shown by the default formatting, for example:
3> $ job | fl * State: Running HasMoreData: True StatusMessage: Location: localhost Command: Start-sleep -sec 30 JobStateInfo: Running Finished: System.Threading.ManualResetEvent InstanceId: de370ea8-763b-4f3b-ba0e-d45f402c8bc4 Id: 3 Name: Job3 ChildJobs: {Job4} PSBeginTime: 3/18/2012 11:07:20 AM PSEndTime: PSJobType: BackgroundJob Output: {} Error: {} Progress: {} Verbose: {} Debug: {} Warning: {}
source to share
You can specify a timeout parameter Wait-Job
:
-Timeout
Specifies the maximum time to wait for each background job in seconds. By default, -1, it waits for the job to complete, no matter how long the runs. Time starts when you send a Wait-Job Command, not a Start-Job Command.
If this time is exceeded, the wait ends and the command line is returned even if the job is still running. No error messages are displayed.
Here's some sample code:
This part just does a few test tasks:
Remove-Job -Name *
$jobs = @()
1..10 | % {
$jobs += Start-Job -ScriptBlock {
Start-Sleep -Seconds (Get-Random -Minimum 5 -Maximum 20)
}
}
The variable $timedOutJobs
contains the given dates. Then you can restart them or whatever you have.
$jobs | Wait-Job -Timeout 10
$timedOutJobs = Get-Job | ? {$_.State -eq 'Running'} | Stop-Job -PassThru
source to share
For completeness, the answer includes the maximum number of seconds per job and the maximum number of concurrent jobs. Like this is what most people are after.
The following example retrieves the printer configuration for each print server. There may be over 3000 printers, so we added regulation.
$i = 0
$maxConcurrentJobs = 40
$maxSecondsPerJob = 60
$jobTimer = @{ }
$StopLongRunningJobs = {
$jobTimer.GetEnumerator().where( {
($_.Value.IsRunning) -and
($_.Value.Elapsed.TotalSeconds -ge $maxSecondsPerJob)
}).Foreach( {
$_.Value.Stop()
Write-Verbose "Stop job '$($_.Name.Name)' that ran for '$($_.Value.Elapsed.TotalSeconds)' seconds"
Stop-Job $_.Name
})
}
Foreach ($Computer in @($GetPrinterJobResults.Where( { $_.Data }) )) {
foreach ($Printer in $Computer.Data) {
do {
& $StopLongRunningJobs
$running = @(Get-Job -State Running)
$Wait = $running.Count -ge $maxConcurrentJobs
if ($Wait) {
Write-Verbose 'Waiting for jobs to fininsh'
$null = $running | Wait-Job -Any -Timeout 5
}
} while ($Wait)
$i++
Write-Verbose "$I $($Computer.ComputerName) Get print config '$($Printer.Name)'"
$Job = $Printer | Get-PrintConfiguration -AsJob -EA Ignore
$jobtimer[$Job] = [System.Diagnostics.Stopwatch]::StartNew()
}
}
$JobResult = Get-Job | Wait-Job -Timeout $maxSecondsPerJob -EA Ignore
$JobResult = Get-Job | Receive-Job -EA Ignore
$JobResult.count
source to share