@pacodelacruz
Paco de la Cruz
Durable Functions 2.0
Stateful Serverless Functions
IntegrationDownUnder
@pacodelacruz
@pacodelacruz
@pacodelacruz
linkedin.com/in/pacodelacruz
pacodelacruz.io
slideshare.net/pacodelac/presentations
github.com/pacodelacruz
@pacodelacruz
Serverless and Azure Functions Recap
What’s Coming in Durable Functions 2.0
Durable Functions Patterns
Function Types
Durable Entities
Demo
Q & A
Agenda
@pacodelacruz
Cloud Compute Spectrum
@pacodelacruz
Azure Compute Spectrum
@pacodelacruz
Serverless FaaS and its Benefits
Server abstraction
(Ops Productivity)
Scaling,
Load balancing &
High availability
built-in
@pacodelacruz
Serverless FaaS and its Benefits
Server abstraction
(Ops Productivity)
Event-driven scaling
not resource-driven
Scaling,
Load balancing &
High availability
built-in
Pay only for
what you use
Devs Productivity
(Programming
Models)
@pacodelacruz
Azure Functions in a Nutshell
Event Triggers Code Outputs
React and get inputs from
a growing list of services
(triggers and input
bindings)
C#, F#,
Node.js, Java,
Python, PowerShell
Send results to a
growing list of services
(output bindings)
@pacodelacruz
Durable Functions in a Nutshell
Based on
Durable Task Framework
Using Azure Storage
(Managed & Abstracted)
To Implement
stateful entities and
workflows-as-code
(C#, F# & Node.js)
Azure Functions
Extension
@pacodelacruz
Durable Entities (Actor-Like)
HTTP Calls from Orch. (with MI)
Pluggable State Providers
Testability Improvements
Orchestrator Roslyn Analyser
What’s Coming in Durable Functions 2.0
@pacodelacruz
Durable Functions Patterns (1.0)
Function Chaining Fan-out & Fan-in Async HTTP APIs
Human Interaction /
Wait for External Events
Monitoring
Start
Get Status
@pacodelacruz
Durable Functions Patterns (2.0)
Function Chaining Fan-out & Fan-in Async HTTP APIs
Human Interaction /
Wait for External Events
Monitoring Durable Entities *
Start
Get Status
Query
* New on 2.0
@pacodelacruz
Durable Functions Types (1.0)
Client ActivityOrchestrator
Start Orch.
Query Orch.
Terminate Orch.
Signal Orch.
Call Activity
Call Sub-orch.
Set Status
Perform Step,
IO
Stateless Stateful Stateless
Client Orchestrator Activity
@pacodelacruz
Durable Functions Types (2.0)
Entity*Client ActivityOrchestrator
Start Orch.
Query Orch.
Terminate Orch.
Signal Orch.
Signal Entity*
Query Entity*
Call Activity
Call Sub-orch.
Set Status
Call Entity*
Perform Step,
IO
Initialise*
Destruct*
Handle Event*
Set State*
Stateless Stateful Stateless Stateful
Client Orchestrator Activity Entity*
* New on 2.0
@pacodelacruz
Addressable via entity Id
Operations execute serially
Created when called or signalled
When not executing, unloaded from memory
One-way messaging from Clients and other Entities ^
Two-way requests from Orchestrations ^
Orchestrations provide distributed locking ^
Durability over latency ^
Durable Entities (Actor-Like)
^ Different from virtual-actors
@pacodelacruz
Function Chaining Pattern
public static async Task<object> Run(
[OrchestrationTrigger] DurableOrchestrationContext ctx)
{
try
{
var x = await ctx.CallActivityAsync<object>("F1");
var y = await ctx.CallActivityAsync<object>("F2", x);
return await ctx.CallActivityAsync<object>("F3", y);
}
catch (Exception ex)
{
// error handling / compensation
}
}
@pacodelacruz
Function Chaining Pattern
public static async Task<object> Run(
[OrchestrationTrigger] DurableOrchestrationContext ctx)
{
try
{
var x = await ctx.CallActivityAsync<object>("F1");
var y = await ctx.CallActivityAsync<object>("F2", x);
return await ctx.CallActivityAsync<object>("F3", y);
}
catch (Exception ex)
{
// error handling / compensation
}
}
@pacodelacruz
Function Chaining Pattern
public static async Task<object> Run(
[OrchestrationTrigger] DurableOrchestrationContext ctx)
{
try
{
var x = await ctx.CallActivityAsync<object>("F1");
var y = await ctx.CallActivityAsync<object>("F2", x);
return await ctx.CallActivityAsync<object>("F3", y);
}
catch (Exception ex)
{
// error handling / compensation
}
}
@pacodelacruz
Fan-out & Fan-In Pattern
public static async Task<int> Run(
[OrchestrationTrigger] DurableOrchestrationContext ctx)
{
object[] workBatch = await ctx.CallActivityAsync<object[]>("F1");
var tasks = new Task<long>[workBatch.Length];
for (int i = 0; i < workBatch.Length; i++)
{
tasks[i] = ctx.CallActivityAsync<int>("F2", workBatch[i]);
}
await Task.WhenAll(tasks);
long sum = tasks.Sum(t => t.Result);
return sum;
}
@pacodelacruz
Fan-out & Fan-In Pattern
public static async Task<int> Run(
[OrchestrationTrigger] DurableOrchestrationContext ctx)
{
object[] workBatch = await ctx.CallActivityAsync<object[]>("F1");
var tasks = new Task<long>[workBatch.Length];
for (int i = 0; i < workBatch.Length; i++)
{
tasks[i] = ctx.CallActivityAsync<int>("F2", workBatch[i]);
}
await Task.WhenAll(tasks);
long sum = tasks.Sum(t => t.Result);
return sum;
}
CallActivityWithRetryAsync
@pacodelacruz
Orchestration Client
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, methods: "post",
Route = "orchestrators/{functionName}")] HttpRequestMessage req,
[OrchestrationClient] DurableOrchestrationClientBase starter, ILogger log)
{
// Function input comes from the request content.
dynamic eventData = await req.Content.ReadAsAsync<object>();
string instanceId = await starter.StartNewAsync("myOrchestrator", eventData);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
var res = starter.CreateCheckStatusResponse(req, instanceId);
return res;
}
@pacodelacruz
Orchestration Client
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, methods: "post",
Route = "orchestrators/{functionName}")] HttpRequestMessage req,
[OrchestrationClient] DurableOrchestrationClientBase starter, ILogger log)
{
// Function input comes from the request content.
dynamic eventData = await req.Content.ReadAsAsync<object>();
string instanceId = await starter.StartNewAsync("myOrchestrator", eventData);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
var res = starter.CreateCheckStatusResponse(req, instanceId);
return res;
}
@pacodelacruz
Orchestration Client
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, methods: "post",
Route = "orchestrators/{functionName}")] HttpRequestMessage req,
[OrchestrationClient] DurableOrchestrationClientBase starter, ILogger log)
{
// Function input comes from the request content.
dynamic eventData = await req.Content.ReadAsAsync<object>();
string instanceId = await starter.StartNewAsync("myOrchestrator", eventData);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
var res = starter.CreateCheckStatusResponse(req, instanceId);
return res;
}
@pacodelacruz
Durable Entity Functions
public class Counter
{
[JsonProperty("value")]
public int CurrentValue { get; set; }
public void Add(int amount) => this.CurrentValue += amount;
public void Reset() => this.CurrentValue = 0;
public int Get() => this.CurrentValue;
[FunctionName(nameof(Counter))]
public static Task Run([EntityTrigger] IDurableEntityContext ctx)
=> ctx.DispatchAsync<Counter>();
}
@pacodelacruz
Durable Entity Functions
public class Counter
{
[JsonProperty("value")]
public int CurrentValue { get; set; }
public void Add(int amount) => this.CurrentValue += amount;
public void Reset() => this.CurrentValue = 0;
public int Get() => this.CurrentValue;
[FunctionName(nameof(Counter))]
public static Task Run([EntityTrigger] IDurableEntityContext ctx)
=> ctx.DispatchAsync<Counter>();
}
@pacodelacruz
Durable Entity Functions
public class Counter
{
[JsonProperty("value")]
public int CurrentValue { get; set; }
public void Add(int amount) => this.CurrentValue += amount;
public void Reset() => this.CurrentValue = 0;
public int Get() => this.CurrentValue;
[FunctionName(nameof(Counter))]
public static Task Run([EntityTrigger] IDurableEntityContext ctx)
=> ctx.DispatchAsync<Counter>();
}
@pacodelacruz
Durable Entity Functions
public class Counter
{
[JsonProperty("value")]
public int CurrentValue { get; set; }
public void Add(int amount) => this.CurrentValue += amount;
public void Reset() => this.CurrentValue = 0;
public int Get() => this.CurrentValue;
[FunctionName(nameof(Counter))]
public static Task Run([EntityTrigger] IDurableEntityContext ctx)
=> ctx.DispatchAsync<Counter>();
}
@pacodelacruz
Durable Functions vs Logic Apps?
Durable Functions Logic Apps
Stateful workflows and entities Stateful workflows
C#, F# and JavaScript Visual designer and WDL
Bindings (~ 20 supported) 250+ connectors
Portable Runtime Run only on Azure
Monitoring based on App Insights & APIs Rich monitoring & management tools
Serverless, dedicated and isolated Serverless & dedicated (ISE)
platform.deloitte.com.au/articles/azure-durable-functions-vs-logic-apps
@pacodelacruz
Questions so far?
@pacodelacruz
Stateful Serverless Request Bin
Why Having Your Own Serverless Request Bin?
Deploy to Azure
Understand the Solution
Test It!
Demo
github.com/pacodelacruz/
serverless-request-bin-durable-functions
@pacodelacruz
Q & A
IntegrationDownUnder
@pacodelacruz
Additional Resources
Twitter @azurefunctions
Documentation aka.ms/durablefunctions &
aka.ms/durable/2docs
Live Web Cast aka.ms/azurefunctionslive
Repos github.com/Azure/azure-functions-durable-extension
github.com/Azure/azure-functions-durable-js
Samples aka.ms/durable/entitysamples
@pacodelacruz
@pacodelacruz
linkedin.com/in/pacodelacruz
pacodelacruz.io
slideshare.net/pacodelac/presentations
github.com/pacodelacruz
@pacodelacruz
:)
IntegrationDownUnder

