Skip to content
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

Wait-Event - Adding -TimeSpan #19705

Open
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

StartAutomating
Copy link

@StartAutomating StartAutomating commented May 24, 2023

Adding a -TimeSpan parameter to Wait-Event (Fixes #19704). This allows more granularity in waiting on events.

Additionally, changed the polling interval within the command to be 1/100th of the timespan.

These changes improve the performance of Wait-Event substantially, as Wait-Event was always going to wait at least 1/5th of a second.

Due to requests for this to include an argument transformation attribute, this also fixes #20112 .

PR Checklist

Copy link
Member

@SteveL-MSFT SteveL-MSFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tests are missing

@ghost ghost added the Waiting on Author The PR was reviewed and requires changes or comments from the author before being accept label May 24, 2023
@SteveL-MSFT SteveL-MSFT added the CommunityDay-Small A small PR that the PS team has identified to prioritize to review label May 24, 2023
@ghost ghost removed the Waiting on Author The PR was reviewed and requires changes or comments from the author before being accept label May 24, 2023
@StartAutomating
Copy link
Author

tests are missing

@SteveL-MSFT added a test

break;
}

received = _eventArrived.WaitOne(200);
received = _eventArrived.WaitOne((int)(_timeoutTimespan.TotalMilliseconds / 100));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wait time should be the number of milliseconds until the expiry time occurs. So you wait exactly once, not a hundred times. So before entering the loop determined the point in time when the expiry will occur, each time round the loop calculate the number of milliseconds between now and the expiry time. If the number of milliseconds is negative then you have expired.

If there is no expiry time set then WaitOne() should be used with no expiry time

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, it still has to check after each wait to see if the event has arrived. Because this is called in a relatively tight loop, this will probably happen more than once.

After an even comes it, the cmdlet checks to see if it is the correct -SourceIdentifier. If it was, then Wait-Event returns, otherwise, it checks again.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should not be right. It should be completely time independent except for the timeout. The AutoResetEvent _eventArrived should be triggered in NotifyEvent which is called by ReceivedEvents_PSEventReceived which is subscribed by Events.ReceivedEvents.PSEventReceived += ReceivedEvents_PSEventReceived;

So if there is no timeout then you should be able to use WaitOne() because the event will be triggered when there is a change.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should not be polling every some arbitrary time period, it should timeout when there is nothing to do, and it should wake up in the loop when an event is received to match the new event.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rhubarb-geek-nz noted, with two additional points:

  1. The case I'm most concerned about is the rapid timeout or return of a single upcoming event (aka, providing a -Timespan). I believe doing a slightly more efficient job of waiting forever does not help that scenario.
  2. When the event is triggered, .WaitOne should return as soon as possible. The timespan on .WaitOne is how long to wait before timing out.

If the PowerShell Team requests this change, we can address this. Otherwise, I'd prefer to keep the code change small and the level of intrigue to a minimum, so as to maximize chances of a speedy integration.

/// Negative values mean never timeout.
/// </summary>
[Parameter]
public TimeSpan TimeSpan
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we could change type of Timeout parameter to TimeSpan and add type converter (seconds to TimeSpan) to mitigate the breaking change

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be nice if we could, and maybe I'm missing some type converter magic we could use.

When casting [Timespan] from an [int], it casts to ticks, not seconds.

Under the covers I'm turning -TimeOut into -Timespan.

One other thought to avoid having a new parameter would be having -TimeOutSec become a [double], which would allow us to pass, say 0.5.

Unfortunately, I believe this is likely to create some accidental back-compat errors, as scripts with a timeout of 0.25 would run fine on later versions and not wait at all on earlier ones (because of rounding).

IMHO, having a new parameter is a much better hint that it's a newer version.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO, having a new parameter is a much better hint that it's a newer version.

If we can use an existing parameter, we should do so. Otherwise it will mislead users, it's bad UX.

PowerShell can do clever things with parameters. You can find examples by searching for ArgumentTransformationAttribute.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SteveL-MSFT what's your opinion: would you rather a new parameter or transform the old one?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me bring this up to the Cmdlets WG

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SteveL-MSFT / the working group:

I'd like to see this fix go in, as it would fairly immediately improve the performance of working modules.

I'm fairly confident that having two (or more) parameters would be the right way to go here. To restate the reasons:

  • Making -TimeoutSec a [double] would break back-compat in a weird way, due to rounding.
  • Making -TimeOutSet a [TimeSpan] would create weird casting errors, as ints become 'ticks' in [TimeSpan]s
  • Making something more complicated to make [TimeSpan] magically castable from [int] might have been nice if it was PowerShell 1, but, as it stands, adding something like a type converter here would potentially break a lot of people's code (and complicate this pull request).
  • Adding a -CancellationToken would be desirable, and I believe it should be another PR.

