-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Coding best practices: async
vs await
improvements
#2156
Conversation
Hello, Henrique! Welcome to Ocelot! Indeed, we have an issue with the improper use of threading operators, a legacy problem from the early days of Ocelot.
Are you aware that Microsoft Learn also provides documentation on best practices? I will find some links for you later. |
No worries, I will review them later. |
Really ? Thanks for the feedback. I wasn't sure if it would be useful, specially because the PR's size.
I'm not sure, I'll be thankful to get these links.
I only got based on |
@raman-m as you can see I just solved the conflicts carefully. The PR's check is failing at
Note: Please let me know if you have any comments (good or not) about the changes, I'm really really interested on. |
Henrique, there's no need to be concerned about the failed tests. Indeed, we do have several unstable tests with a failure probability of 5-10% in CI-CD builds. They tend to be most unstable during US working hours when CircleCI experiences high traffic. |
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.
The primary issue with this PR appears to be the artificial changes resulting from an EOL discrepancy. For more details, please refer to the initial code review comment.
test/Ocelot.AcceptanceTests/Steps.cs
Outdated
DownstreamHostAndPorts = new() { Localhost(port) }, | ||
DownstreamScheme = Uri.UriSchemeHttp, | ||
UpstreamPathTemplate = "/", | ||
UpstreamHttpMethod = new() { HttpMethods.Get }, |
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.
I'm dissatisfied with these superficial changes; it appears to be a line ending issue.
Which IDE do you utilize for rebasing your feature branches?
It's our common practice not to alter end-of-line characters. Additionally, we employ Visual Studio's specific .editorconfig
settings for EOL to circumvent these line ending issues. These settings are exclusive to Visual Studio, which is why we advise rebasing a feature branch onto develop solely using Visual Studio.
Have you overridden the EOL settings in .gitattributes
?
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.
I'm using Visual Studio Community 2022, and I haven't overridden the .gitattributes
neither .editorconfig
. Should I install a specific extension?
I'm really interested on be within the rules of the project, but I'm not sure what's lacking (BTW, thanks for letting me know you're dissatisfied with that, I would be the same way).
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.
It is hard to read PR changes having fake changes!
It's preferable to resolve merge conflicts while honoring the changes in the develop branch. It appears that changes are being collected from the feature branch, even when there are no substantial changes. However, conflicts should be resolved by applying your changes onto the develop branch using a merging tool.
If changes from the feature branch are prioritized (despite being insignificant), the merge tool will record them and apply CRLF end-of-line characters based on the rules specified in .editorconfig
. This is where the issue arises.
test/Ocelot.AcceptanceTests/Steps.cs
Outdated
public async Task<HttpResponseMessage> WhenIGetUrlOnTheApiGatewayAsync(string url, string cookie, string value) | ||
{ | ||
var header = new CookieHeaderValue(cookie, value); | ||
return await WhenIGetUrlOnTheApiGatewayAsync(url, header); |
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.
Why do you await here? 🙄 You can simply return a Task
!
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.
I realize I misunderstand this part at the async guide at Prefer async/await over directly returning Task. I agree we should just return the Task
.
Reading the piece below... Is it looks confuse for you too (specially the final paragraph)?
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.
I can't dispute Mr. Fowler's point, and his advice is indeed beneficial for the library code, not the testing project. Performance is my priority during testing: tests need to be quick. I'm not concerned about leaking internal variables into the broader context in testing projects or a disrupted call stack.
Please fix this!
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.
Already did!
Is the PR prepared for the second round of code review?Revert all renaming in testing projects!Converting synchronous methods to asynchronous should be done discreetly, without renaming the methods! P.S.When you rename a method in Visual Studio or use another refactoring command, Visual Studio applies the command using the default styling rules in |
Hi, what have you decided about this PR? Will you keep this PR open or create a new one? |
As I turned as draft and I'm pushing new commits, I'll try to keep this one. I'll mark as ready and request a new review later. |
@raman-m I carefully improved the PR removing so many fake changes. There were 92 changed files before and now we have 60. Also into there 60 they have lesser changes in each (because I reverted the renaming adding the suffix Reverting fake changes was successfully done for most files using commands like below (
However I couldn't be able to get rid some fake changes at some files which has those fake changes but also some real changes like I also tried to revert and manually re-add the real changes in VS but VS Code and didn't work which looks to be a problem with some repository's config (probably at Example: Note: I could got rid the above fake changes doing: reverting all the changes using the command above and bringing changes manually using I turned the PR as ready to review again with a few files at this scenario. If you want to try something feel free to ask or even to do directly (and let me know please). Files which has some few fake changes yet:
|
Henrique, you made a mistake before the creation of this PR →
The best practice is to always create a separate feature branch from develop, since develop is the default branch of the fork that needs to be synchronized with the upstream repository. Additional commits in the default 'develop' branch can restrict actions such as rebasing and creating new branches. Rebasing, in particular, becomes impossible, leaving only manual work as a viable option. 🤔 |
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.
I'm uncertain who will correct the fictitious changes. Such tasks will consume our precious time...
@@ -51,7 +51,7 @@ public Task<List<Service>> GetAsync() | |||
try | |||
{ | |||
_logger.LogInformation(() => $"Retrieving new client information for service: {ServiceName}..."); | |||
_services = _consulServiceDiscoveryProvider.GetAsync().Result; | |||
_services = _consulServiceDiscoveryProvider.GetAsync().GetAwaiter().GetResult(); |
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.
Since we are within a lock
statement, we must adhere to writing only synchronous code. I agree with the change, however...
@ggnaegi, what would occur if we eliminate the lock
? Due to the need for precise time capturing, we must prevent other threads from executing because the method modifies the class member _lastUpdateTime
on line 59. It appears it's not feasible to convert this method into truly asynchronous, correct?
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.
Not an issue!.. We will tackle the issue of lock
statements versus synchronous task calls in the future.
src/Ocelot/Configuration/Repository/ConsulFileConfigurationPollerOption.cs
Outdated
Show resolved
Hide resolved
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.
Oh, my gosh! 🤯 👇 700 lines of fake changes!
Can't you make manual changes?
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.
Of course!
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.
Advice: Copy a clean file from the develop branch in the upstream repository (which requires another copy of the upstream repo), and then apply your 2-3 line change. This is a straightforward manual modification, but it is always effective.
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.
@raman-m I was trying to figure out why those "fake changes" even though we have the .editorconfig
in place.
Note: the right version is CRLF
(as it's in .editorconfig
file).
Those "fake changes" are because some lines at these files are still as LF
(incorrect!). It means that these changes are actually the correct way to do (I know it let the PR longer/harder to review).
Are you aware of that? Should I undo it even though ? It's important to point out that any person who touch these files using an IDE like VS or VS Code will hit the same problem.
Basically if I undo these "fake changes", when someone else touch these files they'll face the same issue. Considering the long way to the repo please consider that we should keep the changes as the .editorconfig
say. Take a look at the example below.
Also you can just try to do that on your machine and see/confirm it.
Example test/Ocelot.UnitTests/Configuration/FileConfigurationSetterTests.cs
:
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.
I was trying to figure out why those "fake changes" even though we have the .editorconfig in place.
I've detailed the root cause of fictitious ("fake") changes here: they are the result of auto-refactoring commands in IDEs such as Visual Studio, Visual Code, Rider, and others.
Those "fake changes" are because some lines at these files are still as LF (incorrect!). It means that these changes are actually the correct way to do (I know it let the PR longer/harder to review).
This issue has persisted since the project's inception in 2016!
Indeed, some lines end with the LF
character from the Linux OS. Several of our contributors work on Linux and use IDEs like Visual Code, where the default newline character is LF
. Consequently, we have numerous files with inconsistent EOL characters. Do you code on Linux or Windows?
I suggest using Windows to avoid the end-of-line (EOL) issue. ❗
Are you aware of that? Should I undo it even though ? It's important to point out that any person who touch these files using an IDE like VS or VS Code will hit the same problem.
Yes, I am! What are you trying to teach me, man? I've been aware of this issue for over a year.
You'll encounter the problem if you use Linux OS.
Boot into Windows, use Visual Studio Community (it's free), avoid using auto-refactoring commands, and everything will work out fine.
Basically if I undo these "fake changes", when someone else touch these files they'll face the same issue. Considering the long way to the repo please consider that we should keep the changes as the .editorconfig say. Take a look at the example below.
It's irrelevant! The likelihood of encountering this issue remains unchanged. We cannot foresee which operating system the subsequent developer might use. The concern is not regarding future developers, but rather about your current development environment.
If your PR contains numerous artificial changes aka "fake changes", it increases the chances that subsequent developers will encounter this issue. Therefore, I request the removal of all such changes due to end-of-line characters.
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.
Fixed in commit a851098 ✔️
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.
Got the same issue using Windows (VS and VS Code), but also using linux. That's because I asked you gently, while I was trying to help. You was totally rough with no reason to act like that.
@henriqueholtz I'm initiating a rebase of develop onto upstream develop... |
…ating the param name to get rid the warning at "HttpHandlerOptionsCreator" (because its implementation call it as "options", and I kept as it's in the implementation because make more sense)
…cheTests => Reverting fake changes (using `git add --renormalize {fileName}`
…reRealCacheTests => Reverting fake changes (using `git add --renormalize {fileName}`" This reverts commit 4c6d1e7.
…eRealCacheTests => Turning method "WhenICallTheMiddleware" as async" This reverts commit 8d7743d.
…heTests => Turning method "WhenICallTheMiddleware" as async (using notepad++)
… Reverting changes
…Tests => Reverting changes" This reverts commit 07903f3.
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.
Going to fix "fake changes"...
@@ -51,7 +51,7 @@ public Task<List<Service>> GetAsync() | |||
try | |||
{ | |||
_logger.LogInformation(() => $"Retrieving new client information for service: {ServiceName}..."); | |||
_services = _consulServiceDiscoveryProvider.GetAsync().Result; | |||
_services = _consulServiceDiscoveryProvider.GetAsync().GetAwaiter().GetResult(); |
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.
Not an issue!.. We will tackle the issue of lock
statements versus synchronous task calls in the future.
@@ -18,7 +18,7 @@ private int GetDelay() | |||
{ | |||
var delay = 1000; | |||
|
|||
var fileConfig = Task.Run(async () => await _fileConfigurationRepository.Get()).Result; | |||
var fileConfig = _fileConfigurationRepository.Get().GetAwaiter().GetResult(); |
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.
We have this sync version in the interface:
int Delay { get; } |
Why not to add async version to the interface?
After that we can make private and public methods as true async →
Task<int> DelayAsync { get; }
var fileConfig = _fileConfigurationRepository.Get().GetAwaiter().GetResult(); | |
var fileConfig = await _fileConfigurationRepository.Get(); |
Optional goal is using Task.Delay
, if applicable.
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.
The expression has been revised to execute synchronously within the synchronous int GetDelay()
method.
The proposed suggestion for refactoring is beyond the scope of this pull request. We will refactor the interface in future.
TODO added in commit 6ff1011 ✔️
…FluentValidatorTests.cs
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.
Ready for Final Code Review❕
Henrique, the "fake changes," also known as EOL implicit changes, have been removed.
Please halt all development for the following reasons:
- Further development and optimization of testing projects are nonsensical; we have done sufficient work.
- I will address the remaining issues in the main library.
- I am not interested in continuing the development and enhancement of this PR.
It appears that reading Result once is acceptable when the value task is complete, as indicated by the ValueTask{TResult}.Result Property Remarks. Therefore, this change may be unnecessary due to the assurance provided by .IsCompletedSuccessfully. See remarks: https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask-1.result?view=net-8.0#remarks
Add comment with ToDo
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.
Ready for delivery ✅
async
vs await
improvements
@henriqueholtz Thank you for your contribution! ❤️ |
P.S.Please re-fork the Ocelot repository into your account due to the previously mentioned issue of having extra commits in the develop branch, which should only contain commits from the upstream develop. You will need to re-fork the repository. |
Async/await improvements according to .NET AsyncGuidance
.GetAwaiter().GetResult()
instead of.Result
;.Wait()
.Result
.GetAwaiter().GetResult()
Notes:
Ocelot.AcceptanceTests
were already failing (once in a while) atdevelop
branch even before my changes, specially:KubernetesServiceDiscoveryTests.ShouldHighlyLoadOnUnstableKubeProvider_WithRoundRobinLoadBalancing
ConsulServiceDiscoveryTests.Should_send_request_to_service_after_it_becomes_available_in_consul