PoshRSJob icon indicating copy to clipboard operation
PoshRSJob copied to clipboard

Would be great to be able to return Variable Values from an RSJob

Open kilasuit opened this issue 9 years ago • 13 comments

Although this may be a headache to implement this could be useful for helping diagnose any issues with any jobs that have been set and any possible outputs from them

kilasuit avatar Dec 30 '15 02:12 kilasuit

I'll have to think about this one. It sounds like it might be hard to do... Any chance you could provide an example of sort of what you might be looking for so I can look in that direction?

proxb avatar Jan 07 '16 03:01 proxb

I'm in the same situation. on each runspace I output some data on screen for me to later review and return a variable with the actual data I need. When I retrieve the job data using the Receive-RSJob cmd the output contains both everything whats on screen and the variable data. When using PSJobs, I could do $Job = get-job | receive-job. This will show that console output on screen and then the $Job variable will contain the output variable. Hope this makes sense.

LuisEmilioR avatar Aug 30 '16 15:08 LuisEmilioR

@greatwallx2 Any chance you could provide some code on that? I'm not quite following what you are describing (or at lease what I am thinking might be wrong) and would like to see an example that I could work off of.

proxb avatar Aug 31 '16 02:08 proxb

Great script, saved me a ton of time. @greatwallx2 and @kilasuit , If I understand your request right, this can be done. What you're asking is that a command is run in a thread, the output is a variable or array, and you want that variable/array available to the rest of the script? I am doing this currently, parsing through LUN data, grabbing the data I want on each thread/LUN, and then grabbing that data from get-rsjob | receive-rsjob. example: $lunlist | start-rsjob -name "lundata" -scriptblock{ $luninfo = #foo to parse data from XML file $lunname = #match name from specific LUN #info stored in $lunname variable, output to thread by running $lunname. This will result in the HasMoreData field being True. $lunname } #Check for completion of threads, and report status every 30 seconds do { $runningcount = (Get-RSjob -name "lundata" | ? {$_.state-eq "Running"}).count Write-Host "$runningcount threads remaining" Start-Sleep -Seconds 30 } until ($runningcount -eq 0) Write-Host "All threads complete"

#Get the output from each thread, and store it in an array $rsobjs = @() $rsObjs = Get-RSJob -Name "lundata" | Receive-RSJob

#rsObjs now contains the output from all threads, and is available to the rest of the script. The real script is more complex, I'll gladly provide more info if this is what you're looking for.

jeffpeacock avatar Aug 31 '16 18:08 jeffpeacock

@jeffpeacock - although that is certainly useful what I was more ideally requesting would be to see if it would be possible to capture all variable values that are used within the Runspace just prior to the runspace being closed.

Example being that if I were to mistype something into the scriptblock in Start-RSJob then I may be able to diagnose what it was a bit easier though this is more for those larger operations that you may pass to RSJobs.

Hopefully that helps you understand my ask a bit better now @proxb ? PS sorry for delay in better explaining this

kilasuit avatar Aug 31 '16 19:08 kilasuit

@kilasuit Yea, that does help me out more. Just to clarify, you want a way to view the variables within a runspace prior to it being disposed of for debugging purposes. This might go along with another request to have a way to debug a scriptblock, but I will research this further to see if I can push the variable values to a collection or something if a parameter is used.

proxb avatar Aug 31 '16 23:08 proxb

may be [hashtable]::Synchronized can be used in this case ?

something like that:

$syncHash = [hashtable]::Synchronized(@{})

$scriptblock = {
    try {
        #some code
    }
   finally {
        ($Using:syncHash)['vars'] = (Get-Variable)
    }
} 
Start-RSJob $scriptblock | Wait-RSJob
$syncHash['vars']

MVKozlov avatar Sep 01 '16 10:09 MVKozlov

@MVKozlov That would definitely be an alternative to get the variables. Just a quick note that you would have to use $SyncHash like this: ($Using:Synchash)['vars'] for it to work properly within the scriptblock as the parser will complain about assignment expression being invalid.

Edit This is how I would probably envision looking at the variables based on your example. It does require manual work on the user's part to add the extra code, but would produce the results that they are expecting with regard to seeing the variables within the runspace and avoid race conditions with a synchronized collection across threads.

$syncHash = [hashtable]::Synchronized(@{})

$scriptblock = {
    try {
        #some code
    }
   finally {
        #Lock the object to prevent issues with writing to the same object across threads
        [System.Threading.Monitor]::Enter(($Using:syncHash).syncroot)
        #Use pipeline variable so the job name matches the key in the hashtable 
        ($Using:syncHash)[$_] = (Get-Variable)
        #Release the object so other threads can write to it
        [System.Threading.Monitor]::Exit(($Using:syncHash).syncroot)
    }
}

1..10 | Start-RSJob -Name {$_} $scriptblock | Wait-RSJob

#Look at the variables within each scriptblock
$SyncHash

proxb avatar Sep 01 '16 11:09 proxb

OK, already found that my example not usable as it was :). Edited to correct form.

great addition! but I suggest do not use $_ in finally.... things happen ;-)

MVKozlov avatar Sep 01 '16 12:09 MVKozlov

something strange happen with this example:

$syncHash = [hashtable]::Synchronized(@{})
$scriptblock = {
    try {
        #some code
        $Test = $_
        $Test
    }
   finally {
        #Lock the object to prevent issues with writing to the same object across threads
        [System.Threading.Monitor]::Enter(($Using:syncHash).syncroot)
        #Use pipeline variable so the job name matches the key in the hashtable 
        ($Using:syncHash)[$Test] = (Get-Variable)
        #Release the object so other threads can write to it
        [System.Threading.Monitor]::Exit(($Using:syncHash).syncroot)
    }
}

1..10 | Start-RSJob -Name {$_} $scriptblock | Wait-RSJob | Receive-RSJob

#Look at the variables within each scriptblock
Write-Host '---'; $SyncHash.getenumerator() | %{ $_.Value | ? name -eq 'Test' } | Select-Object -ExpandProperty Value

and result:

1
2
3
4
5
6
7
8
9
10
---
10
10
10
10
10
10
10
10
10
10

seems Get-Variable not thread safe

MVKozlov avatar Sep 01 '16 12:09 MVKozlov

Agree that $_ was probably not the best choice for Finally block :)

I did see that _ showed the correct value when iterating through the hashtable, but $Test was definitely not playing nice. Could definitely be some weird threading issue with the contents of Get-Variable considering that this is using a runspacepool and the threads are reused. Definitely need to do some more testing.

proxb avatar Sep 01 '16 14:09 proxb

here is nasty workaround. this way we get all variables values but it is really nasty :-
($Using:syncHash)[$Test] = (Get-Variable | Foreach-Object { [PSCustomObject]@{Name=$_.Name; Value = (Invoke-Expression ('$'+$_.Name))} })

MVKozlov avatar Sep 02 '16 07:09 MVKozlov

This would be great to have implemented. I'm doing something similar where I want particular output from the jobs that are running. Currently I need to rely on what's on the output stream, which means you have to be very careful what's getting written out.

jakauppila avatar Nov 23 '17 06:11 jakauppila