mikhailshilkov / durablefunctions.fsharp Goto Github PK
View Code? Open in Web Editor NEWF#-friendly API layer for Azure Durable Functions
License: MIT License
F#-friendly API layer for Azure Durable Functions
License: MIT License
I was attempting to write some unit tests along the lines of this document
I ran into a problem as its not possible to mock the sealed DurableOrchestrationContext and if I create a mock DurableOrchestrationContextBase its not possible to cast to the concrete type in order to pass to the orchestration workflow
Any objection to me creating a pr to make the change?
Activity.All
runs all activities in parallel and returns when they are done. But sometimes there is no need to wait. For example a scheme that generate a collection of Webhook calls backs. It wants to start all of them and immediately return. It assumes the individual Activity functions will make best attempt to deliver but there is no need to return a result.
Activity.StartAll
?
I am using Durable functions for "fire and forget" callback processing. The DurableFuction is invoked with a collection of callbacks to make. It initiates each one and fails if the response is not 200 OK
. A retry policy is used to ensure the failed attempt is retried n times.
This works, but the only way to indicate a failure is by raising an exception in the activity function. But this is then being logged in our azure app-insights. I think what is happening is that we should be catching the exception in the orchestration. Since we are not I think it is terminating the orchestration and logging an exception to AppInsights.
Mark Heath seems to indicate something like the below should be done, but without re-throwing the exception.
I don't know how this could be done, perhaps an parameter to .All like this Exception -> Unit. All would then catch any exception and pass it to the function to handle and possibly rethrow?
https://markheath.net/post/error-handling-durable-functions
[FunctionName("ExceptionHandlingOrchestrator")]
public static async Task<string> ExceptionHandlingOrchestrator(
[OrchestrationTrigger] DurableOrchestrationContext ctx,
TraceWriter log)
{
var inputData = ctx.GetInput<string>();
try
{
var a1 = await ctx.CallActivityAsync<string>("Activity1", inputData);
var a2 = await ctx.CallActivityAsync<ActivityResult>("Activity2", a1);
var a3 = await ctx.CallActivityAsync<string>("Activity3", a2);
return a3;
}
catch (Exception)
{
await ctx.CallActivityAsync<string>("CleanupActivity", inputData);
// optionally rethrow the exception to fail the orchestration
throw;
}
}
I've not been able to get DurableFunctions.FSharp to work correctly whenusing Paket.
I created two near identical Solutions, one using Paket and one using straight Nuget. These are based on your samples stripped down to just Hello. The Paket one fails on startup with:
[4/12/2019 9:12:57 PM] Microsoft.Azure.WebJobs.Host: Error indexing method 'HttpStart'. Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'starter' to type DurableOrchestrationClient. Make sure the parameter Type is supported by the binding. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).
The complete trace of the the startup error followed by the runtime error when the functin is invoked is below. I've attached a zip of a repository with the two solutions included.
Any ideas?
[4/12/2019 9:12:56 PM] Generating 1 job function(s)
[4/12/2019 9:12:57 PM] Error indexing method 'HttpStart'
[4/12/2019 9:12:57 PM] Microsoft.Azure.WebJobs.Host: Error indexing method 'HttpStart'. Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'starter' to type DurableOrchestrationClient. Make sure the parameter Type is supported by the binding. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).
[4/12/2019 9:12:57 PM] Function 'HttpStart' failed indexing and will be disabled.
[4/12/2019 9:12:57 PM] No job functions found. Try making your job classes and methods public. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).
[4/12/2019 9:12:57 PM] Host initialized (1520ms)
[4/12/2019 9:12:57 PM] Host started (1536ms)
[4/12/2019 9:12:57 PM] Job host started
[4/12/2019 9:12:57 PM] The following 2 functions are in error:
[4/12/2019 9:12:57 PM] HelloSequence: The binding type(s) 'orchestrationTrigger' are not registered. Please ensure the type is correct and the binding extension is installed.
[4/12/2019 9:12:57 PM] SayHello: The binding type(s) 'activityTrigger' are not registered. Please ensure the type is correct and the binding extension is installed.
[4/12/2019 9:12:57 PM]
Hosting environment: Production
Content root path: C:\Users\jbeeko\Desktop\samples\samples-paket\bin\Debug\netcoreapp2.2
Now listening on: http://0.0.0.0:7071
Application started. Press Ctrl+C to shut down.
Http Functions:
HttpStart: [GET] http://localhost:7071/api/orchestrators/{functionName}
[4/12/2019 9:13:02 PM] Host lock lease acquired by instance ID '00000000000000000000000022492EBD'.
[4/12/2019 9:13:11 PM] Executing HTTP request: {
[4/12/2019 9:13:11 PM] "requestId": "7fefab8d-90c9-43de-8c27-18f3aaf031be",
[4/12/2019 9:13:11 PM] "method": "GET",
[4/12/2019 9:13:11 PM] "uri": "/api/orchestrators/HelloSequence"
[4/12/2019 9:13:11 PM] }
[4/12/2019 9:13:12 PM] An unhandled host error has occurred.
[4/12/2019 9:13:12 PM] Microsoft.Azure.WebJobs.Host: 'HttpStart' can't be invoked from Azure WebJobs SDK. Is it missing Azure WebJobs SDK attributes?.
[4/12/2019 9:13:12 PM] Executed HTTP request: {
[4/12/2019 9:13:12 PM] "requestId": "7fefab8d-90c9-43de-8c27-18f3aaf031be",
[4/12/2019 9:13:12 PM] "method": "GET",
[4/12/2019 9:13:13 PM] "uri": "/api/orchestrators/HelloSequence",
[4/12/2019 9:13:13 PM] "identities": [
[4/12/2019 9:13:13 PM] {
[4/12/2019 9:13:13 PM] "type": "WebJobsAuthLevel",
[4/12/2019 9:13:13 PM] "level": "Admin"
[4/12/2019 9:13:13 PM] }
[4/12/2019 9:13:13 PM] ],
[4/12/2019 9:13:13 PM] "status": 500,
[4/12/2019 9:13:13 PM] "duration": 1098
[4/12/2019 9:13:13 PM] }
This Orchestrator
When run with HttpStart
will create an endpoint that can take a query parameter and pass it through to the activity function. For example:
GET http://localhost:7071/api/orchestrators/WorkflowWithInputParameter?input=foo
Will give the following result on the status query url.
{
"instanceId": "a1256fcc2c0048cca2328fcad8d6f088",
"runtimeStatus": "Completed",
"input": "foo",
"customStatus": null,
"output": [
"Hello typed foo Tokyo!",
"Hello typed foo Seattle!",
"Hello typed foo London!"
],
"createdTime": "2019-03-21T21:17:57Z",
"lastUpdatedTime": "2019-03-21T21:17:59Z"
}
Note how the parameter 'foo' has been passed through to the Activity function and added into the Hello message.
How does one pass the parameter as part of a post body? None of the following seem to work:
POST http://localhost:7071/api/orchestrators/WorkflowWithInputParameter
foo
input=foo
input%3Dfoo
{"input":"foo"}
["foo"]
In all cases the input passed through to the activity is null. Also the input in the status query is null.
{
"instanceId": "1265f2937e384895ab0cd5c6753bfaa3",
"runtimeStatus": "Completed",
"input": null,
"customStatus": null,
"output": [
"Hello typed Tokyo!",
"Hello typed Seattle!",
"Hello typed London!"
],
"createdTime": "2019-03-21T21:26:31Z",
"lastUpdatedTime": "2019-03-21T21:26:32Z"
}
Is it possible to implement eternal orchestrations?
I can't see a way I could call the equivalent of context.ContinueAsNew(null)
With this or even access the DurableOrchestrationContext ?
It seems to be working If I do this in the Orchestration function
Orchestrator.run (revocationWorkflow, context) |> ignore
context.ContinueAsNew(null)
but I'm not sure if thats the safe/best way?
Thanks
Should it be possible to have multiple Activity.All declared in a single orchestration?
I modified one of the samples (FanOutFanIn)
let workflow = orchestrator {
let! items =
["Tokyo"; "Seattle"; "London"]
|> List.map (Activity.call hardWork)
|> Activity.all
let! items2 =
["Tokyo"; "Seattle"; "London"]
|> List.map (Activity.call hardWork)
|> Activity.all
// returns "Worked hard on Tokyo!, Worked hard on Seattle!, Worked hard on London!"
return String.concat ", " items
}
and the orchestration status was stuck in "Running" - as a follow up should be able to make additional
Activity.call after a Activity.all completes?
When I try that I get the exception "Multi threading detected" although I can't immediately work out whats causing that (I'm not awaiting any tasks, or performing IO)
Should I be placing that "Activity.all" in a sub orchestration do you think?
Hi, I'm very new to durable functions and I'm still learning how it all works, so I may be making the wrong observations here.
I followed through the steps in the README, but when running the example, it says that only the TypedSequence.Run
function is loaded. I believe though that there is an error here and that the following line:
let SayHello([<ActivityTrigger>] name) = Activity.run sayHello
should instead be
let SayHello([<ActivityTrigger>] name) = sayHello.run name
Changing this makes the SayHello
function load.
However, even after making this change, the hello world application doesn't actually run anything. To get it to work, I had to add the HttpStart
function from HttpStart.fs.
I think the README should be updated to have the above fixes (assuming they are correct and not me misunderstanding things), and it should also include the example URLs to call to verify it works.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.