Giter Club home page Giter Club logo

azure-functions-durable-js's Introduction

Branch Status Support level Programming model Node.js versions
v3.x (Default) Build Status GA (Recommended) V4 18.x+
v2.x Build Status GA V3 14.x+

Durable Functions for Node.js

The durable-functions npm package allows you to write Durable Functions for Node.js. Durable Functions is an extension of Azure Functions that lets you write stateful functions and workflows in a serverless environment. The extension manages state, checkpoints, and restarts for you. Durable Functions' advantages include:

  • Define workflows in code. No JSON schemas or designers are needed.
  • Call other functions synchronously and asynchronously. Output from called functions can be saved to local variables.
  • Automatically checkpoint progress whenever the function schedules async work. Local state is never lost if the process recycles or the VM reboots.

You can find more information at the following links:

A durable function, or orchestration, is a solution made up of different types of Azure Functions:

  • Activity: the functions and tasks being orchestrated by your workflow.
  • Orchestrator: a function that describes the way and order actions are executed in code.
  • Client: the entry point for creating an instance of a durable orchestration.

Durable Functions' function types and features are documented in-depth here.

Version v3.x of the Durable Functions package supports the new v4 Node.js programming model for Azure Functions, which is now generally available! Compared to the v3 model, the v4 model is designed to have a more idiomatic and intuitive experience for JavaScript and TypeScript developers. You can learn more about the v4 programming model at the following links:

Getting Started

You can follow the Visual Studio Code quickstart in JavaScript or TypeScript to get started with a function chaining example, or follow the general checklist below:

  1. Install prerequisites:

  2. Create an Azure Functions app. Visual Studio Code's Azure Functions extension is recommended (version 1.10.4 or above is needed for the v4 programming model).

  3. Install the Durable Functions extension

We recommend using Azure Functions extension bundles to install the Durable Functions extension. Version 3.15 or higher of extension bundles is required for the v4 programming model. Make sure you add the following in your host.json file:

"extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[3.15.0, 4.0.0)"
}
  1. Install the durable-functions npm package (preview version) at the root of your function app:
npm install durable-functions
  1. Write an activity function (see sample in JavaScript/TypeScript):
const df = require("durable-functions");
df.app.activity("myActivity", {
    handler: async function (context) {
        // your code here
    },
});
  1. Write an orchestrator function (see sample in JavaScript/TypeScript):
const df = require("durable-functions");
df.app.orchestration("myOrchestration", function* (context) {
    // your code here
});

Note: Orchestrator functions must follow certain code constraints.

  1. Write your client function (see sample in JavaScript/TypeScript):
const df = require("durable-functions");

df.app.client.http("httpStart", {
    route: "orchestrators/{orchestratorName}",
    handler: async (request, client, context) => {
        const body = await request.json();
        const instanceId = await client.startNew(request.params.orchestratorName, { input: body });

        context.log(`Started orchestration with ID = '${instanceId}'.`);

        return client.createCheckStatusResponse(request, instanceId);
    },
});

Note: Client functions can be started by any trigger binding supported in the Azure Functions runtime version 2.x+. Node.js v4 programming model apps require at least version 4.25 of the Azure Functions runtime. Read more about trigger bindings and 2.x-supported bindings.

Samples

The Durable Functions samples demonstrate several common use cases. They are located in the samples directories (JavaScript/TypeScript). Descriptive documentation is also available:

const df = require("durable-functions");

const helloActivity = df.app.activity("hello", {
    handler: async function (input) {
        return `Hello, ${input}`;
    },
});

df.app.orchestration("helloSequence", function* (context) {
    context.log("Starting chain sample");

    const output = [];
    output.push(yield helloActivity("Tokyo"));
    output.push(yield helloActivity("Seattle"));
    output.push(yield helloActivity("Cairo"));

    return output;
});

How it works

Durable Functions

One of the key attributes of Durable Functions is reliable execution. Orchestrator functions and activity functions may be running on different VMs within a data center, and those VMs or the underlying networking infrastructure is not 100% reliable.

In spite of this, Durable Functions ensures reliable execution of orchestrations. It does so by using storage queues to drive function invocation and by periodically checkpointing execution history into storage tables (using a cloud design pattern known as Event Sourcing). That history can then be replayed to automatically rebuild the in-memory state of an orchestrator function.

Read more about Durable Functions' reliable execution.

Durable Functions JS

The durable-functions shim lets you express a workflow in code as a generator function wrapped by a call to the app.orchestration method. The Durable Functions SDK treats yield-ed calls, such as yield helloActivity("Tokyo"), as points where you want to schedule an asynchronous unit of work and wait for it to complete.

Calling helloActivity("Tokyo") returns a Task object signifying the outstanding work. Task objects can also be obtained by APIs on the context.df object, such as context.df.callHttp(). Only Task objects can be yielded. The SDK appends the action(s) of the Task object to a list which it passes back to the Functions runtime, plus whether the function is completed, and any output or errors.

The Azure Functions extension schedules the desired actions. When the actions complete, the extension triggers the orchestrator function to replay up to the next incomplete asynchronous unit of work or its end, whichever comes first.

azure-functions-durable-js's People

Contributors

aaronpowell avatar amclin avatar anthonychu avatar bharathnimmala-msft avatar castrodd avatar cgillum avatar christopheranderson avatar davidmrdavid avatar dependabot[bot] avatar ejizba avatar hossam-nasr avatar kashimiz avatar kendaleiv avatar kush avatar microsoft-github-policy-service[bot] avatar nytian avatar priyaananthasankar avatar sumbad avatar tsuyoshiushio avatar yoichiro avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

azure-functions-durable-js's Issues

Handle event history with out-of-order eventIds.

Discovered by reproducing #36. Durable Task Framework is not guaranteed to present history events with non-negative eventIds (e.g. TaskScheduled, TimerCreated) in order. From this code:

const df = require("durable-functions");

module.exports = df.orchestrator(function* (context) {
  let helloTasks = [
    context.df.callActivity('SayHello', 'A'),
    context.df.callActivity('SayHello', 'B')
  ];
  yield context.df.Task.all(helloTasks);

  let moreHelloTasks = [
    context.df.callActivity('SayHello', 'C'),
    context.df.callActivity('SayHello', 'D')
  ];
  yield context.df.Task.all(moreHelloTasks);

  return 'OK';
});

the following event history is possible (abridged for clarity):