Durable functions 2.0 (2019-10-10)

  • 1.
    @pacodelacruz Paco de laCruz Durable Functions 2.0 Stateful Serverless Functions IntegrationDownUnder @pacodelacruz
  • 2.
  • 3.
    @pacodelacruz Serverless and AzureFunctions Recap What’s Coming in Durable Functions 2.0 Durable Functions Patterns Function Types Durable Entities Demo Q & A Agenda
  • 4.
  • 5.
  • 6.
    @pacodelacruz Serverless FaaS andits Benefits Server abstraction (Ops Productivity) Scaling, Load balancing & High availability built-in
  • 7.
    @pacodelacruz Serverless FaaS andits Benefits Server abstraction (Ops Productivity) Event-driven scaling not resource-driven Scaling, Load balancing & High availability built-in Pay only for what you use Devs Productivity (Programming Models)
  • 8.
    @pacodelacruz Azure Functions ina Nutshell Event Triggers Code Outputs React and get inputs from a growing list of services (triggers and input bindings) C#, F#, Node.js, Java, Python, PowerShell Send results to a growing list of services (output bindings)
  • 9.
    @pacodelacruz Durable Functions ina Nutshell Based on Durable Task Framework Using Azure Storage (Managed & Abstracted) To Implement stateful entities and workflows-as-code (C#, F# & Node.js) Azure Functions Extension
  • 10.
    @pacodelacruz Durable Entities (Actor-Like) HTTPCalls from Orch. (with MI) Pluggable State Providers Testability Improvements Orchestrator Roslyn Analyser What’s Coming in Durable Functions 2.0
  • 11.
    @pacodelacruz Durable Functions Patterns(1.0) Function Chaining Fan-out & Fan-in Async HTTP APIs Human Interaction / Wait for External Events Monitoring Start Get Status
  • 12.
    @pacodelacruz Durable Functions Patterns(2.0) Function Chaining Fan-out & Fan-in Async HTTP APIs Human Interaction / Wait for External Events Monitoring Durable Entities * Start Get Status Query * New on 2.0
  • 13.
    @pacodelacruz Durable Functions Types(1.0) Client ActivityOrchestrator Start Orch. Query Orch. Terminate Orch. Signal Orch. Call Activity Call Sub-orch. Set Status Perform Step, IO Stateless Stateful Stateless Client Orchestrator Activity
  • 14.
    @pacodelacruz Durable Functions Types(2.0) Entity*Client ActivityOrchestrator Start Orch. Query Orch. Terminate Orch. Signal Orch. Signal Entity* Query Entity* Call Activity Call Sub-orch. Set Status Call Entity* Perform Step, IO Initialise* Destruct* Handle Event* Set State* Stateless Stateful Stateless Stateful Client Orchestrator Activity Entity* * New on 2.0
  • 15.
    @pacodelacruz Addressable via entityId Operations execute serially Created when called or signalled When not executing, unloaded from memory One-way messaging from Clients and other Entities ^ Two-way requests from Orchestrations ^ Orchestrations provide distributed locking ^ Durability over latency ^ Durable Entities (Actor-Like) ^ Different from virtual-actors
  • 16.
    @pacodelacruz Function Chaining Pattern publicstatic async Task<object> Run( [OrchestrationTrigger] DurableOrchestrationContext ctx) { try { var x = await ctx.CallActivityAsync<object>("F1"); var y = await ctx.CallActivityAsync<object>("F2", x); return await ctx.CallActivityAsync<object>("F3", y); } catch (Exception ex) { // error handling / compensation } }
  • 17.
    @pacodelacruz Function Chaining Pattern publicstatic async Task<object> Run( [OrchestrationTrigger] DurableOrchestrationContext ctx) { try { var x = await ctx.CallActivityAsync<object>("F1"); var y = await ctx.CallActivityAsync<object>("F2", x); return await ctx.CallActivityAsync<object>("F3", y); } catch (Exception ex) { // error handling / compensation } }
  • 18.
    @pacodelacruz Function Chaining Pattern publicstatic async Task<object> Run( [OrchestrationTrigger] DurableOrchestrationContext ctx) { try { var x = await ctx.CallActivityAsync<object>("F1"); var y = await ctx.CallActivityAsync<object>("F2", x); return await ctx.CallActivityAsync<object>("F3", y); } catch (Exception ex) { // error handling / compensation } }
  • 19.
    @pacodelacruz Fan-out & Fan-InPattern public static async Task<int> Run( [OrchestrationTrigger] DurableOrchestrationContext ctx) { object[] workBatch = await ctx.CallActivityAsync<object[]>("F1"); var tasks = new Task<long>[workBatch.Length]; for (int i = 0; i < workBatch.Length; i++) { tasks[i] = ctx.CallActivityAsync<int>("F2", workBatch[i]); } await Task.WhenAll(tasks); long sum = tasks.Sum(t => t.Result); return sum; }
  • 20.
    @pacodelacruz Fan-out & Fan-InPattern public static async Task<int> Run( [OrchestrationTrigger] DurableOrchestrationContext ctx) { object[] workBatch = await ctx.CallActivityAsync<object[]>("F1"); var tasks = new Task<long>[workBatch.Length]; for (int i = 0; i < workBatch.Length; i++) { tasks[i] = ctx.CallActivityAsync<int>("F2", workBatch[i]); } await Task.WhenAll(tasks); long sum = tasks.Sum(t => t.Result); return sum; } CallActivityWithRetryAsync
  • 21.
    @pacodelacruz Orchestration Client public staticasync Task<HttpResponseMessage> Run( [HttpTrigger(AuthorizationLevel.Function, methods: "post", Route = "orchestrators/{functionName}")] HttpRequestMessage req, [OrchestrationClient] DurableOrchestrationClientBase starter, ILogger log) { // Function input comes from the request content. dynamic eventData = await req.Content.ReadAsAsync<object>(); string instanceId = await starter.StartNewAsync("myOrchestrator", eventData); log.LogInformation($"Started orchestration with ID = '{instanceId}'."); var res = starter.CreateCheckStatusResponse(req, instanceId); return res; }
  • 22.
    @pacodelacruz Orchestration Client public staticasync Task<HttpResponseMessage> Run( [HttpTrigger(AuthorizationLevel.Function, methods: "post", Route = "orchestrators/{functionName}")] HttpRequestMessage req, [OrchestrationClient] DurableOrchestrationClientBase starter, ILogger log) { // Function input comes from the request content. dynamic eventData = await req.Content.ReadAsAsync<object>(); string instanceId = await starter.StartNewAsync("myOrchestrator", eventData); log.LogInformation($"Started orchestration with ID = '{instanceId}'."); var res = starter.CreateCheckStatusResponse(req, instanceId); return res; }
  • 23.
    @pacodelacruz Orchestration Client public staticasync Task<HttpResponseMessage> Run( [HttpTrigger(AuthorizationLevel.Function, methods: "post", Route = "orchestrators/{functionName}")] HttpRequestMessage req, [OrchestrationClient] DurableOrchestrationClientBase starter, ILogger log) { // Function input comes from the request content. dynamic eventData = await req.Content.ReadAsAsync<object>(); string instanceId = await starter.StartNewAsync("myOrchestrator", eventData); log.LogInformation($"Started orchestration with ID = '{instanceId}'."); var res = starter.CreateCheckStatusResponse(req, instanceId); return res; }
  • 24.
    @pacodelacruz Durable Entity Functions publicclass Counter { [JsonProperty("value")] public int CurrentValue { get; set; } public void Add(int amount) => this.CurrentValue += amount; public void Reset() => this.CurrentValue = 0; public int Get() => this.CurrentValue; [FunctionName(nameof(Counter))] public static Task Run([EntityTrigger] IDurableEntityContext ctx) => ctx.DispatchAsync<Counter>(); }
  • 25.
    @pacodelacruz Durable Entity Functions publicclass Counter { [JsonProperty("value")] public int CurrentValue { get; set; } public void Add(int amount) => this.CurrentValue += amount; public void Reset() => this.CurrentValue = 0; public int Get() => this.CurrentValue; [FunctionName(nameof(Counter))] public static Task Run([EntityTrigger] IDurableEntityContext ctx) => ctx.DispatchAsync<Counter>(); }
  • 26.
    @pacodelacruz Durable Entity Functions publicclass Counter { [JsonProperty("value")] public int CurrentValue { get; set; } public void Add(int amount) => this.CurrentValue += amount; public void Reset() => this.CurrentValue = 0; public int Get() => this.CurrentValue; [FunctionName(nameof(Counter))] public static Task Run([EntityTrigger] IDurableEntityContext ctx) => ctx.DispatchAsync<Counter>(); }
  • 27.
    @pacodelacruz Durable Entity Functions publicclass Counter { [JsonProperty("value")] public int CurrentValue { get; set; } public void Add(int amount) => this.CurrentValue += amount; public void Reset() => this.CurrentValue = 0; public int Get() => this.CurrentValue; [FunctionName(nameof(Counter))] public static Task Run([EntityTrigger] IDurableEntityContext ctx) => ctx.DispatchAsync<Counter>(); }
  • 28.
    @pacodelacruz Durable Functions vsLogic Apps? Durable Functions Logic Apps Stateful workflows and entities Stateful workflows C#, F# and JavaScript Visual designer and WDL Bindings (~ 20 supported) 250+ connectors Portable Runtime Run only on Azure Monitoring based on App Insights & APIs Rich monitoring & management tools Serverless, dedicated and isolated Serverless & dedicated (ISE) platform.deloitte.com.au/articles/azure-durable-functions-vs-logic-apps
  • 29.
  • 30.
    @pacodelacruz Stateful Serverless RequestBin Why Having Your Own Serverless Request Bin? Deploy to Azure Understand the Solution Test It! Demo github.com/pacodelacruz/ serverless-request-bin-durable-functions
  • 31.
  • 32.
    @pacodelacruz Additional Resources Twitter @azurefunctions Documentationaka.ms/durablefunctions & aka.ms/durable/2docs Live Web Cast aka.ms/azurefunctionslive Repos github.com/Azure/azure-functions-durable-extension github.com/Azure/azure-functions-durable-js Samples aka.ms/durable/entitysamples
  • 33.
  • 34.