I am trying to keep this PR concise, as I expect to do future work in this space, and I am trying to do one PR per issue.

If there is a solid blocking reason to pend on this, please let me know what it might be, and I'll be happy to make any modifications you need.

To restate the rationale, in the hopes of a speedy resolution:

No one should have to wait a second to find out things didn't work. The faster one can time out a wait for an event that should be effectively immediate, the faster the rest of the code will work.

Please let me know if any adjustments are needed.

Hope this helps,

James

break;
}

received = _eventArrived.WaitOne(200);
received = _eventArrived.WaitOne((int)(Math.Abs(_timeoutTimespan.TotalMilliseconds) / 100));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not correct. Timeout can accept -1 and no need to divide milliseconds.

@ghost ghost added the Review - Needed The PR is being reviewed label Jun 2, 2023
@ghost
Copy link

ghost commented Jun 2, 2023

This pull request has been automatically marked as Review Needed because it has been there has not been any activity for 7 days.
Maintainer, please provide feedback and/or mark it as Waiting on Author

@StevenBucher98 StevenBucher98 added the PowerShell-Docs needed The PR was reviewed and a PowerShell Docs update is needed label Jun 5, 2023
@SteveL-MSFT SteveL-MSFT added the WG-Cmdlets general cmdlet issues label Jun 19, 2023
@SteveL-MSFT
Copy link
Member

SteveL-MSFT commented Jun 21, 2023

@PowerShell/wg-powershell-cmdlets reviewed this, we agree that the desired behavior is for -Timeout to take a [TimeSpan] and having a parameter type convertor that accepts an int (to maintain existing behavior) or a double, so that partial seconds could be specified rather than having an additional parameter.

@ghost ghost removed the Review - Needed The PR is being reviewed label Jun 21, 2023
@ghost ghost added the Review - Needed The PR is being reviewed label Jun 29, 2023
@ghost
Copy link

ghost commented Jun 29, 2023

This pull request has been automatically marked as Review Needed because it has been there has not been any activity for 7 days.
Maintainer, please provide feedback and/or mark it as Waiting on Author

@SteveL-MSFT SteveL-MSFT added WG-Reviewed A Working Group has reviewed this and made a recommendation Waiting on Author The PR was reviewed and requires changes or comments from the author before being accept labels Jul 5, 2023
@daxian-dbw daxian-dbw removed the Review - Needed The PR is being reviewed label Jul 17, 2023
@microsoft-github-policy-service
Copy link
Contributor

This pull request has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 15 days. It will be closed if no further activity occurs within 10 days of this comment.

@SteveL-MSFT
Copy link
Member

@StartAutomating can you review the CodeFactor issues? Also, CI isn't passing.

@microsoft-github-policy-service microsoft-github-policy-service bot removed the Waiting on Author The PR was reviewed and requires changes or comments from the author before being accept label Aug 13, 2023
@pull-request-quantifier-deprecated

This PR has 86 quantified lines of changes. In general, a change size of upto 200 lines is ideal for the best PR experience!


Quantification details

Label      : Small
Size       : +68 -18
Percentile : 34.4%

Total files changed: 3

Change summary by file extension:
.cs : +50 -9
.ps1 : +18 -9

Change counts above are quantified counts, based on the PullRequestQuantifier customizations.

Why proper sizing of changes matters

Optimal pull request sizes drive a better predictable PR flow as they strike a
balance between between PR complexity and PR review overhead. PRs within the
optimal size (typical small, or medium sized PRs) mean:

  • Fast and predictable releases to production:
    • Optimal size changes are more likely to be reviewed faster with fewer
      iterations.
    • Similarity in low PR complexity drives similar review times.
  • Review quality is likely higher as complexity is lower:
    • Bugs are more likely to be detected.
    • Code inconsistencies are more likely to be detected.
  • Knowledge sharing is improved within the participants:
    • Small portions can be assimilated better.
  • Better engineering practices are exercised:
    • Solving big problems by dividing them in well contained, smaller problems.
    • Exercising separation of concerns within the code changes.

