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

Prevent access to HttpContext throught HttpContextAccessor in TryRefreshAsync #432

Merged
merged 14 commits into from
Jul 27, 2023

Conversation

amerjusupovic
Copy link
Member

No description provided.

@amerjusupovic amerjusupovic requested a review from jimmyca15 June 30, 2023 17:26
@@ -24,9 +25,12 @@ public AzureAppConfigurationRefreshMiddleware(RequestDelegate next, IConfigurati

public async Task InvokeAsync(HttpContext context)
{
foreach (var refresher in Refreshers)
using (var flowControl = ExecutionContext.SuppressFlow())
Copy link
Member

@jimmyca15 jimmyca15 Jun 30, 2023

Choose a reason for hiding this comment

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

I think it needs a comment to explain the reasoning. A suggestion

//
// Configuration refresh is meant to execute as an isolated background task.
// To prevent access of request-based resources, such as HttpContext, we suppress the execution context within the refresh operation.

Copy link
Member

Choose a reason for hiding this comment

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

Did you get a chance to iterate on this?

@amerjusupovic amerjusupovic changed the base branch from main to preview July 11, 2023 16:40
@amerjusupovic amerjusupovic changed the base branch from preview to main July 11, 2023 16:41
@amerjusupovic amerjusupovic marked this pull request as ready for review July 11, 2023 18:22
@amerjusupovic amerjusupovic requested a review from jimmyca15 July 11, 2023 18:22
foreach (var refresher in Refreshers)
//
// Configuration refresh is meant to execute as an isolated background task.
// To prevent access of request-based resources, such as HttpContext, we suppress the execution context within the refresh operation.
Copy link
Member

Choose a reason for hiding this comment

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

using (var flowControl = ExecutionContext.SuppressFlow())

Can you use an explicit type. It's hard to tell what type flowControl is from the right hand side.

@jimmyca15
Copy link
Member

Looks good, but need to figure out the destination branch.

Comment on lines 31 to 37
using (AsyncFlowControl flowControl = ExecutionContext.SuppressFlow())
{
_ = refresher.TryRefreshAsync();
foreach (IConfigurationRefresher refresher in Refreshers)
{
_ = Task.Run(() => refresher.TryRefreshAsync());
}
}

Choose a reason for hiding this comment

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

This seems like an unnecessary amount of work to do on every request. Assume the server is running at 100k RPS. I assume the refreshers themselves are throttled? You still want a check in the middleware to call them at most every 60s(?).

Copy link
Member

Choose a reason for hiding this comment

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

It is mostly no-op. Refresh only takes action on user-configured expiration interval (default 30s).

_ = refresher.TryRefreshAsync();
foreach (IConfigurationRefresher refresher in Refreshers)
{
_ = Task.Run(() => refresher.TryRefreshAsync());
Copy link
Member

Choose a reason for hiding this comment

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

Can this be _ = Task.Run(refresher.TryRefreshAsync);

Copy link
Member Author

Choose a reason for hiding this comment

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

Did you mean something else? It cannot convert from 'method group' to 'System.Action'.

}
}

refreshReadyTime = DateTimeOffset.UtcNow.Add(TimeSpan.FromSeconds(1));

Choose a reason for hiding this comment

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

Set this before doing the work to prevent other concurrent requests from doing duplicate work. I'd also suggest raising it to 15-60s, config doesn't change very often.

Also, store UtcNow in a variable to avoid calling it twice.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thans for the comment, added some logic with help from @jimmyca15 that should help prevent calling the logic in the if statement more than once per second. I don't think we can raise the interval though, since our current minimum refresh interval is 1 second and we wouldn't want to change that.

private readonly RequestDelegate _next;
private long _refreshReadyTime = DateTimeOffset.UtcNow.Ticks;
public IEnumerable<IConfigurationRefresher> Refreshers { get; }
Copy link
Member

Choose a reason for hiding this comment

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

Can you add a new-line before Refreshers to separate properties vs fields.

foreach (var refresher in Refreshers)
long refreshReadyTime = Interlocked.Read(ref _refreshReadyTime);

if (refreshReadyTime <= DateTimeOffset.UtcNow.Ticks &&
Copy link
Member

Choose a reason for hiding this comment

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

DateTimeOffset.UtcNow.Ticks needs a variable, because it should be used in the new value of _refreshReadyTime

Copy link
Member Author

Choose a reason for hiding this comment

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

Missed that, thanks

@amerjusupovic amerjusupovic merged commit 147906b into main Jul 27, 2023
@avanigupta avanigupta linked an issue Aug 2, 2023 that may be closed by this pull request
@amerjusupovic amerjusupovic deleted the ajusupovic/httpcontext-fix branch September 12, 2023 19:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unawaited refresh tasks
4 participants