[
  // orchestrator orchestrator started, execution started
  { "EventId": 0, "EventType": "TaskScheduled", "Name": "SayHello" },  // A
  { "EventId": 1, "EventType": "TaskScheduled", "Name": "SayHello" },  // B
  // orchestrator completed, orchestrator started
  { "EventId": -1, "EventType": "TaskCompleted", "Result": ""Hello A!"", "TaskScheduledId": 0 },
  // orchestrator completed, orchestrator started
  { "EventId": -1, "EventType": "TaskCompleted", "Result": ""Hello B!"", "TaskScheduledId": 1 },
  { "EventId": 3, "EventType": "TaskScheduled", "Name": "SayHello" },  // D
  { "EventId": 2, "EventType": "TaskScheduled", "Name": "SayHello" },  // C
  // orchestrator completed, orchestrator started
  { "EventId": -1, "EventType": "TaskCompleted", "Result": ""Hello C!"", "TaskScheduledId": 2 },
  // orchestrator completed, orchestrator started
  { "EventId": -1, "EventType": "TaskCompleted", "Result": ""Hello C!"", "TaskScheduledId": 2 },
]

Tracing shows the shim incorrectly matches the callActivity("SayHello", "D") call to the history event returning ""Hello C!"", likely due to the (incorrect) assumption that the order in which tasks are scheduled === the order of TaskScheduled events in history. This can affect calls to context.df.Task.any.

Orchestrator values are not passed if not objects

If I have a simple string the orchestrator return no input values

client.startNew(orchestrator, undefined, "bla")
const input = context.df.getInput();
// input will be null;

I imagine this can be related with the change to use Axios now.
And in order to make my code work I needed to change the code in start new from

const response = await axios.post(requestUrl, JSON.stringify(input));

to

const response = await axios.post(requestUrl, input);

Sub Orchestrator only running the first sub orchestrator

Hello,

I'm trying to run a few sub orchestrators from my main orchestrator, only my first orchestration works.

Main orchestrator

`const df = require("durable-functions");

module.exports = df.orchestrator(function* (context) {
const input = context.df.getInput();
const data = yield context.df.callActivity("dataStore", input.treeId);
const nodes = data.nodes;
const hubs = nodes.filter((node) => {
return node.nodetype === "hub"
});
const sites = nodes.filter((node) => {
// console.log(nodes);
return node.nodetype === "site"
});
const results1 = yield context.df.callSubOrchestrator("CreateHubSite", { hub: hubs, table: data.table });

const results2 = yield context.df.callSubOrchestrator("CreateSite", { sites: sites, table: data.table });

context.log("results", results1);

});`
First sub orchestrator

`
const df = require("durable-functions");

module.exports = df.orchestrator(function* (context) {
context.log("Starting hub site");
const inputs = context.df.getInput();
const hubArray = [];
inputs.hub.forEach((hub, index) => {
hubArray.push(context.df.callActivity("hubs", { hub: hub, table: inputs.table[0] }));
});
let results = yield context.df.Task.all(hubArray);

return results;

});
`

Anything after results 1 doesn't run, not even the context.logs. I've looked through the examples and i cant see anything wrong ( I have been banging my head against this all day). Im not sure if this is a bug or I'm doing something wrong.

Thanks

Durable HTTP trigger fails to start

Using the default code when deploying a new instance of a functions App I get the following error when trying to start the HTTP trigger function:

2018-12-06T20:18:51.467 [Error] Executed 'Functions.DurableFunctionsHttpStart1' (Failed, Id=af5b1e03-a723-437f-bb3c-b43ef08570cb)
Result: Failure
Exception: Error: [object Object]
Stack: Error: [object Object]
    at DurableOrchestrationClient.<anonymous> (D:\home\site\wwwroot\node_modules\durable-functions\lib\src\durableorchestrationclient.js:176:23)
    at Generator.next (<anonymous>)
    at fulfilled (D:\home\site\wwwroot\node_modules\durable-functions\lib\src\durableorchestrationclient.js:4:58)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)

I have installed version 1.1.2

context.df.setCustomStatus sometimes not working in 1.7.1

Describe the bug
setCustomStatus sometimes doesn't set custom status

Investigative information

  • Durable Functions extension version: 1.7.1
  • Function App version (1.0 or 2.0): 2.0
  • Programming language used: JS
  • durable-functions npm module version: 1.1.4

To Reproduce
I need singleton orchestrator to process external events strictly one by one.
So I use one specific instanceId to start and send events to orchestrator.
Orchestrator receives events with waitForExternalEvent and executes continueAsNew after every processed event.
But because of this #57 bug I can't use 1.8.0 extension version with feature to store external events in memory queue, so to know when I can send external events I use custom status.
I set custom status to "ready" when orchestrator is waiting to external event, and then set custom status to "busy" when orchestrator is processing and can't get new external event.
In my orchestrator clients functions (which are called by Event Grid) I check this custom status and if status is "ready" I send an event to orchestrator and when it is "busy" just store this event to storage table.
When orchestrator starts new invocation it checks storage table for new events, if there are any events in table, orchestrator set status to "busy" and processes all request from table one by one on every new invocation. If there are no events in table, orchestrator sets custom
status to "ready" and waiting for new external event.
This solution is a bit tricky but works.
But sometimes I have a problem when orchestrator can wait to new external event - setting custom status to "ready" not working, so orchestrator stuck in custom status "busy" and orchestration clients can't send any events to it (because of my check).
But it seems that orchestrator is waiting for external event at that moment, just custom status not updated.
So now I must manually change custom status to "ready" in HubInstances table to continue work.

Simplified version of my orchestrator function looks like this:

df.orchestrator(function*(context) {
  // current state
  let state = context.df.getInput();
  // next event to process
  let nextAction = null;

   // loads new event from azure table storage
   nextAction = yield context.df.callActivity('loadAction', state);

    // if there is no events in table storage
    if (!nextEvent) {
      // set custom status to 'ready'
      context.df.setCustomStatus('ready');
      // wait for new external event
      nextAction = yield context.df.waitForExternalEvent('newEvent');
    }
    // got new event from table storage or orchestration client 
    // set custom status to 'busy'
    context.df.setCustomStatus('busy');

   // processing new event...
   state = yield context.df.callActivity('processEvent', {state, nextAction});
   // restart orchestrator with new state
   yield context.df.continueAsNew(state);
   return state;
});

And simplified version of orchestration client function:

async function orchClient(context, instanceId, event) {
  const client = df.getClient(context);
  const status = await client.getStatus(instanceId);
    // If orchestrator is running
     if (status && status.runtimeStatus === "Running") {
         // if orchestrator is waiting for new external event
          if (status.customStatus === 'ready) {
                 // send event directly to orchestrator
                  await client.raiseEvent(instanceId, 'newEvent', event);
                } else {
                // or save event to table storage
                  await saveEventToTableStorage({
                    PartitionKey: instanceId,
                    RowKey: event.id,
                    ...event
                  });
                }
     }
}

