-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdurable-functions-starting-with-serverless-stateful-orchestrations-in-azure.html
153 lines (132 loc) · 63 KB
/
durable-functions-starting-with-serverless-stateful-orchestrations-in-azure.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
<!DOCTYPE html><html lang=en prefix="og: https://ogp.me/ns#"><head><meta charset=utf-8><base href=https://blog.georgekosmidis.net/durable-functions-starting-with-serverless-stateful-orchestrations-in-azure.html><meta name=viewport content="width=device-width, initial-scale=1"><meta name=description content="Durable Functions is an extension of Azure Functions that enables you to write stateful functions in a serverless computing environment. This extension allows for the creation of complex orchestration workflows where functions can call other functions, wait for those functions to finish, and resume where they left off without maintaining state externally. Durable Functions simplifies the process of writing stateful applications in a stateless environment."><meta name=author content="George Kosmidis"><meta http-equiv=Content-Security-Policy content="script-src 'nonce-fc0cbc39-3c5d-4ed9-824e-538366c81ad4'"><link rel=me type=text/html href=https://github.com/georgekosmidis><link rel=me type=text/html href="https://www.linkedin.com/in/georgekosmidis/"><link rel=me type=text/html href=https://www.youtube.com/c/GeorgeKosmidis><link rel=me type=text/html href="https://sessionize.com/georgekosmidis/"><link rel=me type=text/html href="https://www.meetup.com/munich-dotnet-meetup/members/202480733/profile/"><link rel=me type=text/html href="https://georgekosmidis.net/"><link rel=icon type=image/jpg href=/media/me_180x180.jpg><link rel=apple-touch-icon type=image/jpg sizes=180x180 href=/media/me_180x180.jpg><meta property=og:title content="Durable Functions: Starting with Serverless Stateful Orchestrations in Azure"><meta property=og:type content=article><meta property=og:url content=https://blog.georgekosmidis.net/durable-functions-starting-with-serverless-stateful-orchestrations-in-azure.html><meta property=og:image content=https://blog.georgekosmidis.net/media/100550-feature.png><meta property="og:description " content="Durable Functions is an extension of Azure Functions that enables you to write stateful functions in a serverless computing environment. This extension allows for the creation of complex orchestration workflows where functions can call other functions, wait for those functions to finish, and resume where they left off without maintaining state externally. Durable Functions simplifies the process of writing stateful applications in a stateless environment."><meta property=og:article:published_time content="04/03/2023 15:29:00"><meta property=og:article:modified_time content="04/03/2023 15:29:00"><meta property=og:article:expiration_time content="12/31/9999 23:59:59"><meta property=og:article:author content="George Kosmidis"><meta property=og:article:section content=""><meta property=og:article:tag content=" Azure Functions, Cloud Orchestration, Durable Functions, Serverless Computing, Stateful Workflows"><link href=https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css rel=stylesheet integrity=sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3 crossorigin=anonymous><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/cookieconsent@3.1.1/build/cookieconsent.min.css integrity="sha256-zQ0LblD/Af8vOppw18+2anxsuaz3pWYyVWi+bTvTH8Q=" crossorigin=anonymous><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/@highlightjs/cdn-assets@11.4.0/styles/vs2015.min.css integrity="sha256-Pi771++jBrwgeHVYGOa1sjN8idXlrrYSKQVI7+JA54k=" crossorigin=anonymous><title>Durable Functions: Starting with Serverless Stateful Orchestrations in Azure</title><style>header{background-image:url(/media/header_bg.jpg);background-size:cover;background-position:100% 100%}header svg{width:25px;fill:#f8f9fa}footer svg{width:25px;fill:black}.me{width:180px;height:180px}.article{overflow-wrap:break-word;word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;word-break:break-word}.article img{max-width:100%;height:auto}h1,h2,h3,h4,h5,h6{font-weight:400}h2{margin-top:4rem}.right-column-container h2,.index-card-wrapper h2{margin-top:0}.h3,h3{margin-top:3rem;font-size:1.65rem}.h4,h4{font-size:1.4eem}.h5,h5{font-size:1.2eem}.h4,.h5,h4,h5{margin-top:2rem}th,strong,b{font-weight:500}a{text-decoration:none}a:hover{text-decoration:underline}a.btn svg{margin-bottom:3px}.article-google-engine-top,.article-google-engine-left{display:none}@media (min-width:992px){.container{max-width:1200px}header > .container{max-width:768px}.article-google-engine-left{display:block}td{padding:10px}}@media (max-width:991.98px){.container{padding-right:calc(var(--bs-gutter-x) * .3);padding-left:calc(var(--bs-gutter-x) * .3)}.article-google-engine-top{display:block}td{padding:5px 2px 5px 2px}}blockquote{text-rendering:optimizelegibility;font-weight:400;line-height:160%;box-sizing:inherit;background-color:#DEEBFF;outline-color:#171717;color:#171717;word-wrap:break-word;word-break:break-word;border:1px solid #DEEBFF;border-radius:.375rem;margin-top:1rem;padding:1rem;font-size:1rem;transition:height .5s ease-in,opacity .5s ease-in;display:block;position:relative}blockquote p{margin-bottom:0}dfn{border-color:#343a40}</style><script async src="https://www.googletagmanager.com/gtag/js?id=UA-3071108-41" nonce=fc0cbc39-3c5d-4ed9-824e-538366c81ad4></script><script nonce=fc0cbc39-3c5d-4ed9-824e-538366c81ad4>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}
gtag('js',new Date());gtag('config','UA-3071108-41');</script></head><body class=bg-light><header class="container-fluid p-3 bg-dark border-bottom"> <div class="container text-white"> <div class=row> <div class="col-sm-12 col-md-3 text-center"> <a href="/"> <img src=/media/me_180x180.jpg class="rounded-circle border border-white border-5 me" alt="George Kosmidis - Durable Functions is an extension of Azure Functions that enables you to write stateful functions in a serverless computing environment. This extension allows for the creation of complex orchestration workflows where functions can call other functions, wait for those functions to finish, and resume where they left off without maintaining state externally. Durable Functions simplifies the process of writing stateful applications in a stateless environment."> </a> </div> <div class="col-sm-12 col-md-9"> <div class=container> <div class=row> <div class=col> <h1 class="display-5 text-center">George Kosmidis</h1> <small class="d-block text-center mt-3">Microsoft MVP | Speaks of Azure, AI & .NET | Founder of Munich .NET <br> Building tomorrow @<div style=display:none>slalom</div> <img src=/media/slalom_small.png title=slalom alt=slalom></small> </div> </div> <div class="row mt-3"> <div class="col text-center"> <a href=https://github.com/georgekosmidis target=_blank class=btn rel=noopener> <svg role=img class=icon viewBox="0 0 24 24" xmlns=http://www.w3.org/2000/svg><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"></path></svg> </a> <a href="https://www.linkedin.com/in/georgekosmidis/" target=_blank class=btn rel=noopener> <svg role=img viewBox="0 0 24 24" xmlns=http://www.w3.org/2000/svg><title>LinkedIn</title><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"></path></svg> </a> <a href=https://www.youtube.com/c/GeorgeKosmidis target=_blank class=btn rel=noopener> <svg role=img viewBox="0 0 24 24" xmlns=http://www.w3.org/2000/svg><title>YouTube</title><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"></path></svg> </a> <a href="https://sessionize.com/georgekosmidis/" target=_blank class=btn rel=noopener> <svg role=img viewBox="0 0 288 288" xmlns=http://www.w3.org/2000/svg><title>Sessionize</title><g transform="translate(0.000000,288.000000) scale(0.100000,-0.100000)"><path d="M135 2861 c-22 -10 -54 -34 -72 -52 -67 -71 -63 22 -63 -1369 0 -1391 -4 -1298 63 -1369 70 -73 42 -71 715 -71 l602 0 0 40 c0 108 -52 232 -127 301 -92 85 -229 125 -469 139 -119 6 -160 13 -190 28 -63 32 -96 81 -108 161 -6 43 12 87 470 1118 263 590 480 1073 483 1073 3 0 22 -42 41 -92 47 -127 157 -344 228 -453 156 -239 367 -450 607 -608 100 -65 373 -202 478 -238 37 -13 67 -27 67 -30 0 -3 -348 -160 -772 -349 -425 -188 -777 -346 -782 -350 -4 -5 11 -18 35 -30 235 -119 367 -331 394 -633 l7 -77 482 0 c444 0 484 2 521 19 51 23 111 89 125 138 8 27 10 249 8 773 -4 660 -6 743 -22 809 -69 289 -197 522 -396 721 -199 199 -432 327 -721 396 -67 16 -148 18 -819 21 -712 3 -747 2 -785 -16z m2069 -483 c99 -51 198 -181 200 -264 1 -57 -21 -79 -77 -78 -137 3 -328 220 -288 328 19 50 84 55 165 14z"></path></g></svg> </a> <a href="https://www.meetup.com/munich-dotnet-meetup/" target=_blank class=btn rel=noopener> <svg role=img viewBox="0 0 24 24" xmlns=http://www.w3.org/2000/svg><title>Meetup</title><path d="M6.98.555a.518.518 0 0 0-.105.011.53.53 0 1 0 .222 1.04.533.533 0 0 0 .409-.633.531.531 0 0 0-.526-.418zm6.455.638a.984.984 0 0 0-.514.143.99.99 0 1 0 1.02 1.699.99.99 0 0 0 .34-1.36.992.992 0 0 0-.846-.482zm-3.03 2.236a5.029 5.029 0 0 0-4.668 3.248 3.33 3.33 0 0 0-1.46.551 3.374 3.374 0 0 0-.94 4.562 3.634 3.634 0 0 0-.605 4.649 3.603 3.603 0 0 0 2.465 1.597c.018.732.238 1.466.686 2.114a3.9 3.9 0 0 0 5.423.992c.068-.047.12-.106.184-.157.987.881 2.47 1.026 3.607.24a2.91 2.91 0 0 0 1.162-1.69 4.238 4.238 0 0 0 2.584-.739 4.274 4.274 0 0 0 1.19-5.789 2.466 2.466 0 0 0 .433-3.308 2.448 2.448 0 0 0-1.316-.934 4.436 4.436 0 0 0-.776-2.873 4.467 4.467 0 0 0-5.195-1.656 5.106 5.106 0 0 0-2.773-.807zm-5.603.817a.759.759 0 0 0-.423.135.758.758 0 1 0 .863 1.248.757.757 0 0 0 .193-1.055.758.758 0 0 0-.633-.328zm15.994 2.37a.842.842 0 0 0-.47.151.845.845 0 1 0 1.175.215.845.845 0 0 0-.705-.365zm-8.15 1.028c.063 0 .124.005.182.014a.901.901 0 0 1 .45.187c.169.134.273.241.432.393.24.227.414.089.534.02.208-.122.369-.219.984-.208.633.011 1.363.237 1.514 1.317.168 1.199-1.966 4.289-1.817 5.722.106 1.01 1.815.299 1.96 1.22.186 1.198-2.136.753-2.667.493-.832-.408-1.337-1.34-1.12-2.26.16-.688 1.7-3.498 1.757-3.93.059-.44-.177-.476-.324-.484-.19-.01-.34.081-.526.362-.169.255-2.082 4.085-2.248 4.398-.296.56-.67.694-1.044.674-.548-.029-.798-.32-.72-.848.047-.31 1.26-3.049 1.323-3.476.039-.265-.013-.546-.275-.68-.263-.135-.572.07-.664.227-.128.215-1.848 4.706-2.032 5.038-.316.576-.65.76-1.152.784-1.186.056-2.065-.92-1.678-2.116.173-.532 1.316-4.571 1.895-5.599.389-.69 1.468-1.216 2.217-.892.387.167.925.437 1.084.507.366.163.759-.277.913-.412.155-.134.302-.276.49-.357.142-.06.343-.095.532-.094zm10.88 2.057a.468.468 0 0 0-.093.011.467.467 0 0 0-.36.555.47.47 0 0 0 .557.36.47.47 0 0 0 .36-.557.47.47 0 0 0-.464-.37zm-22.518.81a.997.997 0 0 0-.832.434 1 1 0 1 0 1.39-.258 1 1 0 0 0-.558-.176zm21.294 2.094a.635.635 0 0 0-.127.013.627.627 0 0 0-.48.746.628.628 0 0 0 .746.483.628.628 0 0 0 .482-.746.63.63 0 0 0-.621-.496zm-18.24 6.097a.453.453 0 0 0-.092.012.464.464 0 1 0 .195.908.464.464 0 0 0 .356-.553.465.465 0 0 0-.459-.367zm13.675 1.55a1.044 1.044 0 0 0-.583.187 1.047 1.047 0 1 0 1.456.265 1.044 1.044 0 0 0-.873-.451zM11.4 22.154a.643.643 0 0 0-.36.115.646.646 0 0 0-.164.899.646.646 0 0 0 .899.164.646.646 0 0 0 .164-.898.646.646 0 0 0-.54-.28z"></path></svg> </a> </div> </div> </div> </div> </div> </div> </header> <main class="container bg-light pt-4 pb-5"> <div class="row masonry"> <div class=container> <div class="row mx-auto article-google-engine-top"> <div class="col pb-4"> <div class="card text-center bg-transparent border-0" style=height:33px><script async src="https://cse.google.com/cse.js?cx=c67a1214306f44e87" nonce=fc0cbc39-3c5d-4ed9-824e-538366c81ad4></script><div class=gcse-searchbox-only></div> </div> </div> </div> <div class="row mx-auto"> <div class="col-lg-8 mb-4 shadow bg-white rounded"> <div class="container article"> <div class="row border-end border-start border-bottom bg-light"> <div class=col style=text-align:center> <a class="btn align-middle" href=https://github.com/georgekosmidis/blog.georgekosmidis.net//tree/main/workables/articles/100550-durable-functions-starting-with-serverless-stateful-orchestrations-in-azure/content.html role=button style=text-decoration:none target=_blank rel=noopener> <svg xmlns=http://www.w3.org/2000/svg width=22 height=22 fill=currentColor viewBox="0 0 16 16"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z" /></svg> Edit Page </a> <a class="btn align-middle" href="https://github.com/georgekosmidis/blog.georgekosmidis.net//issues/new?permalink=https://github.com/georgekosmidis/blog.georgekosmidis.net//tree/main/workables/articles/100550-durable-functions-starting-with-serverless-stateful-orchestrations-in-azure/content.html" role=button style=text-decoration:none target=_blank rel=noopener> <svg xmlns=http://www.w3.org/2000/svg width=22 height=22 fill=currentColor viewBox="0 0 24 24"><path fill-rule=evenodd d="M2.5 12a9.5 9.5 0 1119 0 9.5 9.5 0 01-19 0zM12 1C5.925 1 1 5.925 1 12s4.925 11 11 11 11-4.925 11-11S18.075 1 12 1zm0 13a2 2 0 100-4 2 2 0 000 4z"></path></svg> Create Issue </a> <a class="btn align-middle" href="https://github.com/georgekosmidis/blog.georgekosmidis.net/discussions/new?title=Durable Functions: Starting with Serverless Stateful Orchestrations in Azure&body=Article%20URL:%20https://blog.georgekosmidis.net//durable-functions-starting-with-serverless-stateful-orchestrations-in-azure.html" role=button style=text-decoration:none target=_blank rel=noopener> <svg aria-hidden=true height=22 viewBox="0 0 16 16" width=22><path fill-rule=evenodd d="M1.5 2.75a.25.25 0 01.25-.25h8.5a.25.25 0 01.25.25v5.5a.25.25 0 01-.25.25h-3.5a.75.75 0 00-.53.22L3.5 11.44V9.25a.75.75 0 00-.75-.75h-1a.25.25 0 01-.25-.25v-5.5zM1.75 1A1.75 1.75 0 000 2.75v5.5C0 9.216.784 10 1.75 10H2v1.543a1.457 1.457 0 002.487 1.03L7.061 10h3.189A1.75 1.75 0 0012 8.25v-5.5A1.75 1.75 0 0010.25 1h-8.5zM14.5 4.75a.25.25 0 00-.25-.25h-.5a.75.75 0 110-1.5h.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0114.25 12H14v1.543a1.457 1.457 0 01-2.487 1.03L9.22 12.28a.75.75 0 111.06-1.06l2.22 2.22v-2.19a.75.75 0 01.75-.75h1a.25.25 0 00.25-.25v-5.5z"></path></svg> Discuss </a> <a class="btn ms-4" href="/" role=button style=text-decoration:none> <svg xmlns=http://www.w3.org/2000/svg width=22 height=22 class=mb-1 fill=currentColor class="bi bi-house-fill" viewBox="0 0 16 16"><path fill-rule=evenodd d="m8 3.293 6 6V13.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 13.5V9.293l6-6zm5-.793V6l-2-2V2.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5z"></path><path fill-rule=evenodd d="M7.293 1.5a1 1 0 0 1 1.414 0l6.647 6.646a.5.5 0 0 1-.708.708L8 2.207 1.354 8.854a.5.5 0 1 1-.708-.708L7.293 1.5z"></path></svg> Go Home </a> </div> </div> <div class=row> <div class="col mt-3"> <h1 class="text-center display-6">Durable Functions: Starting with Serverless Stateful Orchestrations in Azure</h1> </div> </div> <div class=row> <div class="col pb-2"> <div class="text-black-50 text-center"> <small>by <a href="https://georgekosmidis.net/" rel=noopener>George Kosmidis</a> / Published 2 years ago</small> </div> </div> </div> <div class=row> <div class="col text-center"> <img src=/media/100550-feature.png alt="Durable Functions: Starting with Serverless Stateful Orchestrations in Azure"> </div> </div> <div class=row> <div class="col pt-3 pb-6"> <h2 id=table-of-contents>Table of Contents</h2> <ul><li><a href=#table-of-contents>Table of Contents</a></li> <li><a href=#introduction-to-serverless-computing>Introduction to Serverless Computing</a></li> <li><a href=#serverless-computing-on-azure>Serverless Computing on Azure</a> <ul><li><a href=#key-features-of-azure-functions->Key Features of Azure Functions:</a></li> <li><a href=#example--a-simple-http-triggered-azure-function>Example: A Simple HTTP-triggered Azure Function</a></li></ul> </li> <li><a href=#durable-serverless-computing-on-azure>Durable Serverless Computing on Azure</a> <ul><li><a href=#why-durable-functions->Why Durable Functions?</a></li> <li><a href=#key-concepts>Key Concepts</a></li> <li><a href=#example--order-processing-workflow>Example: Order Processing Workflow</a></li></ul> </li> <li><a href=#core-concepts-of-azure-durable-functions>Core Concepts of Azure Durable Functions</a> <ul><li><a href=#function-chaining--how-to-execute-a-sequence-of-functions-in-a-specific-order>Function Chaining: How to Execute a Sequence of Functions in a Specific Order</a></li> <li><a href=#fan-out-fan-in--implementing-parallel-processing-patterns-and-aggregating-results>Fan-out/Fan-in: Implementing Parallel Processing Patterns and Aggregating Results</a></li> <li><a href=#async-http-apis--building-long-running-processes-with-http-endpoints>Async HTTP APIs: Building Long-Running Processes with HTTP Endpoints</a></li> <li><a href=#monitoring--creating-workflows-that-monitor-the-status-of-external-services>Monitoring: Creating Workflows that Monitor the Status of External Services</a></li> <li><a href=#human-interaction--managing-workflows-that-require-human-intervention-or-approval>Human Interaction: Managing Workflows That Require Human Intervention or Approval</a></li> <li><a href=#resources-and-further-reading>Resources and Further Reading</a></li></ul> </li></ul> <h2 id=introduction-to-serverless-computing>Introduction to Serverless Computing</h2> <p>Serverless computing is a cloud computing execution model where the cloud provider dynamically manages the allocation and provisioning of servers. A serverless application runs in stateless compute containers that are event-triggered, ephemeral (may last for one invocation), and fully managed by the cloud provider. This means developers can focus on their code without worrying about the underlying infrastructure.</p> <p>The primary benefits of serverless computing include:</p> <ul><li><strong>Cost Efficiency</strong>: You only pay for the compute time you consume, down to the nearest 100 milliseconds, making it highly cost-effective.</li> <li><strong>Scalability</strong>: Automatically scales your application by running code in response to each trigger.</li> <li><strong>Simplified Operations</strong>: Eliminates the need to manage servers, leading to faster development cycles.</li></ul> <h2 id=serverless-computing-on-azure>Serverless Computing on Azure</h2> <p>Azure Functions is Microsoft's answer to serverless computing. It allows developers to write less code, maintain less infrastructure, and save on costs. In essence, Azure Functions is a serverless compute service that enables you to run code on-demand without having to explicitly provision or manage infrastructure.</p> <h3 id=key-features-of-azure-functions>Key Features of Azure Functions:</h3> <ul><li><strong>Event-driven</strong>: You can trigger functions from a variety of events, including HTTP requests, queue messages, and more.</li> <li><strong>Integrated Security</strong>: Protect your functions with oAuth providers such as Azure Entra.</li> <li><strong>Programming Language Support</strong>: Write functions using your choice of C#, Java, JavaScript, TypeScript, and Python.</li> <li><strong>Scalability</strong>: Azure Functions scale automatically based on demand, so your code always has the resources it needs to run, but you're only charged for the exact amount of resources your functions use.</li></ul> <h3 id=example-a-simple-http-triggered-azure-function>Example: A Simple HTTP-triggered Azure Function</h3> <p>Let’s look at a simple example of an Azure Function that's triggered by an HTTP request. This function will return a personalized greeting to the user.</p> <pre><code class=language-csharp>using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
public static class GreetFunction
{
[FunctionName("GreetFunction")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req)
{
string name = req.Query["name"];
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the query string for a personalized response."
: $"Hello, {name}. This HTTP triggered function executed successfully.";
return new OkObjectResult(responseMessage);
}
}
</code></pre> <p>In this example:</p> <ul><li>The <code>HttpTrigger</code> attribute indicates that the function is triggered by an HTTP request.</li> <li>The function checks for a <code>name</code> query parameter and uses it to generate a personalized greeting. If no name is provided, it returns a generic message.</li> <li>The function returns an <code>OkObjectResult</code>, which is an HTTP 200 OK response, containing the greeting message.</li></ul> <h2 id=durable-serverless-computing-on-azure>Durable Serverless Computing on Azure</h2> <p>Durable Functions is an extension of Azure Functions that enables you to write stateful functions in a serverless computing environment. This extension allows for the creation of complex orchestration workflows where functions can call other functions, wait for those functions to finish, and resume where they left off without maintaining state externally. Durable Functions simplifies the process of writing stateful applications in a stateless environment.</p> <h3 id=why-durable-functions>Why Durable Functions?</h3> <ul><li><strong>State Management</strong>: Automatically manages state persistence, enabling you to focus on business logic rather than data storage.</li> <li><strong>Complex Workflows</strong>: Simplifies the development of complex workflows, including sequential and parallel execution patterns.</li> <li><strong>Durability and Reliability</strong>: Ensures execution even in the event of unexpected failures, with built-in retry policies and error handling.</li> <li><strong>Serverless Benefits</strong>: Inherits the scalability, flexibility, and cost-efficiency of Azure Functions.</li></ul> <h3 id=key-concepts>Key Concepts</h3> <ul><li><strong>Orchestrator Functions</strong>: Control the workflow, executing other functions in a sequence or parallel, making decisions, and managing state.</li> <li><strong>Activity Functions</strong>: Perform the actual work of the workflow, such as database operations, calls to external services, or any computational task.</li> <li><strong>Client Functions</strong>: Trigger orchestrations or entities and inquire about the status.</li> <li><strong>Entity Functions</strong>: Entity functions define operations for reading and updating small pieces of state.</li></ul> <h3 id=example-order-processing-workflow>Example: Order Processing Workflow</h3> <p>Imagine a scenario where you need to process orders in an e-commerce system. The workflow involves validating the order, checking inventory, processing payment, and finally, sending a confirmation email to the customer.</p> <h4 id=step-1-define-the-orchestrator-function>Step 1: Define the Orchestrator Function</h4> <p>The orchestrator function coordinates the entire process. It calls activity functions for each step of the process and waits for their completion.</p> <pre><code class=language-csharp>[FunctionName("ProcessOrderOrchestrator")]
public static async Task<object> RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
var order = context.GetInput<Order>();
// Validate the order
bool isValid = await context.CallActivityAsync<bool>("ValidateOrder", order);
if (isValid)
{
// Check inventory
bool isInStock = await context.CallActivityAsync<bool>("CheckInventory", order);
if (isInStock)
{
// Process payment
bool paymentSuccess = await context.CallActivityAsync<bool>("ProcessPayment", order);
if (paymentSuccess)
{
// Send confirmation email
await context.CallActivityAsync("SendConfirmationEmail", order);
return "Order processed successfully!";
}
}
}
return "Order processing failed.";
}
</code></pre> <h4 id=step-2-implement-activity-functions>Step 2: Implement Activity Functions</h4> <p>Each step in the process is implemented as an activity function. For example, here's a simplified version of the <code>ValidateOrder</code> activity function:</p> <pre><code class=language-csharp>[FunctionName("ValidateOrder")]
public static bool ValidateOrder([ActivityTrigger] Order order, ILogger log)
{
// Logic to validate the order
return true; // Assume the order is valid for this example
}
</code></pre> <p>Each activity function (<code>ValidateOrder</code>, <code>CheckInventory</code>, <code>ProcessPayment</code>, <code>SendConfirmationEmail</code>) would be implemented similarly, performing its specific task.</p> <h4 id=step-3-trigger-the-workflow>Step 3: Trigger the Workflow</h4> <p>A client function triggers the orchestrator function, starting the workflow. The client could be an HTTP-triggered function that receives order submissions.</p> <h2 id=core-concepts-of-azure-durable-functions>Core Concepts of Azure Durable Functions</h2> <p>To fully leverage the power of Durable Functions, it’s essential to go through a few core concepts that also underpin how these functions operate and interact within the Azure ecosystem.</p> <h3 id=function-chaining-how-to-execute-a-sequence-of-functions-in-a-specific-order>Function Chaining: How to Execute a Sequence of Functions in a Specific Order</h3> <p>Function chaining in Azure Durable Functions is a pattern that allows you to execute a series of functions in a particular sequence. This pattern is particularly useful when you have tasks that need to be performed in order, where each task starts only after the previous one has completed, and the output of one function becomes the input to the next. Here’s a deeper dive into how function chaining works and how to implement it effectively.</p> <h4 id=how-durable-functions-facilitate-function-chaining>How Durable Functions Facilitate Function Chaining</h4> <p>Statefulness is crucial for function chaining, as it allows the workflow to remember the state of execution as it moves from one function to the next. Durable Functions use "orchestrator functions" to control the workflow, including the execution order of functions.</p> <h4 id=implementing-function-chaining>Implementing Function Chaining</h4> <ol><li><p><strong>Define Your Functions</strong>: Start by defining the Azure Functions that will form the links in your chain. Each function should perform a discrete task.</p> </li> <li><p><strong>Create an Orchestrator Function</strong>: The orchestrator function is responsible for controlling the sequence in which your functions are called. You define the logic of the function chaining in this orchestrator.</p> </li> <li><p><strong>Use <code>CallActivityAsync</code></strong>: Inside the orchestrator function, use the <code>CallActivityAsync</code> method to call each function in the sequence. This method asynchronously calls another function, waits for its completion, and retrieves the result. The syntax is straightforward: <code>var result = await context.CallActivityAsync<ReturnType>("FunctionName", input);</code> where <code>ReturnType</code> is the type of result expected from the function, <code>"FunctionName"</code> is the name of the function to call, and <code>input</code> is any input the function requires.</p> </li> <li><p><strong>Pass Results Along</strong>: After a function completes, its return value can be passed as input to the next function in the chain. This is done within the orchestrator function, allowing you to control the flow of data between functions.</p> </li></ol> <h4 id=example-scenario>Example Scenario</h4> <p>Imagine a workflow where you need to process an order, charge a payment, and then send a confirmation email. You would create three functions: <code>ProcessOrder</code>, <code>ChargePayment</code>, and <code>SendConfirmationEmail</code>. Your orchestrator function would call these functions in sequence, passing the necessary data from one step to the next.</p> <pre><code class=language-csharp>[FunctionName("OrderWorkflow")]
public static async Task Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
var orderDetails = context.GetInput<OrderDetails>();
// Process the order
var processedOrder = await context.CallActivityAsync<OrderDetails>("ProcessOrder", orderDetails);
// Charge payment
var paymentResult = await context.CallActivityAsync<PaymentResult>("ChargePayment", processedOrder);
// Send confirmation email
await context.CallActivityAsync("SendConfirmationEmail", paymentResult);
}
</code></pre> <p>In this example, each function is called in order, with the output of one function serving as the input for the next. This ensures that the order is processed, paid for, and confirmed in a sequence that respects the logical flow of the application.</p> <h4 id=best-practices>Best Practices</h4> <ul><li><strong>Idempotency</strong>: Ensure that your functions are idempotent, meaning they can be called multiple times without changing the result beyond the initial call. This is important for retry policies and error recovery in durable functions.</li></ul> <h3 id=fan-outfan-in-implementing-parallel-processing-patterns-and-aggregating-results>Fan-out/Fan-in: Implementing Parallel Processing Patterns and Aggregating Results</h3> <p>When dealing with complex workflows in serverless architectures, efficiency and scalability are key. The "Fan-out/Fan-in" pattern is a powerful strategy in Durable Functions that enables you to execute multiple functions in parallel and then aggregate their results. This pattern is particularly useful for tasks that can be processed independently, allowing for significant performance improvements over sequential processing.</p> <h4 id=understanding-the-fan-outfan-in-pattern>Understanding the Fan-out/Fan-in Pattern</h4> <p><strong>Fan-out</strong> refers to the process of executing multiple operations in parallel. Rather than running tasks one after another, the Fan-out pattern disperses them across multiple instances, allowing them to run concurrently. This is particularly useful for operations like batch processing, data analysis, or any scenario where tasks are not dependent on the outcome of others.</p> <p><strong>Fan-in</strong> is the subsequent step where the results of these parallel operations are collected and aggregated. Once all parallel tasks have completed, the Fan-in process consolidates their outcomes into a single result. This could be a summary, a combined dataset, or any form of aggregation that supports the workflow's objectives.</p> <h4 id=implementing-fan-outfan-in-in-durable-functions>Implementing Fan-out/Fan-in in Durable Functions</h4> <p>Durable Functions simplifies the implementation of the Fan-out/Fan-in pattern through its orchestration capabilities. Here's a step-by-step guide to leveraging this pattern:</p> <ol><li><p><strong>Orchestration Function</strong>: Start with an orchestration function that defines the workflow. This function will coordinate the fan-out to parallel activities and the subsequent fan-in of results.</p> </li> <li><p><strong>Fan-out</strong>: Use the <code>CallActivityAsync</code> method within a loop or parallel tasks to initiate multiple instances of an activity function. These instances run concurrently, achieving the fan-out effect.</p> </li> <li><p><strong>Collect Results</strong>: As each activity function completes, its result is returned to the orchestration function. Store these results in a collection, such as a list or an array.</p> </li> <li><p><strong>Fan-in</strong>: Once all parallel tasks have completed, proceed to the fan-in phase. Aggregate the collected results into a final outcome. This could involve summing numbers, concatenating strings, merging datasets, etc.</p> </li> <li><p><strong>Return Aggregate Result</strong>: The orchestration function returns the aggregated result, concluding the workflow.</p> </li></ol> <h4 id=best-practices-and-considerations>Best Practices and Considerations</h4> <ul><li><strong>Concurrency Limits</strong>: Be aware of the concurrency and throughput limits of your environment. Excessive parallelism can lead to throttling or increased costs.</li> <li><strong>Error Handling</strong>: Implement comprehensive error handling for individual tasks. Failures in one task should not compromise the entire operation.</li> <li><strong>State Management</strong>: Durable Functions efficiently manages state for you, but be mindful of the data being passed between functions to avoid performance bottlenecks.</li></ul> <h4 id=a-simple-example>A simple example</h4> <p>To implement this pattern in Durable Functions, you'll primarily work with an orchestrator function. The orchestrator function is responsible for coordinating the execution of activity functions (the tasks that will run in parallel) and aggregating their results.</p> <p>Here's a simple example:</p> <pre><code class=language-csharp>[FunctionName("FanOutFanInOrchestrator")]
public static async Task<List<string>> RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
var outputs = new List<string>();
// Assume we have a list of tasks to process in parallel
var tasksToProcess = new List<string> { "Task1", "Task2", "Task3" };
// Fan-out: Initiating parallel tasks
var parallelTasks = new List<Task<string>>();
foreach (var task in tasksToProcess)
{
Task<string> taskOperation = context.CallActivityAsync<string>("ProcessTask", task);
parallelTasks.Add(taskOperation);
}
// Fan-in: Waiting for all tasks to complete and aggregating results
await Task.WhenAll(parallelTasks);
foreach (var completedTask in parallelTasks)
{
outputs.Add(await completedTask);
}
// Outputs contains the aggregated results of all completed tasks
return outputs;
}
[FunctionName("ProcessTask")]
public static string ProcessTask([ActivityTrigger] string task, ILogger log)
{
// Simulate task processing
log.LogInformation($"Processing {task}");
return $"{task} completed";
}
</code></pre> <h4 id=explanation>Explanation</h4> <ul><li><p><strong>Orchestrator Function (<code>FanOutFanInOrchestrator</code>)</strong>: This function orchestrates the workflow. It starts by creating a list of tasks (<code>tasksToProcess</code>). For each task, it initiates an asynchronous operation by calling the <code>ProcessTask</code> activity function. These operations are executed in parallel (fan-out). After initiating all tasks, it waits for all of them to complete using <code>Task.WhenAll</code> and aggregates the results (fan-in).</p> </li> <li><p><strong>Activity Function (<code>ProcessTask</code>)</strong>: Represents a single unit of work that is executed as part of the parallel operations. In this example, it simply logs the processing of a task and returns a string indicating completion.</p> </li></ul> <h3 id=async-http-apis-building-long-running-processes-with-http-endpoints>Async HTTP APIs: Building Long-Running Processes with HTTP Endpoints</h3> <p>Durable Functions provide an elegant solution for handling long-running processes in serverless applications. One of the standout features is the ability to create asynchronous HTTP APIs, which are crucial for tasks that require more time to complete than a standard HTTP request/response cycle allows. This feature is especially useful for processes such as data processing, batch jobs, or any task that may take an unpredictable amount of time to complete.</p> <h4 id=the-challenge-with-long-running-http-requests>The Challenge with Long-Running HTTP Requests</h4> <p>In traditional HTTP interactions, a client sends a request to a server, which processes the request and returns a response. This synchronous model assumes that the processing time is short, allowing the client to remain connected until the response is received. However, long-running processes challenge this assumption, leading to timeouts or a poor user experience as the client waits for a response.</p> <h4 id=how-durable-functions-address-this>How Durable Functions Address This</h4> <p>Durable Functions introduce a pattern that decouples the long-running task from the initial HTTP request, using status query endpoints, send asynchronous response techniques, and external event handling:</p> <h5 id=starting-the-process>1. Starting the Process</h5> <p>When an HTTP request initiates a long-running process, the Durable Function orchestrator function starts the task and immediately responds to the client with an HTTP 202 Accepted status. This response includes URLs for status querying and sending external events to the process, if necessary.</p> <pre><code class=language-csharp>[FunctionName("StartProcess")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequestMessage req,
[DurableClient] IDurableOrchestrationClient starter,
ILogger log)
{
// Parse request and start a new orchestration
var instanceId = await starter.StartNewAsync("MyOrchestration", null);
// Return a response with status endpoint
var response = starter.CreateCheckStatusResponse(req, instanceId);
response.Headers.RetryAfter = new RetryConditionHeaderValue(TimeSpan.FromSeconds(10));
return response;
}
</code></pre> <h5 id=checking-process-status>2. Checking Process Status</h5> <p>The client can use the provided status query endpoint to check the progress of the task. This endpoint returns the current state of the process, including running, completed, or failed, along with any outputs if the process has completed.</p> <h5 id=handling-long-processing-times>3. Handling Long Processing Times</h5> <p>For processes that take an extended period, Durable Functions leverage durable timers to pause the orchestrator function, freeing up resources while waiting. The orchestrator function can resume once the external event is triggered or the timer completes, ensuring efficient resource utilization.</p> <h4 id=benefits-of-async-http-apis-in-durable-functions>Benefits of Async HTTP APIs in Durable Functions</h4> <ul><li><strong>Scalability</strong>: By decoupling the execution from the client request, resources are used more efficiently, allowing for greater scalability.</li> <li><strong>Resiliency</strong>: The stateful nature of Durable Functions means that long-running processes can withstand failures and continue from their last known state.</li> <li><strong>Flexibility</strong>: Clients are not tied up waiting for a response and can check back at their convenience or be notified upon completion.</li></ul> <h3 id=monitoring-creating-workflows-that-monitor-the-status-of-external-services>Monitoring: Creating Workflows that Monitor the Status of External Services</h3> <p>In the reality of serverless architectures, maintaining awareness of your external services' health and performance is crucial, and for that, a particularly powerful application of Durable Functions is creating workflows designed to monitor the status of external services continuously.</p> <h4 id=the-basics-of-monitoring-with-durable-functions>The Basics of Monitoring with Durable Functions</h4> <p>Monitoring workflows in Durable Functions are typically implemented using the "Eternal Orchestrations" pattern. This pattern allows an orchestration to run indefinitely, invoking itself after a specified delay, thus creating a continuous loop. This approach is particularly suited for monitoring tasks, where the workflow needs to poll an external service at regular intervals to check its status.</p> <h4 id=implementing-a-monitoring-workflow>Implementing a Monitoring Workflow</h4> <p>Here's a high-level approach to implementing a monitoring workflow using Durable Functions:</p> <ol><li><p><strong>Orchestration Function Setup</strong>: Begin by defining an orchestration function. This function will act as the central coordinator for your monitoring workflow. It will call activity functions to perform specific tasks, such as checking the service's status and sending alerts if necessary.</p> </li> <li><p><strong>Activity Function for Service Check</strong>: Implement an activity function dedicated to querying the external service's status. This could involve making HTTP requests to a status endpoint, checking for specific responses, or validating the service's response time against predefined thresholds.</p> </li> <li><p><strong>Scheduling Checks</strong>: Within the orchestration function, use the <code>CreateTimer</code> method to schedule the next service check. This method enables you to specify a delay, effectively determining how frequently your workflow polls the external service.</p> </li> <li><p><strong>Handling Service Status</strong>: Based on the response from your activity function, implement logic within your orchestration to handle various service states. For example, if the service is down, you might initiate another activity function to send an alert to your operations team.</p> </li> <li><p><strong>Looping for Continuous Monitoring</strong>: To create an eternal orchestration, make sure the orchestration function calls itself after completing each cycle of service checks and handling. This self-invocation, combined with a delay set by <code>CreateTimer</code>, ensures that the monitoring workflow runs continuously.</p> </li></ol> <h4 id=best-practices-1>Best Practices</h4> <ul><li><strong>Idempotency</strong>: Ensure that your activity functions are idempotent. Since your monitoring workflow will repeatedly invoke these functions, idempotency ensures that repeated executions do not lead to unintended consequences.</li> <li><strong>Scalability</strong>: Consider the scalability of your workflow. Durable Functions are designed to scale automatically, but monitoring highly available services may require adjustments to function timeouts and concurrency settings.</li> <li><strong>Error Handling</strong>: Implement comprehensive error handling within your workflow. This includes handling transient errors gracefully and defining clear escalation paths for persistent issues with the external service.</li> <li><strong>Resource Optimization</strong>: Be mindful of the costs associated with polling external services frequently. Optimize the frequency of checks to balance between timely awareness and resource consumption.</li></ul> <h4 id=practical-example-monitoring-an-external-api-service>Practical Example: Monitoring an External API Service</h4> <p>In this example, we'll create a simple Durable Function workflow that monitors the availability of an external API service by periodically sending HTTP requests to check its health endpoint.</p> <h5 id=step-1-setup-the-orchestration-function>Step 1: Setup the Orchestration Function</h5> <p>First, define your orchestration function. This function will orchestrate the monitoring process, including scheduling checks and handling the results.</p> <pre><code class=language-csharp>[FunctionName("MonitorExternalServiceOrchestrator")]
public static async Task RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
DateTime nextCheck = context.CurrentUtcDateTime.AddMinutes(5); // Schedule the next check in 5 minutes
await context.CallActivityAsync("CheckExternalServiceStatus", null);
await context.CreateTimer(nextCheck, CancellationToken.None); // Wait until the next check
context.ContinueAsNew(null); // Continue the orchestration indefinitely
}
</code></pre> <h5 id=step-2-implement-the-activity-function-to-check-service-status>Step 2: Implement the Activity Function to Check Service Status</h5> <p>Create an activity function that performs the actual check on the external service's health endpoint.</p> <pre><code class=language-csharp>[FunctionName("CheckExternalServiceStatus")]
public static async Task<bool> CheckServiceStatus([ActivityTrigger] IDurableActivityContext context, ILogger log)
{
var httpClient = new HttpClient();
var response = await httpClient.GetAsync("https://your-external-service/health");
bool isServiceUp = response.IsSuccessStatusCode;
if (!isServiceUp)
{
// Log or handle the service being down (e.g., send an alert)
log.LogError("External service is down.");
}
else
{
log.LogInformation("External service is up.");
}
return isServiceUp;
}
</code></pre> <p>By adapting this example, you can monitor virtually any external service or endpoint, customizing the check frequency, handling logic, and alerting mechanisms to suit your specific requirements.</p> <h3 id=human-interaction-managing-workflows-that-require-human-intervention-or-approval>Human Interaction: Managing Workflows That Require Human Intervention or Approval</h3> <p>Durable Functions, an extension of Azure Functions, allows developers to build complex orchestration workflows in a serverless environment. A significant capability of these orchestrations is managing processes that require human intervention or approval. This functionality is crucial for workflows where a decision or action by a person is needed before the process can proceed.</p> <h4 id=understanding-human-interaction-in-workflows>Understanding Human Interaction in Workflows</h4> <p>Human interactions in automated workflows typically involve pausing the workflow until an external input is received. This could be an approval from a manager, feedback from a client, or a manual review of generated content. Durable Functions handle these scenarios through <strong>external events</strong>.</p> <h4 id=implementing-human-interaction>Implementing Human Interaction</h4> <p>To implement a workflow that requires human intervention in Durable Functions:</p> <ol><li><p><strong>Start with an Orchestrator Function</strong>: This function coordinates the workflow, including the wait for human input.</p> </li> <li><p><strong>Use External Events for Pausing and Resuming</strong>: The orchestrator function can pause its execution waiting for an external event. This event represents the human action, such as an approval.</p> </li> <li><p><strong>Sending Approval Requests</strong>: Typically, an activity function sends an approval request to a human via email or a web interface. This message includes a way to send the approval back, often through a secure link that triggers another Azure Function.</p> </li> <li><p><strong>Waiting for Approval</strong>: The orchestrator function waits for the external event triggered by human action. This is done using the <code>waitForExternalEvent</code> method, which effectively pauses the workflow until the specified event is received.</p> </li> <li><p><strong>Resuming the Workflow</strong>: Once the external event (e.g., approval) is received, the orchestrator function continues, executing the next steps in the workflow based on the input received from the human interaction.</p> </li></ol> <h4 id=handling-timeouts>Handling Timeouts</h4> <p>In real-world scenarios, human actions may not be immediate. Durable Functions allows setting a timeout for the wait. If the human action is not received within the timeout period, the workflow can proceed with a default action, escalate the issue, or retry the request.</p> <h4 id=example-scenario-document-approval-process>Example Scenario: Document Approval Process</h4> <p>Consider a document approval process where a document generated by a system needs to be reviewed and approved by a manager before it is finalized:</p> <ul><li><strong>Step 1</strong>: The orchestrator function starts the workflow, generating the document and sending a notification to the manager for approval.</li> <li><strong>Step 2</strong>: The workflow waits for the manager's approval, pausing execution until an external event signals the manager's decision.</li> <li><strong>Step 3</strong>: Upon receiving approval (the external event), the workflow resumes, finalizing the document and proceeding to the next steps, such as notifying stakeholders or archiving the document.</li></ul> <h3 id=resources-and-further-reading>Resources and Further Reading</h3> <h4 id=official-documentation-and-tutorials>Official Documentation and Tutorials</h4> <ul><li>Azure Durable Functions Documentation: <a href="https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-overview?WT.mc_id=DT-MVP-5004591">docs.microsoft.com/azure/azure-functions/durable/durable-functions-overview</a></li> <li>Microsoft Learn - Durable Functions Module: <a href="https://docs.microsoft.com/learn/modules/create-long-running-serverless-workflow-with-durable-functions?WT.mc_id=DT-MVP-5004591">docs.microsoft.com/learn/modules/create-long-running-serverless-workflow-with-durable-functions</a></li></ul> <h4 id=community-resources-forums-and-blogs>Community Resources, Forums, and Blogs</h4> <ul><li>GitHub Repository for Durable Functions: <a href=https://github.com/Azure/azure-functions-durable-extension>github.com/Azure/azure-functions-durable-extension</a></li> <li>Stack Overflow - Azure Functions Durable Tag: <a href=https://stackoverflow.com/questions/tagged/azure-functions-durable>stackoverflow.com/questions/tagged/azure-functions-durable</a></li> <li>Azure Blog: <a href="https://azure.microsoft.com/blog/topics/azure-functions?WT.mc_id=DT-MVP-5004591">azure.microsoft.com/blog/topics/azure-functions</a></li></ul> </div> </div> <div class="row border-end border-start border-top bg-light mt-4"> <div class="col pt-2" style=text-align:center> This page is <strong>open source</strong>. Noticed a typo? Or something unclear?<br> <a class="btn align-middle" href=https://github.com/georgekosmidis/blog.georgekosmidis.net//tree/main/workables/articles/100550-durable-functions-starting-with-serverless-stateful-orchestrations-in-azure/content.html role=button style=text-decoration:none target=_blank rel=noopener> <svg xmlns=http://www.w3.org/2000/svg width=22 height=22 fill=currentColor viewBox="0 0 16 16"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z" /></svg> Edit Page </a> <a class="btn align-middle" href="https://github.com/georgekosmidis/blog.georgekosmidis.net//issues/new?permalink=https://github.com/georgekosmidis/blog.georgekosmidis.net//tree/main/workables/articles/100550-durable-functions-starting-with-serverless-stateful-orchestrations-in-azure/content.html" role=button style=text-decoration:none target=_blank rel=noopener> <svg xmlns=http://www.w3.org/2000/svg width=22 height=22 fill=currentColor viewBox="0 0 24 24"><path fill-rule=evenodd d="M2.5 12a9.5 9.5 0 1119 0 9.5 9.5 0 01-19 0zM12 1C5.925 1 1 5.925 1 12s4.925 11 11 11 11-4.925 11-11S18.075 1 12 1zm0 13a2 2 0 100-4 2 2 0 000 4z"></path></svg> Create Issue </a> <a class="btn align-middle" href="https://github.com/georgekosmidis/blog.georgekosmidis.net/discussions/new?title=Durable Functions: Starting with Serverless Stateful Orchestrations in Azure&body=Article%20URL:%20https://blog.georgekosmidis.net//durable-functions-starting-with-serverless-stateful-orchestrations-in-azure.html" role=button style=text-decoration:none target=_blank rel=noopener> <svg aria-hidden=true height=22 viewBox="0 0 16 16" version=1.1 width=22 data-view-component=true class="octicon octicon-comment-discussion UnderlineNav-octicon d-none d-sm-inline"><path fill-rule=evenodd d="M1.5 2.75a.25.25 0 01.25-.25h8.5a.25.25 0 01.25.25v5.5a.25.25 0 01-.25.25h-3.5a.75.75 0 00-.53.22L3.5 11.44V9.25a.75.75 0 00-.75-.75h-1a.25.25 0 01-.25-.25v-5.5zM1.75 1A1.75 1.75 0 000 2.75v5.5C0 9.216.784 10 1.75 10H2v1.543a1.457 1.457 0 002.487 1.03L7.061 10h3.189A1.75 1.75 0 0012 8.25v-5.5A1.75 1.75 0 0010.25 1h-8.5zM14.5 4.75a.25.25 0 00-.25-.25h-.5a.75.75 0 110-1.5h.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0114.25 12H14v1.543a1.457 1.457 0 01-2.487 1.03L9.22 12.28a.75.75 0 111.06-1.06l2.22 2.22v-2.19a.75.75 0 01.75-.75h1a.25.25 0 00.25-.25v-5.5z"></path></svg> Discuss </a> </div> </div> </div> </div> <div class=col-lg-4> <div class="container px-0 mx-0 right-column-container"> <div class="col mb-4"> <div class="card text-center bg-transparent border-0" style=height:33px><script async src="https://cse.google.com/cse.js?cx=c67a1214306f44e87" nonce=fc0cbc39-3c5d-4ed9-824e-538366c81ad4></script><div class=gcse-searchbox-only></div> </div> </div> <div class="col mb-4"> <div class="card shadow"> <img src=/media/mvp.png class="bd-placeholder-img card-img-top" alt="Microsoft MVP - George Kosmidis"> <a href=https://mvp.microsoft.com/en-us/PublicProfile/5004591 target=_blank title="Microsoft MVP - George Kosmidis" class=stretched-link rel=noopener></a> </div> </div> <div class="col mb-4"> <div class="card shadow"> <img src=/media/azure-architecture-icons.png class="bd-placeholder-img card-img-top" alt="Azure Architecture Icons - SVGs, PNGs and draw.io libraries"> <a href=/azure-architecture-icons.html target=_top title="Azure Architecture Icons - SVGs, PNGs and draw.io libraries" class=stretched-link rel=noopener></a> </div> </div> </div> </div> </div> <div class=col-lg-4> </div> </div> </div> </div> </main> <footer class="footer mt-auto py-3 border-top"> <div class="container mx-auto text-center"> <a href=https://github.com/georgekosmidis target=_blank class=btn rel=noopener> <svg role=img class=icon viewBox="0 0 24 24" xmlns=http://www.w3.org/2000/svg><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"></path></svg> </a> <a href="https://www.linkedin.com/in/georgekosmidis/" target=_blank class=btn rel=noopener> <svg role=img viewBox="0 0 24 24" xmlns=http://www.w3.org/2000/svg><title>LinkedIn</title><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"></path></svg> </a> <a href=https://www.youtube.com/c/GeorgeKosmidis target=_blank class=btn rel=noopener> <svg role=img viewBox="0 0 24 24" xmlns=http://www.w3.org/2000/svg><title>YouTube</title><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"></path></svg> </a> <a href="https://sessionize.com/georgekosmidis/" target=_blank class=btn rel=noopener> <svg role=img viewBox="0 0 288 288" xmlns=http://www.w3.org/2000/svg><title>Sessionize</title><g transform="translate(0.000000,288.000000) scale(0.100000,-0.100000)"><path d="M135 2861 c-22 -10 -54 -34 -72 -52 -67 -71 -63 22 -63 -1369 0 -1391 -4 -1298 63 -1369 70 -73 42 -71 715 -71 l602 0 0 40 c0 108 -52 232 -127 301 -92 85 -229 125 -469 139 -119 6 -160 13 -190 28 -63 32 -96 81 -108 161 -6 43 12 87 470 1118 263 590 480 1073 483 1073 3 0 22 -42 41 -92 47 -127 157 -344 228 -453 156 -239 367 -450 607 -608 100 -65 373 -202 478 -238 37 -13 67 -27 67 -30 0 -3 -348 -160 -772 -349 -425 -188 -777 -346 -782 -350 -4 -5 11 -18 35 -30 235 -119 367 -331 394 -633 l7 -77 482 0 c444 0 484 2 521 19 51 23 111 89 125 138 8 27 10 249 8 773 -4 660 -6 743 -22 809 -69 289 -197 522 -396 721 -199 199 -432 327 -721 396 -67 16 -148 18 -819 21 -712 3 -747 2 -785 -16z m2069 -483 c99 -51 198 -181 200 -264 1 -57 -21 -79 -77 -78 -137 3 -328 220 -288 328 19 50 84 55 165 14z"></path></g></svg> </a> <a href=https://www.facebook.com/groups/msdevtech target=_blank class=btn rel=noopener> <svg xmlns=http://www.w3.org/2000/svg viewBox="0 0 400 400" role=img><path d="M353.701,0H55.087C24.665,0,0.002,24.662,0.002,55.085v298.616c0,30.423,24.662,55.085,55.085,55.085 h147.275l0.251-146.078h-37.951c-4.932,0-8.935-3.988-8.954-8.92l-0.182-47.087c-0.019-4.959,3.996-8.989,8.955-8.989h37.882 v-45.498c0-52.8,32.247-81.55,79.348-81.55h38.65c4.945,0,8.955,4.009,8.955,8.955v39.704c0,4.944-4.007,8.952-8.95,8.955 l-23.719,0.011c-25.615,0-30.575,12.172-30.575,30.035v39.389h56.285c5.363,0,9.524,4.683,8.892,10.009l-5.581,47.087 c-0.534,4.506-4.355,7.901-8.892,7.901h-50.453l-0.251,146.078h87.631c30.422,0,55.084-24.662,55.084-55.084V55.085 C408.786,24.662,384.124,0,353.701,0z" /></svg> </a> <a href="https://www.meetup.com/munich-dotnet-meetup/" target=_blank class=btn rel=noopener> <svg role=img viewBox="0 0 24 24" xmlns=http://www.w3.org/2000/svg><title>Meetup</title><path d="M6.98.555a.518.518 0 0 0-.105.011.53.53 0 1 0 .222 1.04.533.533 0 0 0 .409-.633.531.531 0 0 0-.526-.418zm6.455.638a.984.984 0 0 0-.514.143.99.99 0 1 0 1.02 1.699.99.99 0 0 0 .34-1.36.992.992 0 0 0-.846-.482zm-3.03 2.236a5.029 5.029 0 0 0-4.668 3.248 3.33 3.33 0 0 0-1.46.551 3.374 3.374 0 0 0-.94 4.562 3.634 3.634 0 0 0-.605 4.649 3.603 3.603 0 0 0 2.465 1.597c.018.732.238 1.466.686 2.114a3.9 3.9 0 0 0 5.423.992c.068-.047.12-.106.184-.157.987.881 2.47 1.026 3.607.24a2.91 2.91 0 0 0 1.162-1.69 4.238 4.238 0 0 0 2.584-.739 4.274 4.274 0 0 0 1.19-5.789 2.466 2.466 0 0 0 .433-3.308 2.448 2.448 0 0 0-1.316-.934 4.436 4.436 0 0 0-.776-2.873 4.467 4.467 0 0 0-5.195-1.656 5.106 5.106 0 0 0-2.773-.807zm-5.603.817a.759.759 0 0 0-.423.135.758.758 0 1 0 .863 1.248.757.757 0 0 0 .193-1.055.758.758 0 0 0-.633-.328zm15.994 2.37a.842.842 0 0 0-.47.151.845.845 0 1 0 1.175.215.845.845 0 0 0-.705-.365zm-8.15 1.028c.063 0 .124.005.182.014a.901.901 0 0 1 .45.187c.169.134.273.241.432.393.24.227.414.089.534.02.208-.122.369-.219.984-.208.633.011 1.363.237 1.514 1.317.168 1.199-1.966 4.289-1.817 5.722.106 1.01 1.815.299 1.96 1.22.186 1.198-2.136.753-2.667.493-.832-.408-1.337-1.34-1.12-2.26.16-.688 1.7-3.498 1.757-3.93.059-.44-.177-.476-.324-.484-.19-.01-.34.081-.526.362-.169.255-2.082 4.085-2.248 4.398-.296.56-.67.694-1.044.674-.548-.029-.798-.32-.72-.848.047-.31 1.26-3.049 1.323-3.476.039-.265-.013-.546-.275-.68-.263-.135-.572.07-.664.227-.128.215-1.848 4.706-2.032 5.038-.316.576-.65.76-1.152.784-1.186.056-2.065-.92-1.678-2.116.173-.532 1.316-4.571 1.895-5.599.389-.69 1.468-1.216 2.217-.892.387.167.925.437 1.084.507.366.163.759-.277.913-.412.155-.134.302-.276.49-.357.142-.06.343-.095.532-.094zm10.88 2.057a.468.468 0 0 0-.093.011.467.467 0 0 0-.36.555.47.47 0 0 0 .557.36.47.47 0 0 0 .36-.557.47.47 0 0 0-.464-.37zm-22.518.81a.997.997 0 0 0-.832.434 1 1 0 1 0 1.39-.258 1 1 0 0 0-.558-.176zm21.294 2.094a.635.635 0 0 0-.127.013.627.627 0 0 0-.48.746.628.628 0 0 0 .746.483.628.628 0 0 0 .482-.746.63.63 0 0 0-.621-.496zm-18.24 6.097a.453.453 0 0 0-.092.012.464.464 0 1 0 .195.908.464.464 0 0 0 .356-.553.465.465 0 0 0-.459-.367zm13.675 1.55a1.044 1.044 0 0 0-.583.187 1.047 1.047 0 1 0 1.456.265 1.044 1.044 0 0 0-.873-.451zM11.4 22.154a.643.643 0 0 0-.36.115.646.646 0 0 0-.164.899.646.646 0 0 0 .899.164.646.646 0 0 0 .164-.898.646.646 0 0 0-.54-.28z"></path></svg> </a> <div class=text-muted>© Copyright 2022, <a href="https://georgekosmidis.net/" target=_blank rel=noopener>George Kosmidis</a> <br> Is lawyer the most boring job in the world? Read the <a href=/privacy.html>Privacy Policy</a> to find out. <br><small class=text-black-50>Last Build: 2025-02-28T03:07:50+00:00 </small> </div> </div> </footer><script src=https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js integrity=sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p crossorigin=anonymous nonce=fc0cbc39-3c5d-4ed9-824e-538366c81ad4></script><script src=https://cdn.jsdelivr.net/npm/cookieconsent@3.1.1/build/cookieconsent.min.js integrity="sha256-5VhCqFam2Cn+yjw61zbBNrbHVJ6SRydPeKopYlngbiQ=" crossorigin=anonymous nonce=fc0cbc39-3c5d-4ed9-824e-538366c81ad4></script><script src=https://cdn.jsdelivr.net/npm/@highlightjs/cdn-assets@11.4.0/highlight.min.js integrity="sha256-GCgWKkl4RE3+M/TNH5d/F80Tz30PQT+Oubq5Q3I5c20=" crossorigin=anonymous nonce=fc0cbc39-3c5d-4ed9-824e-538366c81ad4></script><script nonce=fc0cbc39-3c5d-4ed9-824e-538366c81ad4>hljs.highlightAll();</script><script nonce=fc0cbc39-3c5d-4ed9-824e-538366c81ad4>window.cookieconsent.initialise({"palette":{"popup":{"background":"#343c66","text":"#cfcfe8"},"button":{"background":"#f71559"}},"showLink":false,"theme":"classic","content":{"message":"Cookie Warning! My blog uses Google Analytics so Google knows you are here. Maybe others too, including FBI, CIA, NASA, ESA, IIS and other acronyms. If you don't like it, you must unfortunately go :(","dismiss":"It's just code, I 'll stay!"}});</script></body></html>