-
Notifications
You must be signed in to change notification settings - Fork 8.1k
WIP: Make module intrinsics code thread safe by using a ConcurrentBag instead of a Collection to prevent errors that surface in PSSA #9860
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…ead of a Collection. This avoids errors being thrown when PSScriptAnalyzer executes concurrent Test-ModuleManifest calls
| Dbg.Assert(input != null, "Caller should verify that input != null"); | ||
|
|
||
| input.Sort( | ||
| var inputList = new List<T>(input); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because ConcurrentBag does not support .Sort in the same way, I create a temporary List for it. This should not have a performance implication because the ConcurrentBag input gets cleared and re-populated anyway in the code below
| ss.Internal.ExportedVariables.AddRange(updated); | ||
| foreach(var psVariable in updated) | ||
| { | ||
| ss.Internal.ExportedVariables.Add(psVariable); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ConcurrentBag does not have the AddRange API, hence this change. There are only 2 places where I have to do the foreach 'by hand', therefore I do not think it is worthwhile creating an AddRange extension method unless you feel otherwise
|
@bergmeister Could you please clarify - running some Test-ModuleManifest-s in parallel raises exceptions? For the same module or different module? |
|
@iSazonov For the same module manifest if I understand the workings of PSSA correctly (not just because it was easier to repro but I think this is also what happens in sporadic production failures because PSSA runs all rules in parallel foreach file and there are 2 rules that call it to get the PSModuleInfo and check if the module manifest is in a certain expected, 'healthy' state). Yes, in theory, PSSA could try to have a (thread safe) cache similar to the |
|
I'm not sure this is the right fix. First, I wonder why a If the |
Yes, I agree too. At first look the exceptions come from enumeration (ex., exportedCmdlets property) in time when second runspace (thread) changes the same property - what is the second action? module autoloading? If so second thread must wait until first one finish to load the module and a right fix would be to isolate the module until finish the module loading. |
|
@lzybkr @iSazonov using (var ps = System.Management.Automation.PowerShell.Create())
{
ps.RunspacePool = _runSpacePool;
ps.AddCommand("Test-ModuleManifest")
.AddParameter("Path", filePath)
.AddParameter("WarningAction", ActionPreference.SilentlyContinue);
try
{
psObj = ps.Invoke();
}
{ ... }
}The above code gets called from from different threads (each rule has its own thread) that are started as follows here: allowedRules.Select(scriptRule => Task.Factory.StartNew(() => .... );Do me this looks like legit usage of the PowerShell API, i.e. the |
|
@bergmeister - the PowerShell api usage looks fine, so I'm assuming a PowerShell data structure is being shared across threads, in which case this comment applies:
|
|
@lzybkr OK, so should the locking then be somewhere in the I wonder if the call to |
|
@bergmeister - I'm honestly not too sure, but I'd really have to dig to provide a more concrete direction. |
|
@bergmeister If it is time critical you could make a workaround in PSSA for the rule. (I guess that the fix can take more time in PowerShell engine). |
|
@KirkMunro If you are digging in Import-Module you could keep in mind the race condition issue too. |
|
@iSazonov We have merged a PR yesterday in PSSA to workaround this by having a lock around calls to |
|
@bergmeister I agree to close the PR and open new issue. |
|
Opened issue #9889 and will therefore close this PR for now |
PR Summary
This avoids errors being thrown when
PSScriptAnalyzerexecutes concurrentTest-ModuleManifestcallsThe issues were reported in the following issues and the stack traces all pointed to PowerShell's code
The bug is very hard to repro, the best example was this one and one needs a fast machine as I found that once I attached the debugger to PowerShell it was not happening any more on my dev machine so I had to spin up a 16 core Azure development VM to repro whilst still having the debugger attached...
I verified that this fix makes the concurrency errors not surface any more. The errors were surfacing in all recent version of PowerShell (5.1, 6.2, 7.0-preview1) and PSScriptAnalyzer (1.18.0, 1.17.1, 1.16.1). This PR will resolve the issue going forwards. To make PSScriptAnalyzer cope with it better when version older than PS v7 are used, PSScriptAnalyzer will probably use some retry logic as proposed in this PR.
cc @JamesWTruher @Jaykul @LaurentDardenne
PR Context
PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright headerWIP:or[ WIP ]to the beginning of the title (theWIPbot will keep its status check atPendingwhile the prefix is present) and remove the prefix when the PR is ready.