I have 2 csv exports from HubHistory tables with different examples and can send it to email.

Also I would like to ask for advice - is there a better way to implement singleton orchestrator to process events strictly one by one? Maybe it would be simpler just to use some external queue or service bus?

getStatus throws exception when instance does not exist

I'm trying to follow the durable-function singleton pattern described here using an unused instance id of '123456789'.

In the trace logging following a request I can see the 404 being generated as expected:

[15/01/2019 18:12:55] Executing HTTP request: {
[15/01/2019 18:12:55]   "requestId": "a18f8494-0da6-4800-8489-e5b6d561ad63",
[15/01/2019 18:12:55]   "method": "GET",
[15/01/2019 18:12:55]   "uri": "/runtime/webhooks/durabletask/instances/123456789"
[15/01/2019 18:12:55] }
[15/01/2019 18:12:55] Executed HTTP request: {
[15/01/2019 18:12:55]   "requestId": "a18f8494-0da6-4800-8489-e5b6d561ad63",
[15/01/2019 18:12:55]   "method": "GET",
[15/01/2019 18:12:55]   "uri": "/runtime/webhooks/durabletask/instances/123456789",
[15/01/2019 18:12:55]   "identities": [
[15/01/2019 18:12:55]     {
[15/01/2019 18:12:55]       "type": "WebJobsAuthLevel",
[15/01/2019 18:12:55]       "level": "Admin"
[15/01/2019 18:12:55]     }
[15/01/2019 18:12:55]   ],
[15/01/2019 18:12:55]   "status": 404,
[15/01/2019 18:12:55]   "duration": 53
[15/01/2019 18:12:55] }
[15/01/2019 18:12:55] [error] Worker 89ff52f0-8c65-4ea2-a98f-4debe8e31278 uncaught exception:  SyntaxError: Unexpected end of JSON input

As you can see following the request there is an exception generated which I suspect is due to the empty JSON response in the 404.

I am running azure functions locally (OS: MacOS, Functions version: 2.3.199)

Singleton orchestrator with re-run-once-at-end

Hi

This is a question, not an issue (could be tagged question).
Is it possible to have a singleton orchestrator which would re-run at end?

Our setup works like this:

  • Singleton orchestrator
  • Triggered by SharePoint upon item change (update/create)
  • Loads items "changed last 30 minutes"
  • Works on these items in actions
  • Every time the trigger is re-called while the orchestrator is running it will wait until it's complete, then run it again for every trigger

That last step should look more like this:

  • Every time the trigger is re-called while orchestrator is running it should flag that a change has happened, and the orchestrator should re-run once when complete

What we want to get rid of is the amount of times the orchestrator is run, for example if our function is triggered 5 times while the orchestrator is running all of these triggers will cause a full run at the end, while we actually only need one single run at the end.
The first trigger should cause a run, and if triggered while running it should run once more to handle the items changed while running.

This could perhaps be solved using a simple queue or blob, with a flag that can be checked at the end, but is it atomic enough?

Here is our HTTP endpoint creating the singleton orchestrator.
I've read that a hard coded instanceId should create a singleton, but this code is the only way which has worked for me:

const df = require("durable-functions");

module.exports = async function (context, req) {
  const client = df.getClient(context);
  const instanceId = 'subscription';

  const status = await client.getStatus(instanceId);
  if (status && status.runtimeStatus === 'Running') {
    const timeoutFourMinutes = 4 * 60 * 1000;
    const intervalTenSeconds = 10 * 1000;
    await client.waitForCompletionOrCreateCheckStatusResponse(
      req, instanceId, timeoutFourMinutes, intervalTenSeconds);

    // Just to be sure, might be superfluous
    await client.terminate(instanceId, 'Timeout');
  }
  await client.startNew('SubscriptionOrchestrator', instanceId, req.query);
  return client.createCheckStatusResponse(context.bindingData.req, instanceId);
};

Azure function apps authentication bug

Describe the bug
When durable function is up in Function apps, using the function apps authentication, i receive a 500 error status. along with:

Exception: Error: You do not have permission to view this directory or page.
Stack: Error: You do not have permission to view this directory or page.
at DurableOrchestrationClient. (D:\home\site\wwwroot\node_modules\durable-functions\lib\src\durableorchestrationclient.js:247:43)
at Generator.next ()
at fulfilled (D:\home\site\wwwroot\node_modules\durable-functions\lib\src\durableorchestrationclient.js:4:58)
at
at process._tickCallback (internal/process/next_tick.js:188:7)

Investigative information

  • Durable Functions extension version: 1.7.1
  • Function App version (1.0 or 2.0): 2.0
  • Programming language used: Javascript

If JavaScript

  • durable-functions npm module version: 1.1.4

If deployed to Azure

  • Timeframe issue observed: 2:51:24 pm gmt
  • Function App name: sitecreatorv2
  • Function name(s): httpRequest
  • Region: North Europe
  • Orchestration instance ID(s): -- didn't get that far

If you don't want to share your Function App name or Functions names on GitHub, please be sure to provide your Invocation ID, Timestamp, and Region - we can use this to look up your Function App/Function. Provide an invocation id per Function. See the Functions Host wiki for more details.

To Reproduce
Steps to reproduce the behavior:

  1. fire http request
  2. view logs to see error

While not required, providing your orchestrator's source code in anonymized form is often very helpful when investigating unexpected orchestrator behavior.

Expected behavior
Should fire off my orchestrators

Actual behavior
Errors out with You do not have permission to view this directory or page.

Screenshots
If applicable, add screenshots to help explain your problem.

Known workarounds
Provide a description of any known workarounds you used.

Additional context

  • Development environment (ex. Visual Studio)
  • Links to source
  • Additional bindings used
  • Function invocation IDs

Unhelpful error message when orchestration not found.

I was following the sample on the docs site but received the following error when hitting the URL for httpStart:

System.Private.CoreLib: Exception while executing function: Functions.HttpStart. System.Private.CoreLib: Result: Failure
Exception: Error: [object Object]
Stack: Error: [object Object]
    at DurableOrchestrationClient.<anonymous> (C:\_Projects\tmp03\node_modules\durable-functions\lib\src\durableorchestrationclient.js:247:43)
    at Generator.next (<anonymous>)
    at fulfilled (C:\_Projects\tmp03\node_modules\durable-functions\lib\src\durableorchestrationclient.js:4:58)
    at process._tickCallback (internal/process/next_tick.js:68:7).

I then cloned the project repo (b1512bf, current dev HEAD) to try the samples and received the same error. I've attached the log file from the function run: logs.txt. Not quite sure where to go next in diagnosis of the problem, but happy to help.

