Skip to content

[durable-functions v4] Investigate restoring context.df.callHttp as a worker-side durable activity #318

Description

@YunchuWang

Follow-up from PR #282 (review comment #18, P3)

PR #282 added the packages/azure-functions-durable compat package (npm durable-functions v4) on the @microsoft/durabletask-js gRPC core. As part of it, context.df.callHttp(...) was made to throw, documented as a breaking change in the CHANGELOG (review comment #18). This issue tracks whether callHttp can be restored as a worker-side durable activity rather than left unsupported.

Short answer from investigation: yes, it is supportable — but as a scoped feature with behavioral deltas, not a byte-for-byte drop-in. Filing so the design + tradeoffs get a real decision.

Why it is supportable

callHttp was never an engine/backend primitive. In v3 the orchestrator emitted a CallHttp action (ActionType.CallHttp = 8) and the Functions host ran the raw HTTP send as a built-in WebJobs activity:

  • host chain: OutOfProcOrchestrationShim.cs:177 -> context.CallHttpAsync -> TaskHttpActivityShim : TaskActivity -> httpClient.SendAsync (extension Listener/TaskHttpActivityShim.cs L20/49/58)

Confirmed absent from the gRPC backend surface: proto (0 HTTP messages), durabletask-dotnet (0 CallHttp), durabletask-python core (0). So there is no host-managed durable-HTTP to inherit over gRPC.

Because it is "make an HTTP request from an orchestration, replay-safely", any backend with activities can provide it: register an auto-injected fetch-based durable activity and call it from the orchestrator. The orchestrator never does I/O; the activity does; the result is journaled -> replay-safe.

Helpful nuance re: the async (202) polling pattern

In v3 the polling loop is already SDK-orchestrated, not host-magic: DurableOrchestrationContext.callHttp returns a CallHttpWithPollingTask that drives a durable timer + re-poll loop (defaultHttpAsyncRequestSleepTimeMillseconds) when asynchronousPatternEnabled/enablePolling is true (default). Only each individual HTTP send went to the host activity. That means a worker-side port can reuse the same structure: core createTimer + repeated activity calls in orchestrator space, with the activity replacing TaskHttpActivityShim.

v3 API surface to match:

  • callHttp(options: CallHttpOptions): Task<DurableHttpResponse>
  • CallHttpOptions: method, url, body, headers, tokenSource, enablePolling (alias asynchronousPatternEnabled, default true)
  • types: DurableHttpRequest, DurableHttpResponse, ManagedIdentityTokenSource / TokenSource

What must be (re)built, and the real tradeoffs

  1. HTTP-send activity (replaces host TaskHttpActivityShim) — auto-registered fetch-based durable activity; single request/response; replay-safe. (cheap, core of the feature)
  2. 202 async-polling loop — reimplement CallHttpWithPollingTask semantics with core createTimer + repeat activity (follow Location/Retry-After until terminal). (moderate; structure exists in v3 to copy)
  3. Managed-identity auth — reimplement tokenSource / ManagedIdentityTokenSource token acquisition inside the activity. (needs an identity/token dependency decision)
  4. Trust / network boundary shift (design call, not code) — HTTP now originates from the app process, not the Functions host. Different egress, identity, and security story than v3. Must be documented; may matter for some users.
  5. Timeout / retry / error-shape parity with DurableHttpResponse.

Suggested phasing

  • Phase 1: basic callHttp (single request/response via fetch activity, no polling, no managed identity). Restores the common case; keep throwing only for the unsupported options.
  • Phase 2: 202 async-polling parity.
  • Phase 3: managed-identity tokenSource parity.

Acceptance

Links

Follow-up to PR #282 review comment #18.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions