Summary
Add a first-class synchronous durable-client binding to azure-functions-durable so history export and scheduled-tasks code can obtain a sync durabletask client directly, instead of building one from the async binding.
Background
durable_client_input injects the async DurableFunctionsClient (an AsyncTaskHubGrpcClient). But several features use the sync durabletask clients:
- history-export activities use a sync
TaskHubGrpcClient;
ScheduledTaskClient and ExportHistoryClient are built on the sync client.
Today, route handlers and the export activities build a sync client from the async binding's config (rpcBaseUrl / taskHubName / requiredQueryStringParameters) via an async→sync adapter (_sync_client / _build_sync_client). That adapter is duplicated and a bit awkward.
Proposal
Add a sync client binding that injects a sync client built from the same host client-config JSON, removing the adapter. A sync consumer still needs the Functions conveniences (create_http_management_payload, create_check_status_response), so the injected type should be a real SyncDurableFunctionsClient carrying those helpers — not a bare TaskHubGrpcClient.
Naming: keep DurableFunctionsClient = async (avoids a breaking rename / v1 compat), add SyncDurableFunctionsClient. Retire the sync client once durabletask ships async ScheduledTaskClient / ExportHistoryClient.
Groundwork already merged (PR #155)
_add_rich_client is generic over the client constructor.
wrap_activity leaves native multi-binding activities untouched (so an activity can carry a durable-client binding).
- The durable-client middleware invokes synchronous user functions (awaits only awaitable results).
So the remaining work is: a sync client class + a sync binding decorator + lifecycle/caching design.
Open design questions
- Decorator shape:
durable_client_input(..., sync=True) vs a separate durable_client_input_sync (the injected type differs).
- Which Functions helpers / v1 shims the sync client carries.
- Per-process caching and gRPC channel lifecycle (export fans out many activity invocations).
Context
Follow-up from #155.
Summary
Add a first-class synchronous durable-client binding to
azure-functions-durableso history export and scheduled-tasks code can obtain a sync durabletask client directly, instead of building one from the async binding.Background
durable_client_inputinjects the asyncDurableFunctionsClient(anAsyncTaskHubGrpcClient). But several features use the sync durabletask clients:TaskHubGrpcClient;ScheduledTaskClientandExportHistoryClientare built on the sync client.Today, route handlers and the export activities build a sync client from the async binding's config (
rpcBaseUrl/taskHubName/requiredQueryStringParameters) via an async→sync adapter (_sync_client/_build_sync_client). That adapter is duplicated and a bit awkward.Proposal
Add a sync client binding that injects a sync client built from the same host client-config JSON, removing the adapter. A sync consumer still needs the Functions conveniences (
create_http_management_payload,create_check_status_response), so the injected type should be a realSyncDurableFunctionsClientcarrying those helpers — not a bareTaskHubGrpcClient.Naming: keep
DurableFunctionsClient= async (avoids a breaking rename / v1 compat), addSyncDurableFunctionsClient. Retire the sync client oncedurabletaskships asyncScheduledTaskClient/ExportHistoryClient.Groundwork already merged (PR #155)
_add_rich_clientis generic over the client constructor.wrap_activityleaves native multi-binding activities untouched (so an activity can carry a durable-client binding).So the remaining work is: a sync client class + a sync binding decorator + lifecycle/caching design.
Open design questions
durable_client_input(..., sync=True)vs a separatedurable_client_input_sync(the injected type differs).Context
Follow-up from #155.