What can I do to optimize my changes

  • Use the PullRequestQuantifier to quantify your PR accurately
    • Create a context profile for your repo using the context generator
    • Exclude files that are not necessary to be reviewed or do not increase the review complexity. Example: Autogenerated code, docs, project IDE setting files, binaries, etc. Check out the Excluded section from your prquantifier.yaml context profile.
    • Understand your typical change complexity, drive towards the desired complexity by adjusting the label mapping in your prquantifier.yaml context profile.
    • Only use the labels that matter to you, see context specification to customize your prquantifier.yaml context profile.
  • Change your engineering behaviors
    • For PRs that fall outside of the desired spectrum, review the details and check if:
      • Your PR could be split in smaller, self-contained PRs instead
      • Your PR only solves one particular issue. (For example, don't refactor and code new features in the same PR).

How to interpret the change counts in git diff output

  • One line was added: +1 -0
  • One line was deleted: +0 -1
  • One line was modified: +1 -1 (git diff doesn't know about modified, it will
    interpret that line like one addition plus one deletion)
  • Change percentiles: Change characteristics (addition, deletion, modification)
    of this PR in relation to all other PRs within the repository.


Was this comment helpful? 👍  :ok_hand:  :thumbsdown: (Email)
Customize PullRequestQuantifier for this repository.

@StartAutomating
Copy link
Author

@SteveL-MSFT

@StartAutomating can you review the CodeFactor issues? Also, CI isn't passing.

Fixed.

Please let me know if you'd like some tests on the ArgumentTransform attribute as well.

Copy link
Member

@JamesWTruher JamesWTruher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks pretty good - just looking for 1 additional test for the transform

$stopwatch.ElapsedMilliseconds | Should -BeGreaterThan 200
$stopwatch.ElapsedMilliseconds | Should -BeLessThan 300
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should probably have an additional test for the transform. Replicating the test at line 16 with a string value of "00:00:00.25" should do it. I don't think we need a test for a non-convertable value unless you want to validate that you're getting a parameter bind/transform fail error

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JamesWTruher a string value would be good.

It's a scenario where we'd need to use a -TransformScript, not just a property name (we'd check to see if it could be a timespan, then pull out the total seconds).

What is the preferred mechanism for using a ScriptBlock as an attribute value within the PowerShell codebase?

Also, Joel Brings up a fair point:

#20112 (comment)

Additionally, I feel it would be very nice if we could make $This in the Transform be the command the ScriptBlock is tied to (but, alas, don't quite know how we'd be able to know that within an attribute). It may also be somewhat nice to have a string property used for metadata about the transform (e.g. a description).

Any ideas on how to make these happen?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SeeminglyScience had suggested these be two separate PRs (one for the transform attribute, one for Wait-Event's changes).

As eager as I am to see Wait-Event work with subsecond timeouts (it limits functionality of obs-powershell+ScriptDeck), please let me know if you'd prefer these be separate PRs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@StartAutomating I would agree with @SeeminglyScience that these should be two separate PRs starting with the ArgumentTransform (thanks for taking the feedback and creating it!). It should probably have its own set of tests independent of this cmdlet. Once that one is merged, this PR should be simplified to just use that attribute.

@microsoft-github-policy-service microsoft-github-policy-service bot added Waiting on Author The PR was reviewed and requires changes or comments from the author before being accept and removed Waiting on Author The PR was reviewed and requires changes or comments from the author before being accept labels Aug 14, 2023
@microsoft-github-policy-service microsoft-github-policy-service bot added the Review - Needed The PR is being reviewed label Aug 28, 2023
@microsoft-github-policy-service
Copy link
Contributor

This pull request has been automatically marked as Review Needed because it has been there has not been any activity for 7 days.
Maintainer, please provide feedback and/or mark it as Waiting on Author

1 similar comment
@microsoft-github-policy-service
Copy link
Contributor

This pull request has been automatically marked as Review Needed because it has been there has not been any activity for 7 days.
Maintainer, please provide feedback and/or mark it as Waiting on Author

@StartAutomating
Copy link
Author

@SteveL-MSFT thanks for the approval, however, I was still planning on making the [ArgumentTransform] a bit better.

If this isn't fully merged, please let me do that this week.

@SteveL-MSFT SteveL-MSFT removed the CommunityDay-Small A small PR that the PS team has identified to prioritize to review label Feb 12, 2024
@microsoft-github-policy-service microsoft-github-policy-service bot removed the Review - Needed The PR is being reviewed label Feb 12, 2024
@SteveL-MSFT
Copy link
Member

@StartAutomating removing the community day label until you are ready

@microsoft-github-policy-service microsoft-github-policy-service bot added the Review - Needed The PR is being reviewed label Feb 19, 2024

This PR has 137 quantified lines of changes. In general, a change size of upto 200 lines is ideal for the best PR experience!


Quantification details

Label      : Medium
Size       : +119 -18
Percentile : 47.4%

Total files changed: 5

Change summary by file extension:
.cs : +60 -9
.ps1 : +59 -9

Change counts above are quantified counts, based on the PullRequestQuantifier customizations.

Why proper sizing of changes matters

Optimal pull request sizes drive a better predictable PR flow as they strike a
balance between between PR complexity and PR review overhead. PRs within the
optimal size (typical small, or medium sized PRs) mean:

  • Fast and predictable releases to production:
    • Optimal size changes are more likely to be reviewed faster with fewer
      iterations.
    • Similarity in low PR complexity drives similar review times.
  • Review quality is likely higher as complexity is lower:
    • Bugs are more likely to be detected.
    • Code inconsistencies are more likely to be detected.
  • Knowledge sharing is improved within the participants:
    • Small portions can be assimilated better.
  • Better engineering practices are exercised:
    • Solving big problems by dividing them in well contained, smaller problems.
    • Exercising separation of concerns within the code changes.

What can I do to optimize my changes

  • Use the PullRequestQuantifier to quantify your PR accurately
    • Create a context profile for your repo using the context generator
    • Exclude files that are not necessary to be reviewed or do not increase the review complexity. Example: Autogenerated code, docs, project IDE setting files, binaries, etc. Check out the Excluded section from your prquantifier.yaml context profile.
    • Understand your typical change complexity, drive towards the desired complexity by adjusting the label mapping in your prquantifier.yaml context profile.
    • Only use the labels that matter to you, see context specification to customize your prquantifier.yaml context profile.
  • Change your engineering behaviors
    • For PRs that fall outside of the desired spectrum, review the details and check if:
      • Your PR could be split in smaller, self-contained PRs instead
      • Your PR only solves one particular issue. (For example, don't refactor and code new features in the same PR).

How to interpret the change counts in git diff output

  • One line was added: +1 -0
  • One line was deleted: +0 -1
  • One line was modified: +1 -1 (git diff doesn't know about modified, it will
    interpret that line like one addition plus one deletion)
  • Change percentiles: Change characteristics (addition, deletion, modification)
    of this PR in relation to all other PRs within the repository.


Was this comment helpful? 👍  :ok_hand:  :thumbsdown: (Email)
Customize PullRequestQuantifier for this repository.

This PR has 144 quantified lines of changes. In general, a change size of upto 200 lines is ideal for the best PR experience!


Quantification details

Label      : Medium
Size       : +124 -20
Percentile : 48.8%

Total files changed: 6

Change summary by file extension:
.cs : +60 -9
.ps1 : +64 -11

Change counts above are quantified counts, based on the PullRequestQuantifier customizations.

Why proper sizing of changes matters

Optimal pull request sizes drive a better predictable PR flow as they strike a
balance between between PR complexity and PR review overhead. PRs within the
optimal size (typical small, or medium sized PRs) mean:

  • Fast and predictable releases to production:
    • Optimal size changes are more likely to be reviewed faster with fewer
      iterations.
    • Similarity in low PR complexity drives similar review times.
  • Review quality is likely higher as complexity is lower:
    • Bugs are more likely to be detected.
    • Code inconsistencies are more likely to be detected.
  • Knowledge sharing is improved within the participants:
    • Small portions can be assimilated better.
  • Better engineering practices are exercised:
    • Solving big problems by dividing them in well contained, smaller problems.
    • Exercising separation of concerns within the code changes.

What can I do to optimize my changes

  • Use the PullRequestQuantifier to quantify your PR accurately
    • Create a context profile for your repo using the context generator
    • Exclude files that are not necessary to be reviewed or do not increase the review complexity. Example: Autogenerated code, docs, project IDE setting files, binaries, etc. Check out the Excluded section from your prquantifier.yaml context profile.
    • Understand your typical change complexity, drive towards the desired complexity by adjusting the label mapping in your prquantifier.yaml context profile.
    • Only use the labels that matter to you, see context specification to customize your prquantifier.yaml context profile.
  • Change your engineering behaviors
    • For PRs that fall outside of the desired spectrum, review the details and check if:
      • Your PR could be split in smaller, self-contained PRs instead
      • Your PR only solves one particular issue. (For example, don't refactor and code new features in the same PR).

How to interpret the change counts in git diff output

  • One line was added: +1 -0
  • One line was deleted: +0 -1
  • One line was modified: +1 -1 (git diff doesn't know about modified, it will
    interpret that line like one addition plus one deletion)
  • Change percentiles: Change characteristics (addition, deletion, modification)
    of this PR in relation to all other PRs within the repository.


Was this comment helpful? 👍  :ok_hand:  :thumbsdown: (Email)
Customize PullRequestQuantifier for this repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Medium PowerShell-Docs needed The PR was reviewed and a PowerShell Docs update is needed Review - Needed The PR is being reviewed WG-Cmdlets general cmdlet issues WG-Reviewed A Working Group has reviewed this and made a recommendation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ArgumentTransform Attribute Wait-Event should allow more granularity
8 participants