Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/azure-functions-durable/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@

### Fixes

- Forward the active Functions invocation ID in client gRPC metadata, including `getClient(context)`
and `app.client.*` starters. Invocation-local scoping preserves correlation across awaits and
concurrent calls without retaining IDs on cached clients or changing channel reuse. Calls outside
a Functions invocation omit the header
([#395](https://github.com/microsoft/durabletask-js/issues/395)).
- Preserve received failure `properties` in `durable-functions/testing` results, including
nested failures. This does not collect arbitrary JavaScript `Error` fields.
- Forward the optional `reason` from the classic `suspend()` / `resume()` aliases to the
Expand Down
19 changes: 16 additions & 3 deletions packages/azure-functions-durable/src/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { app as azFuncApp } from "@azure/functions";
import * as grpc from "@grpc/grpc-js";
import { MetadataGenerator } from "@microsoft/durabletask-js";
import { AsyncLocalStorage } from "node:async_hooks";
import { getUserAgent } from "./user-agent";

export function createAzureFunctionsMetadataGenerator(
taskHubName: string,
): MetadataGenerator {
const invocationIds = new AsyncLocalStorage<string>();

azFuncApp.hook.preInvocation((context) => {
const invocationId = context.invocationContext.invocationId;
const handler = context.functionHandler;
// Scope the handler, not the hook: the runtime invokes the handler after pre-invocation hooks.
context.functionHandler = (...args) => invocationIds.run(invocationId, handler, ...args);
});

export function createAzureFunctionsMetadataGenerator(taskHubName: string): MetadataGenerator {
const userAgent = getUserAgent();

return async (): Promise<grpc.Metadata> => {
const metadata = new grpc.Metadata();
metadata.set("taskhub", taskHubName);
metadata.set("x-user-agent", userAgent);
const invocationId = invocationIds.getStore();
if (invocationId) {
metadata.set("x-azure-functions-invocationid", invocationId);
}
return metadata;
};
}
Loading
Loading