Environment

  • OS: Windows 10
  • node -v: 10.15.2
  • func -v: 2.4.419

Support for async/await in Javascript ochestrator?

Thank you for supporting JavaScript in Durable Functions.
the sample in ochestrator functions is using "yield", is there a plan to support "async/await" style? As you know, requirement of Node.js in Azure Functions V2 is 8.4.0 or higher, I think that it is more friendly to use "async/await".

Function does not return expected value, sometimes (returning old data?)

I'm getting some really odd behaviour with a function. I had the function return some test output earlier in development, and now it's "stuck" returning that output. Sometimes. Seems half the time it returns the expeced output (what's currently in the code), half the time it retuns some old (cached?) output that no longer exists anywhere in my project.

The client/orchestrator is designed to take a function name as an input and run that single function (it's for testing and developing functions in isolation!). It's triggered from a HTTP call eg: http://localhost:7071/api/integration-test/my-function-name and returns the function ID for tracking etc:

test-client

function.json:

{
  "bindings": [
    {
      "authLevel": "anonymous",
      "name": "req",
      "type": "httpTrigger",
      "direction": "in",
      "route": "integration-test/{functionName}",
      "methods": ["post"]
    },
    {
      "name": "$return",
      "type": "http",
      "direction": "out"
    },
    {
      "name": "starter",
      "type": "orchestrationClient",
      "direction": "out"
    }
  ]
}

index.ts

export async function run(context: Context, req: HttpRequest): Promise<any> {
  if (!(req.params && req.params.functionName)) {
    return {
      body: `No specified function name in params`,
      status: HttpStatusCode.BadRequest
    };
  }

  const id = uuid();

  const startArgs = [
    {
      FunctionName: "test-orchestrator",
      Input: {
        testFunction: req.params.functionName,
        testInput: req.body
      },
      InstanceId: id
    }
  ];

  context.bindings.starter = startArgs;

  return {
    body: id,
    status: HttpStatusCode.OK
  };
}

test-orchestrator

The test-orchestrator function takes that input, kicks off the function that it has been passed:

function.json

{
    "disabled": false,
    "bindings": [
      {
        "type": "orchestrationTrigger",
        "name": "context",
        "direction": "in"
      }
    ]
  }

index.ts:

module.exports = orchestrator(function*(context) {
  AzureLoggingContext.getInstance().addContext(context);

  const input = context.df.getInput();

  return yield context.df.callActivity(
    input.testFunction,
    input.testInput
  );
});

my-function-name

Now, the function I'm trying to test has been taken back to something stupidly simple:
function.json

{
  "disabled": false,
  "bindings": [
    {
      "name": "input",
      "type": "activityTrigger",
      "direction": "in"
    }
  ]
}

index.ts

export async function run(context: Context, input: { keyValuePairs: any, mapTemplateConfigurationId: string }): Promise<any> {
  context.log!("Mapping with input " + JSON.stringify(input));
  return { test: "test" };
}

It just retuns { test: "test" }. However, at some point in the past it returned some placeholder data: {"key":"???","kind":"Alert","ou":"???","type":"ENTITY","attributes":[]}.

This is the DurableFunctionsHubInstances from the storage tables:

PartitionKey Timestamp ExecutionId Input Name Output
efa9d520-fe00-11e8-bc5e-35e09dcfee36 2018-12-12T11:28:03.753Z d6c907b0b28c49789c1d0a2990e4d47e {"testFunction":"my-function-name","testInput":{"keyValuePairs":"test","mapTemplateConfigurationId":"test"}} test-orchestrator {"test":"test"}
33052e00-fe01-11e8-bc5e-35e09dcfee36 2018-12-12T11:29:58.673Z abc756ba41fe447b8f83dfff3fb0c07e {"testFunction":"my-function-name","testInput":{"keyValuePairs":"test","mapTemplateConfigurationId":"test"}} test-orchestrator {"test":"test"}
6824e170-fe01-11e8-bc5e-35e09dcfee36 2018-12-12T11:31:12.692Z 9fe6ae3f761445778aab9fd8de64e471 {"testFunction":"my-function-name","testInput":{"keyValuePairs":"test","mapTemplateConfigurationId":"test"}} test-orchestrator {"test":"test"}
87d202f0-fe01-11e8-bc5e-35e09dcfee36 2018-12-12T11:32:09.961Z fd45a3af4d404eceb786fea6796259b0 {"testFunction":"my-function-name","testInput":{"keyValuePairs":"test","mapTemplateConfigurationId":"test"}} test-orchestrator {"key":"???","kind":"Alert","ou":"???","type":"ENTITY","attributes":[]}

Every time I call the function there's a chance it works and returns the current function implementation. But randomly it just returns some old output. A filesearch on my code results in no occurances of this data.

Error handling/propagation.

Unhandled exceptions thrown by activities and sub-orchestrators should be propagated back to the calling orchestrator.

Improve JS orchestration client function experience.

Currently, the recommended way to write an orchestration client "starter" function in JavaScript is to bind OrchestrationClient as an output binding, construct an object matching the structure of OrchestrationStartArgs within the function, and set the OrchestrationClient binding equal to the object.

While this approach successfully starts orchestrations, it has several disadvantages compared to the C# experience, such as:

  • orchestrationIds must be generated by the starter function, not the extension
  • although the instance management endpoints are still available, there is no way to generate the URIs and return them as JSON
  • more

This is less than optimal.

API Implementation Timeline

Have you any timeline on releasing "Not Yet Implemented" APIs from Checklist?
I know that durable functions are only in preview now, but we need to know will we be able to completely switch from c# to js in the near future?

An activity function that returns false is converted to null in orchestrator

Hi,

I'm using v1.1.4 of durable-functions in javascript.

I have an activty function that returns whether a condition is met, let's say it's :

module.exports = async function (context) {
    return false;
};

When my orchestrator calls this activity funciton, it receives null instead of false :

module.exports = df.orchestrator(function* (context) {
    const myState = yield context.df.callActivity('myActivity');
    return `My state is ${myState}`;
});

If my activity function returns true instead of false, the result is correct, indeed true.
Am I missing something here or it's a bug ?

Thanks

`context.df.isReplaying` not updating between activity calls

According to e.g. https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-diagnostics#javascript-functions-2x-only-1 and this video isReplaying should be a 'live' property that updates before and after each yield to indicate whether that piece of code has been visited before.

This does not seem to be the case.

E.g. with the following orchestrator:

const df = require("durable-functions");

module.exports = df.orchestrator(function*(context) {
    if (!context.df.isReplaying) { context.log(`Calling 1`); }
    yield context.df.callActivity("TestActivity", 1);

    if (!context.df.isReplaying) { context.log(`Calling 2`); }
    yield context.df.callActivity("TestActivity", 2);

    if (!context.df.isReplaying) { context.log(`Calling 3`); }
    yield context.df.callActivity("TestActivity", 3);

    if (!context.df.isReplaying) { context.log(`Conditional log`); }
    context.log("Unconditional log");
});

I get:

[8-2-2019 14:02:41] Executing HTTP request: {
[8-2-2019 14:02:41]   "requestId": "262d31ee-ca4b-41f9-9338-1ffd9410dd74",
[8-2-2019 14:02:41]   "method": "POST",
[8-2-2019 14:02:41]   "uri": "/api/orchestrators/TestOrchestrator"
[8-2-2019 14:02:41] }
[8-2-2019 14:02:41] Executing 'Functions.HttpStart' (Reason='This function was programmatically called via the host APIs.', Id=fc8069de-128e-408b-8095-b9ea313bb235)
[8-2-2019 14:02:41] Executing HTTP request: {
[8-2-2019 14:02:41]   "requestId": "290b3408-15b0-4d95-af30-6bb734f467a2",
[8-2-2019 14:02:41]   "method": "POST",
[8-2-2019 14:02:41]   "uri": "/runtime/webhooks/durabletask/orchestrators/TestOrchestrator"
[8-2-2019 14:02:41] }
[8-2-2019 14:02:42] 9287fec086514b6d996df2b48dfd368c: Function 'TestOrchestrator (Orchestrator)' scheduled. Reason: NewInstance. IsReplay: False. State: Scheduled. HubName: DurableFunctionsHub. AppName: . SlotName: . ExtensionVersion: 1.7.0. SequenceNumber: 2.
[8-2-2019 14:02:42] Executed HTTP request: {
[8-2-2019 14:02:42]   "requestId": "290b3408-15b0-4d95-af30-6bb734f467a2",
[8-2-2019 14:02:42]   "method": "POST",
[8-2-2019 14:02:42]   "uri": "/runtime/webhooks/durabletask/orchestrators/TestOrchestrator",
[8-2-2019 14:02:42]   "identities": [
[8-2-2019 14:02:42]     {
[8-2-2019 14:02:42]       "type": "WebJobsAuthLevel",
[8-2-2019 14:02:42]       "level": "Admin"
[8-2-2019 14:02:42]     }
[8-2-2019 14:02:42] Started orchestration with ID = '9287fec086514b6d996df2b48dfd368c'.
[8-2-2019 14:02:42]   ],
[8-2-2019 14:02:42]   "status": 202,
[8-2-2019 14:02:42]   "duration": 521
[8-2-2019 14:02:42] }
[8-2-2019 14:02:42] Executed 'Functions.HttpStart' (Succeeded, Id=fc8069de-128e-408b-8095-b9ea313bb235)
[8-2-2019 14:02:42] Executed HTTP request: {
[8-2-2019 14:02:42]   "requestId": "262d31ee-ca4b-41f9-9338-1ffd9410dd74",
[8-2-2019 14:02:42]   "method": "POST",
[8-2-2019 14:02:42]   "uri": "/api/orchestrators/TestOrchestrator",
[8-2-2019 14:02:42]   "identities": [
[8-2-2019 14:02:42]     {
[8-2-2019 14:02:42]       "type": "WebJobsAuthLevel",
[8-2-2019 14:02:42]       "level": "Admin"
[8-2-2019 14:02:42]     }
[8-2-2019 14:02:42]   ],
[8-2-2019 14:02:42]   "status": 202,
[8-2-2019 14:02:42]   "duration": 868
[8-2-2019 14:02:42] }
[8-2-2019 14:02:42] Host lock lease acquired by instance ID '000000000000000000000000A2BDC8CD'.
[8-2-2019 14:02:58] Executing 'Functions.TestOrchestrator' (Reason='', Id=b2efbbe3-3160-4f06-b1c5-334ff8b9822b)
[8-2-2019 14:02:58] 9287fec086514b6d996df2b48dfd368c: Function 'TestOrchestrator (Orchestrator)' started. IsReplay: False. Input: (16 bytes). State: Started. HubName: DurableFunctionsHub. AppName: . SlotName: . ExtensionVersion: 1.7.0. SequenceNumber: 3.
[8-2-2019 14:02:58] Calling 1
[8-2-2019 14:02:58] 9287fec086514b6d996df2b48dfd368c: Function 'TestActivity (Activity)' scheduled. Reason: TestOrchestrator. IsReplay: False. State: Scheduled. HubName: DurableFunctionsHub. AppName: . SlotName: . ExtensionVersion: 1.7.0. SequenceNumber: 4.
[8-2-2019 14:02:58] Executed 'Functions.TestOrchestrator' (Succeeded, Id=b2efbbe3-3160-4f06-b1c5-334ff8b9822b)
[8-2-2019 14:02:58] 9287fec086514b6d996df2b48dfd368c: Function 'TestActivity (Activity)' started. IsReplay: False. Input: (12 bytes). State: Started. HubName: DurableFunctionsHub. AppName: . SlotName: . ExtensionVersion: 1.7.0. SequenceNumber: 5.
[8-2-2019 14:02:58] Executing 'Functions.TestActivity' (Reason='', Id=984dba6b-b196-4dc2-801a-901cab3da3bc)
[8-2-2019 14:02:58] Executed 'Functions.TestActivity' (Succeeded, Id=984dba6b-b196-4dc2-801a-901cab3da3bc)
[8-2-2019 14:02:58] 9287fec086514b6d996df2b48dfd368c: Function 'TestActivity (Activity)' completed. ContinuedAsNew: False. IsReplay: False. Output: (4 bytes). State: Completed. HubName: DurableFunctionsHub. AppName: . SlotName: .
ExtensionVersion: 1.7.0. SequenceNumber: 6.
[8-2-2019 14:02:58] Executing 'Functions.TestOrchestrator' (Reason='', Id=87ba9990-0b31-4566-82e4-cc06927d1d69)
[8-2-2019 14:02:58] 9287fec086514b6d996df2b48dfd368c: Function 'TestActivity (Activity)' scheduled. Reason: TestOrchestrator. IsReplay: False. State: Scheduled. HubName: DurableFunctionsHub. AppName: . SlotName: . ExtensionVersion: 1.7.0. SequenceNumber: 7.
[8-2-2019 14:02:58] Executed 'Functions.TestOrchestrator' (Succeeded, Id=87ba9990-0b31-4566-82e4-cc06927d1d69)
[8-2-2019 14:02:58] 9287fec086514b6d996df2b48dfd368c: Function 'TestActivity (Activity)' started. IsReplay: False. Input: (12 bytes). State: Started. HubName: DurableFunctionsHub. AppName: . SlotName: . ExtensionVersion: 1.7.0. SequenceNumber: 8.
[8-2-2019 14:02:58] Executing 'Functions.TestActivity' (Reason='', Id=5787431a-0daa-44eb-8fdd-1e326e6ee3d6)
[8-2-2019 14:02:58] Executed 'Functions.TestActivity' (Succeeded, Id=5787431a-0daa-44eb-8fdd-1e326e6ee3d6)
[8-2-2019 14:02:58] 9287fec086514b6d996df2b48dfd368c: Function 'TestActivity (Activity)' completed. ContinuedAsNew: False. IsReplay: False. Output: (4 bytes). State: Completed. HubName: DurableFunctionsHub. AppName: . SlotName: .
ExtensionVersion: 1.7.0. SequenceNumber: 9.
[8-2-2019 14:02:58] Executing 'Functions.TestOrchestrator' (Reason='', Id=57e14dfa-ad64-4059-868d-c189900cc2ae)
[8-2-2019 14:02:58] 9287fec086514b6d996df2b48dfd368c: Function 'TestActivity (Activity)' scheduled. Reason: TestOrchestrator. IsReplay: False. State: Scheduled. HubName: DurableFunctionsHub. AppName: . SlotName: . ExtensionVersion: 1.7.0. SequenceNumber: 10.
[8-2-2019 14:02:58] Executed 'Functions.TestOrchestrator' (Succeeded, Id=57e14dfa-ad64-4059-868d-c189900cc2ae)
[8-2-2019 14:02:58] 9287fec086514b6d996df2b48dfd368c: Function 'TestActivity (Activity)' started. IsReplay: False. Input: (12 bytes). State: Started. HubName: DurableFunctionsHub. AppName: . SlotName: . ExtensionVersion: 1.7.0. SequenceNumber: 11.
[8-2-2019 14:02:58] Executing 'Functions.TestActivity' (Reason='', Id=d060ffad-5161-4552-900b-d95cfce72005)
[8-2-2019 14:02:58] Executed 'Functions.TestActivity' (Succeeded, Id=d060ffad-5161-4552-900b-d95cfce72005)
[8-2-2019 14:02:58] 9287fec086514b6d996df2b48dfd368c: Function 'TestActivity (Activity)' completed. ContinuedAsNew: False. IsReplay: False. Output: (4 bytes). State: Completed. HubName: DurableFunctionsHub. AppName: . SlotName: .
ExtensionVersion: 1.7.0. SequenceNumber: 12.
[8-2-2019 14:02:58] Executing 'Functions.TestOrchestrator' (Reason='', Id=0d8e5c1d-1308-4d61-8a08-51faeef50404)
[8-2-2019 14:02:58] Unconditional log
[8-2-2019 14:02:58] Executed 'Functions.TestOrchestrator' (Succeeded, Id=0d8e5c1d-1308-4d61-8a08-51faeef50404)

Note that there's only "Calling 1" and "Unconditional log", I would have expected each log line to be present exactly once.

How to implement a basic unit test in javascript for an azure durable function orchestration

What would be the unit test which would fake the call to callActivity in the orchestrator below to return a known value and to expect that the orchestrator returns that value.

The examples on the azure durable functions documentation for unit testing are all written in C# and I've not been able to replicate them in javascript despite several attempts. This is because I don't know how to construct an orchestrator with a fake context.

const df = require('durable-functions');

module.exports = df.orchestrator(function* orchestratorFunctionGenerator(context) {
  context.log('Starting GetIndexNamesOrchestrator');
  const apimApiName = context.df.getInput();

  context.log({ apimApiName });

  const indexNames = yield context.df.callActivity('GetIndexNames', apimApiName);

  context.log({ indexNames });

  return indexNames;
});

This is replicated from this SO question

DurableOrchestrationContext.InstanceId

Support the InstanceId property of DurableOrchestrationContext. This will require work on Azure/azure-functions-durable-extension to pass the instanceId through via the context object.

Support sub-orchestrations.

Includes translations of the following methods:

  • CallSubOrchestratorAsync(String, Object)
  • CallSubOrchestratorAsync(String, String, Object)
  • CallSubOrchestratorWithRetryAsync(String, RetryOptions, Object)
  • CallSubOrchestratorWithRetryAsync(String, RetryOptions, String, Object)

Sub orchestrator output returns null

When returning values from sub orchestrators the output is always null.

Versions:
durable-functions: 1.1.4
func: 2.4.498
Extension: 1.8.0

Orchestrator1

const df = require("durable-functions");

module.exports = df.orchestrator(function* (context) {
  return yield context.df.callSubOrchestrator('Orchestrator2');
});

Orchestrator2

const df = require("durable-functions");

module.exports = df.orchestrator(function* (context) {
  return yield context.df.callActivity('Hello', 'World');
});

Reply from statusQueryGetUri URL:

{"instanceId":"fe3a5fec8c334b57808d0c7aab9c60b3",
"runtimeStatus":"Running","input":null,"customStatus":null,"output":null,
"createdTime":"2019-03-26T07:55:45Z","lastUpdatedTime":"2019-03-26T07:55:47Z"}

Reply from func durable get-instances

[
  {
    "CompletedTime": "0001-01-01T00:00:00",
    "CompressedSize": 0,
    "CreatedTime": "2019-03-26T07:55:46.2895672Z",
    "Input": "null",
    "LastUpdatedTime": "2019-03-26T07:55:47.0216018Z",
    "Name": "Orchestrator2",
    "OrchestrationInstance": {
      "InstanceId": "dd1dbffcab474b4eb89a91f6dc3a3e5d:0",
      "ExecutionId": "6c28f54ea19e4c5987ea0acdff845cdc"
    },
    "OrchestrationStatus": 1,
    "Output": "\"Hello World!\"",
    "ParentInstance": null,
    "Size": 0,
    "Status": null,
    "Tags": null,
    "Version": ""
  },
  {
    "CompletedTime": "0001-01-01T00:00:00",
    "CompressedSize": 0,
    "CreatedTime": "2019-03-26T07:55:45.4105473Z",
    "Input": "null",
    "LastUpdatedTime": "2019-03-26T07:55:47.2439506Z",
    "Name": "Orchestrator1",
    "OrchestrationInstance": {
      "InstanceId": "fe3a5fec8c334b57808d0c7aab9c60b3",
      "ExecutionId": "dd1dbffcab474b4eb89a91f6dc3a3e5d"
    },
    "OrchestrationStatus": 0,
    "Output": null,
    "ParentInstance": null,
    "Size": 0,
    "Status": null,
    "Tags": null,
    "Version": ""
  }
]
Continuation token for next set of results: 'bnVsbA=='

WaitForAnyTask is not compatible with TimerTasks?

Describe the bug
This does not appear to be documented but as far as I can tell, waitForAnyTask is not meant to be used with TimerTasks

Investigative information

  • Durable Functions extension version: 1.7.1
  • Function App version (1.0 or 2.0): 2.0
  • Programming language used: Javascript

If JavaScript

  • durable-functions npm module version: 1.1.4

To Reproduce
Code like the following:

yield context.df.waitForAnyTask([timerTask, waitForExternalEventTask]);
if(winner === waitForExternalEventTask)){
   yield context.df.waitForExternalEvent("some other event");
}

seem to cause non-deterministic orchestration behavior WHEN

  1. In the first/original run, the external event of waitForExternalEventTask fires first. (This results in a second wait for a different event in the if clause.
  2. The timer task then subsequently completes (but does not trigger the orchestration to run since it is waiting for an event
  3. The external event in the if clause fires, and the orchestration "goes into" replay
  4. In replay, the timerTask ends up winning the waitForAnyTask race, and so the replay never goes into the if clause

net result: non-deterministic workflow

May I confirm if waitForAnyTasks is not intended to work with Timers? Is there anyway to wait for a timer and at the same time, wait for an event?

Upgrade Azure durable functions from 1.6.2 to 1.7.0

I have written some durable functions using version 1.6.2 . The new 1.7.0 is now out. I want to upgrade.

Will just doing func extensions install -p Microsoft.Azure.WebJobs.Extensions.DurableTask -v 1.7.0 do the job? or do I need to do something else?

[JS] Getting orchestrator output exception when orchestrator returns undefined

All my orchestrations (which return true or false) have the following output entry in the "Instances" Azure Storage table.

Orchestrator function 'orchestrator' failed: TypeError: Cannot convert undefined or null to object    
	at hasOwnProperty (<anonymous>)    at Orchestrator.shouldFinish (C:\git\test\node_modules\durable-functions\lib\src\orchestrator.js:380:48)    
	at Orchestrator.<anonymous> (C:\git\test\node_modules\durable-functions\lib\src\orchestrator.js:64:30)    at Generator.next (<anonymous>)    
	at C:\git\test\node_modules\durable-functions\lib\src\orchestrator.js:7:71    
	at new Promise (<anonymous>)    
	at __awaiter (C:\git\test\node_modules\durable-functions\lib\src\orchestrator.js:3:12)    
	at Orchestrator.handle (C:\git\test\node_modules\durable-functions\lib\src\orchestrator.js:22:16)    
	at C:\git\test\node_modules\durable-functions\lib\src\shim.js:8:9    
	at WorkerChannel.invocationRequest (C:\Users\clkoh\AppData\Roaming\nvm\v8.11.3\node_modules\azure-functions-core-tools\bin\workers\node\worker-bundle.js:28866:26)

This corresponds to

return Object.prototype.hasOwnProperty.call(result, "isCompleted") && !(result as Task).isCompleted

This is a little baffling but does not actually block anything since I am not dependent on the output at the moment :)

custom Orchestration status returns null.

Documentation found here: https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-custom-orchestration-status

Is incorrect.
Running the OrchestratorFunction I am getting a null error :

System.Private.CoreLib: Exception while executing function: Functions.HttpStart. System.Private.CoreLib: Result: Failure
Exception: TypeError: Cannot read property 'toString' of null
Stack: TypeError: Cannot read property 'toString' of null
    at module.exports (C:\Users\scott.kelly\Documents\git\durable-function-apps\HttpStart\index.js:13:52)
    at process._tickCallback (internal/process/next_tick.js:68:7).

I imagine this is because customStatus is not set initially in the Orchestrator and so has a null status.

Should also be noted in the listed example above for Javascript, the lines:

let durableOrchestrationStatus = await client.getStatus(instanceId);
    while (status.customStatus.toString() !== "London") {

are inconsistent. The example documentation is running a while loop on "status", which is never declared nor defined? It seems that the while loop should be running on "durableOrchestrationStatus.customStatus"..

I have attempted to fix null issue by running a while on the value and breaking when it is not null, however this sets off an infinite loop..
I have also attempted to set customStatus on orchestrator initialisation, but nothing updates.

Will keep attempting fixes, but clarification would be helpful.

Activity function does not execute after caught error.

Initially reported in MicrosoftDocs/azure-docs#19097: When an error thrown by an activity function is propagated back to the orchestrator and caught, subsequent callActivity requests fail to execute; instead, the function completes. For example, in the following orchestration, if ThrowsError does what it says on the tin, the orchestrator will catch said error, but Cleanup never executes.

Fixing this is a requirement for #18

const df = require("durable-functions");

module.exports = df.orchestrator(function*(context){
    const input = context.df.getInput();

    try {
        const result1 = yield context.df.callActivity("ThrowsError");
    } catch (e) {
        const result2 = yield context.df.callActivity("Cleanup");
    }

    return input;
});

Details
durable-functions 1.0.0

Javascript Activity Function Timeout never fails

I wanted to confirm this behaviour. I have an activity function running a on a consumption plan that timesout after 10mins. However, the javascript orchestrator still remains in a 'running' state. I tried using the timers to complete the orchestration, but i guess its still waiting for all tasks to complete?

Can anyone confirm this behaviour and if so Is there a work around? Is there a way to perhaps cancel an activity function?

Question: Getting value from context.bindings.starter

Hi,
I might have misunderstood how durable functions work.
But I have made:

  1. An HttPTrigger, that starts an Orchestration. with
context.bindings.starter = [{
            FunctionName: "OTestarDur1",
            Input: siteName,
        }];
  1. The orchestration calls an activity and gets all the lists in SharePoint.
const lists = yield context.df.callActivity("ATestarDur1", site);
return lists;

All of this works. But is it possible back in the HttpTrigger to get the value of the lists I returned?
From the context.bindings.starter maybe?

/Simon

JS context.df.continueAsNew not working in 1.8.0

Describe the bug
contunueAsNew stopped working after updating to 1.8.0
in 1.7.1 it works fine

Investigative information

  • Durable Functions extension version: 1.8.0
  • Function App version (1.0 or 2.0): 2.0
  • Programming language used: JS
  • durable-functions npm module version: 1.1.4

To Reproduce
https://github.com/deman4ik/funcs-durable-js

  • func extensions install
  • npm install
  • func start

Steps to reproduce the behavior:

  1. POST http://localhost:7071/api/HttpTrigger with any not null data in body
  2. Check HubHistory

Expected behavior
Orchestrator started with random not null input
Checks Input
continueAsNew with new state (false)
Orchestrator started with "false" input
Checks Input
Orchestrator finished with result "false"

Actual behavior
Orchestrator started with random not null input
Checks Input
Orchestrator finished with result from input

Return object from async activity function

In an activity function we return something by doing

context.done(null, object);

to the durable function where object is my target object to sent.

but when i make my activity function async, i can not use context.done

without context.done how can i send my desired object from activity function to the orchestration?

OrchestratorClient fails when waiting for completion

This does not happen all the time but occasionally the orchestration client fails when waiting for completion.

Error

Orchestrator function 'createOrchestrator' failed: TypeError: Cannot read property 'Timestamp' of undefined
    at Orchestrator.<anonymous> (D:\home\site\wwwroot\node_modules\durable-functions\lib\src\orchestrator.js:88:74)
    at Generator.next (<anonymous>)
    at D:\home\site\wwwroot\node_modules\durable-functions\lib\src\orchestrator.js:7:71
    at new Promise (<anonymous>)
    at __awaiter (D:\home\site\wwwroot\node_modules\durable-functions\lib\src\orchestrator.js:3:12)
    at Orchestrator.handle (D:\home\site\wwwroot\node_modules\durable-functions\lib\src\orchestrator.js:22:16)
    at D:\home\site\wwwroot\node_modules\durable-functions\lib\src\shim.js:8:9
    at WorkerChannel.invocationRequest (D:\Program Files (x86)\SiteExtensions\Functions\2.0.12342\32bit\workers\node\worker-bundle.js:16077:26)
    at ClientDuplexStream.WorkerChannel.eventStream.on (D:\Program Files (x86)\SiteExtensions\Functions\2.0.12342\32bit\workers\node\worker-bundle.js:15967:30)
    at ClientDuplexStream.emit (events.js:182:13)

This issue seems to happen here:

context.df.currentUtcDateTime = decisionStartedEvent.Timestamp;

I resolved this by adding a check on decisionStartedEvent. Similar to whats being done here:

currentUtcDateTime: decisionStartedEvent
? decisionStartedEvent.Timestamp
: undefined,

Unable to cast object of type 'Microsoft.Azure.WebJobs.DurableOrchestrationContext' to type 'System.String' error

Describe the bug
When following the quickstart tutorial (https://docs.microsoft.com/fr-fr/azure/azure-functions/durable/quickstart-js-vscode), and published the project to Azure, got the following error whe running on Azure portal:

2019-03-25T08:52:02 Welcome, you are now connected to log-streaming service.
2019-03-25T08:52:04.165 [Information] Executing 'Functions.OrchestratorFunction' (Reason='This function was programmatically called via the host APIs.', Id=c1e04160-e3bb-4dc2-8774-f41fe6f589be)
2019-03-25T08:52:04.433 [Error] Executed 'Functions.OrchestratorFunction' (Failed, Id=c1e04160-e3bb-4dc2-8774-f41fe6f589be)
Unable to cast object of type 'System.String' to type 'Microsoft.Azure.WebJobs.DurableOrchestrationContext'.

Investigative information

  • Durable Functions extension version: 1.8.0
  • Function App version (1.0 or 2.0): 2.0
  • Programming language used: JavaScript

If JavaScript

  • durable-functions npm module version: 1.1.4

If deployed to Azure

  • Timeframe issue observed:
  • Function App name: ADF2
  • Function name(s): E1_SayHello, HttpStart, OrchestratorFunction
  • Region: West Europe

To Reproduce
Steps to reproduce the behavior: Quickstart javascript tutorial (https://docs.microsoft.com/fr-fr/azure/azure-functions/durable/quickstart-js-vscode )

Known workarounds
Set version of "Microsoft.Azure.WebJobs.Extensions.DurableTask" to 1.8.0 in extensions.csproj

Multiple calls to context.df.Task.all

When I call context.df.Task.all multiple times in same orchestrator the last call will never end.
The function will never complete.

Example:

const df = require("durable-functions");

module.exports = df.orchestrator(function* (context) {
  let helloTasks = [
    context.df.callActivity('SayHello'),
    context.df.callActivity('SayHello')
  ];
  yield context.df.Task.all(helloTasks);

  let moreHelloTasks = [
    context.df.callActivity('SayHello'),
    context.df.callActivity('SayHello')
  ];
  yield context.df.Task.all(moreHelloTasks);

  return 'OK';
});

If one of the arrays has one element it will complete.

startNew throws EPROTO error locally

When running locally, making calls to DurableOrchestrationClient.startNew to start an orchestrator function results in an EPROTO error being thrown and the orchestrator not starting.

This is due to Azure/azure-functions-host#2024, which causes the DurableOrchestrationClient class to use the wrong webhook URLs. The workaround is to set local environment variable WEBSITE_HOSTNAME to localhost:<port>, ex. localhost:7071.

Errored Orchestration function is not reported as Failed in monitor/app insights

Just calling this simple Orchestration function that always throws an Error, I'd expect it to be reported as failed in Monitor or App Insights. But that's not the case -

const df = require("durable-functions");

module.exports = df.orchestrator(function*(context){
    const input = context.df.getInput();

    throw Error("Failed purposefully");
});

I trigger this via a Queue bound Client

const df = require("durable-functions");

module.exports = async function (context, myQueueItem) {
    const client = df.getClient(context);
    const instanceId = await client.startNew("FailPurposefully", undefined, myQueueItem);
    context.log(`Started orchestration with ID = '${instanceId}'.`);
};

When I check the execution in Monitor, both are reported as success=True. Here is the output of running it locally. Both reported as Succeeded

Executed 'Functions.QueueTrigger' (Succeeded, Id=a4ba77d1-c789-4474-9ec4-86b40e1dd909)
Executing 'Functions.FailPurposefully' (Reason='', Id=484bfb15-c17f-422a-ac4f-b9bd391b74b6)
8d269cb140ff46fe977fd6158fe2a0e4: Function 'FailPurposefully (Orchestrator)' started. IsReplay: False. Input: (196 bytes). State: Started. HubName: DurableFunctionsHub. AppName: . SlotName: . ExtensionVersion: 1.7.1. SequenceNumber: 3.
Executed 'Functions.FailPurposefully' (Succeeded, Id=484bfb15-c17f-422a-ac4f-b9bd391b74b6)

Just to make sure that I am not missing anything, I checked the source of orchestrator.ts and looks like the orchestrator always passes null as error, essentially swallowing it.

How do we get the Durable functions flagged when Errored?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.