proxb / PoshRSJob

Provides an alternative to PSjobs with greater performance and less overhead to run commands in the background, freeing up the console and allowing throttling on the jobs.
MIT License
541 stars 87 forks source link

Start-RSJob: Throttle parameter have no effect inside a ForEach-Object block #195

Closed jnury closed 5 years ago

jnury commented 5 years ago

Hi,

The following code has the expected behavior (2 simultaneous executions):

Measure-Command { 
    1..10 | Start-RSJob -ScriptBlock { Start-Sleep -Seconds 3 } -Throttle 2 | Wait-RSJob
}

[...]
Seconds           : 15

But the following one doesn't (all job run simultaneously):

measure-command { 
    1..10 | ForEach-Object { Start-RSJob -ScriptBlock { Start-Sleep -Seconds 3 } -Throttle 2 } | Wait-RSJob
}

[...]
Seconds           : 3

It's because Start-RSJob is called 1 time in the first code but 10 times in the second.

Do you think it's possible to workaround this behavior ? Maybe with a variable shared between different calls (like WebSession for Invoke-WebRequest) ?

ForEach-Object is really usefull when you want to pass multiple arguments to your scriptblock (or there is another way to do that ?).

copdips commented 5 years ago

hi,

have you tried -argumentlist or -variabletoimport param of start-rsjob ?

Please provide an real example where you want to pass the args.

jnury commented 5 years ago

Thank-you for your answer.

I did not tried VariableToImport so far. This is an example code (totally useless :-D):

$scriptblock = {
    param (
        $SleepSecond,
        $Status
    )
    Write-Warning "Service was $Status"
    Start-Sleep -Seconds $SleepSecond
}

Measure-Command {
    Get-Service | Select-Object -First 10 | ForEach-Object {
        $status = $_.Status.ToString()
        if ($status -eq 'Running') {
            $argumentList = @(2, 'Started')
        }
        else {
            $argumentList = @(4, 'Not started')
        }

        Start-RSJob -ScriptBlock $scriptblock -ArgumentList $argumentList -Name $_.Name -Throttle 2
    } | Wait-RSJob | Receive-RSJob
}

The result is 4 seconds (should between 10 and 20).

MVKozlov commented 5 years ago

use -Batch - it is a key for runspace identification and throttking

copdips commented 5 years ago

Sorry I cannot get your point. In your given example, whatever the number of service you set in 'select -first', the duration only depends on the max sleep seconds defined in $arguementList. In your case the max is 4, so the result is around 4 seconds ia good for me. start-rsjob is non blocking, the sleep in the start-rsjob scriptblock wont block ForEach.

jnury commented 5 years ago

@MVKozlov : thank you ! It works:

measure-command { 
    1..10 | ForEach-Object { Start-RSJob -ScriptBlock { Start-Sleep -Seconds 3 } -Throttle 2 -Batch 'Test' } | Wait-RSJob
}

[...]
Seconds           : 15

@copdips : the total duration should depends on the average duration of a job AND the Throttle parameter. In my example, it only depends on the max duration of a job.

copdips commented 5 years ago

I think to use really the -Throttle 2 in your example, you may remove the foreach block, and move the part between foreach and start-rsjob to the script block, and you send the service object directly to start-rsjob, than start-rsjob will run in 2 threads at the same time that will take 10 to 20 seconds.

please check.the param $InputObject of start-rsjob, only this param is ValueFromPipeline, only using it will make sense of -Throttle. in your example, start-rsjob doesn't use it. be aware that start-rsjob launches only non blocking async runspaces as long as the the maxrunspaces number is not reached in the same runspacepool.

https://github.com/proxb/PoshRSJob/blob/master/PoshRSJob/Public/Start-RSJob.ps1#L170

by using -batch, all the runspaces will get the same name even they are launched by foreach, so the -throttle defined in the first and the unique runspacepool will be reused each time. without -batch and in a foreach block, every start-rsjob will create a new runspacepool, 10 services so 10 independant runspacepools with only 1 runspace inside, each does its job (sleep) in //.

https://github.com/proxb/PoshRSJob/blob/master/PoshRSJob/Public/Start-RSJob.ps1#L505

https://github.com/proxb/PoshRSJob/blob/master/PoshRSJob/Public/Start-RSJob.ps1#L515

copdips commented 5 years ago

replace -batch "test" by batch "test$($.name)" will bring you back to 4 seconds as the runspacepool names are different from each other again.

jnury commented 5 years ago

Thank you for your explanations @copdips

I want to have the same runspacePool for all my jobs as this is the only way to have Throttle working. So everything is OK now with -Batch parameter