Skip to content

Support 202 Automatic Polling at Worker.Extensions.DurableTask #3092

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

Open
wants to merge 7 commits into
base: dev
Choose a base branch
from

Conversation

nytian
Copy link
Collaborator

@nytian nytian commented Apr 16, 2025

Issue describing the changes in this PR

resolves #2781

Pull request checklist

  • My changes do not require documentation changes
    • Otherwise: Documentation PR is ready to merge and referenced in pending_docs.md
  • My changes should not be added to the release notes for the next release
    • Otherwise: I've added my notes to release_notes.md
  • My changes do not need to be backported to a previous version
    • Otherwise: Backport tracked by issue/PR #issue_or_pr
  • I have added all required tests (Unit tests, E2E tests)
  • My changes do not require any extra work to be leveraged by OutOfProc SDKs
    • Otherwise: That work is being tracked here: #issue_or_pr_in_each_sdk
  • My changes do not change the version of the WebJobs.Extensions.DurableTask package
    • Otherwise: major or minor version updates are reflected in /src/Worker.Extensions.DurableTask/AssemblyInfo.cs
  • My changes do not add EventIds to our EventSource logs
    • Otherwise: Ensure the EventIds are within the supported range in our existing Windows infrastructure. You may validate this with a deployed app's telemetry. You may also extend the range by completing a PR such as this one.
  • My changes should be added to v2.x branch.
    • Otherwise: This change applies exclusively to WebJobs.Extensions.DurableTask v3.x. It will be retained only in the dev and main branches and will not be merged into the v2.x branch.

@nytian nytian requested review from bachuv, cgillum and jviau April 17, 2025 21:07
}
else
{
// TODO: use the configuration DefaultAsyncRequestSleepTimeMilliseconds from durabletaskextension
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The context here is from TaskOrchestrationContext, and it doesn't have property saving config from DurableExtension. We can do this but that might require quite a lot changes, including DurableTask.Abstractions. Thus I am not sure if we should do that, or if there is any other easy way to do that.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm. I'm not sure the right approach for this. One option would be to introduce a generic property bag (Dictionary<string, object>) in TaskOrchestrationContext, and have that be populated using data from the trigger context that we get from the WebJobs extension. For example, adding a a key named df.http.defaultAsyncRequestSleepTimeMilliseconds and have the value come from host.json. We still have to add the property bag to the DurableTask.Abstractions, but we only need to make that one change and we can easily add other properties to the dictionary in the future without changing DurableTask.Abstractions a second time. Let me know if I can help explain this idea better.

public class HTTPFeatureTests
{
private readonly FunctionAppFixture _fixture;
private readonly ITestOutputHelper _output;
Copy link
Member

Choose a reason for hiding this comment

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

Looks like this code is not following our style guidelines. For example:

Suggested change
private readonly ITestOutputHelper _output;
readonly ITestOutputHelper output;

Please update accordingly.

Assert.Equal(HttpStatusCode.Accepted, response.StatusCode);
string statusQueryGetUri = await DurableHelpers.ParseStatusQueryGetUriAsync(response);

await DurableHelpers.WaitForOrchestrationStateAsync(statusQueryGetUri, "Completed", 150);
Copy link
Member

Choose a reason for hiding this comment

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

What is the 150 parameter and what are it's units? Please use a named parameter to make the code easier to understand.


// Verify that the output contains the LongRunningOrchestrator's result,
// ensuring the orchestrator completed and did not just return a 202 Accepted.
Assert.Contains("Hello Tokyo", orchestrationDetails.Output);
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 also assert the the orchestration ran for the expected length of time.

Also, is there any way we can confirm how many times the implementation did polling, perhaps by examining logs?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we emit any logs about the activity named HttpTaskActivityReservedName?

// If Headers is null or missing, we can't poll the Location URL, so return the response.
if (response.Headers is null)
{
break;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we add a log here that will let us know that the headers were null or missing? It might help with debugging.


// Verify that the output contains the LongRunningOrchestrator's result,
// ensuring the orchestrator completed and did not just return a 202 Accepted.
Assert.Contains("Hello Tokyo", orchestrationDetails.Output);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we emit any logs about the activity named HttpTaskActivityReservedName?

// TODO: use the configuration DefaultAsyncRequestSleepTimeMilliseconds from durabletaskextension
fireAt = context.CurrentUtcDateTime.AddMilliseconds(30000);
// Gets configuration DefaultAsyncRequestSleepTimeMilliseconds from durabletaskextension
int defaultAsyncRequestSleepTimeMilliseconds = context.Properties.TryGetValue("df.http.defaultAsyncRequestSleepTimeMilliseconds", out var value) && value is double d
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you extract this to a constant? Same with the other location of this. I get they are different assemblies with no common library to put it in - so two different constants are fine.

<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="Package source" value="C:\Users\naiyuantian\localNuget" />
Copy link
Contributor

Choose a reason for hiding this comment

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

Make sure to remove this before checking in.

@@ -39,6 +43,9 @@ public RemoteOrchestratorContext(OrchestrationRuntimeState runtimeState, TaskOrc
[JsonProperty("upperSchemaVersion")]
internal int UpperSchemaVersion { get; } = 4;

[JsonProperty("configurations")]
public Dictionary<string, object?> Configurations { get; private set; }
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't have to be a dictionary - you could have a POCO here. It will serialize all the same.

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.

TaskOrchestrationContext.CallHttpAsync` automated polling of "202 Accepted" responses.
4 participants