Skip to content

Commit 7d749af

Browse files
authored
Merge pull request #97423 from anthonychu/20191129-update-samples-batch-1
Update Durable Functions tutorials
2 parents 768709e + 4f51aa4 commit 7d749af

File tree

7 files changed

+236
-258
lines changed

7 files changed

+236
-258
lines changed

articles/azure-functions/durable/durable-functions-cloud-backup.md

Lines changed: 56 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,13 @@ ms.author: azfuncdf
1010

1111
*Fan-out/fan-in* refers to the pattern of executing multiple functions concurrently and then performing some aggregation on the results. This article explains a sample that uses [Durable Functions](durable-functions-overview.md) to implement a fan-in/fan-out scenario. The sample is a durable function that backs up all or some of an app's site content into Azure Storage.
1212

13-
[!INCLUDE [v1-note](../../../includes/functions-durable-v1-tutorial-note.md)]
14-
1513
[!INCLUDE [durable-functions-prerequisites](../../../includes/durable-functions-prerequisites.md)]
1614

1715
## Scenario overview
1816

1917
In this sample, the functions upload all files under a specified directory recursively into blob storage. They also count the total number of bytes that were uploaded.
2018

21-
It's possible to write a single function that takes care of everything. The main problem you would run into is **scalability**. A single function execution can only run on a single VM, so the throughput will be limited by the throughput of that single VM. Another problem is **reliability**. If there's a failure midway through, or if the entire process takes more than 5 minutes, the backup could fail in a partially completed state. It would then need to be restarted.
19+
It's possible to write a single function that takes care of everything. The main problem you would run into is **scalability**. A single function execution can only run on a single virtual machine, so the throughput will be limited by the throughput of that single VM. Another problem is **reliability**. If there's a failure midway through, or if the entire process takes more than 5 minutes, the backup could fail in a partially completed state. It would then need to be restarted.
2220

2321
A more robust approach would be to write two regular functions: one would enumerate the files and add the file names to a queue, and another would read from the queue and upload the files to blob storage. This approach is better in terms of throughput and reliability, but it requires you to provision and manage a queue. More importantly, significant complexity is introduced in terms of **state management** and **coordination** if you want to do anything more, like report the total number of bytes uploaded.
2422

@@ -28,84 +26,103 @@ A Durable Functions approach gives you all of the mentioned benefits with very l
2826

2927
This article explains the following functions in the sample app:
3028

31-
* `E2_BackupSiteContent`
32-
* `E2_GetFileList`
33-
* `E2_CopyFileToBlob`
29+
* `E2_BackupSiteContent`: An [orchestrator function](durable-functions-bindings.md#orchestration-trigger) that calls `E2_GetFileList` to obtain a list of files to back up, then calls `E2_CopyFileToBlob` to back up each file.
30+
* `E2_GetFileList`: An [activity function](durable-functions-bindings.md#activity-trigger) that returns a list of files in a directory.
31+
* `E2_CopyFileToBlob`: An activity function that backs up a single file to Azure Blob Storage.
3432

35-
The following sections explain the configuration and code that is used for C# scripting. The code for Visual Studio development is shown at the end of the article.
33+
### E2_BackupSiteContent orchestrator function
3634

37-
## The cloud backup orchestration (Visual Studio Code and Azure portal sample code)
35+
This orchestrator function essentially does the following:
3836

39-
The `E2_BackupSiteContent` function uses the standard *function.json* for orchestrator functions.
37+
1. Takes a `rootDirectory` value as an input parameter.
38+
2. Calls a function to get a recursive list of files under `rootDirectory`.
39+
3. Makes multiple parallel function calls to upload each file into Azure Blob Storage.
40+
4. Waits for all uploads to complete.
41+
5. Returns the sum total bytes that were uploaded to Azure Blob Storage.
4042

41-
[!code-json[Main](~/samples-durable-functions/samples/csx/E2_BackupSiteContent/function.json)]
43+
# [C#](#tab/csharp)
4244

4345
Here is the code that implements the orchestrator function:
4446

45-
### C#
47+
[!code-csharp[Main](~/samples-durable-functions/samples/precompiled/BackupSiteContent.cs?range=16-42)]
4648

47-
[!code-csharp[Main](~/samples-durable-functions/samples/csx/E2_BackupSiteContent/run.csx)]
49+
Notice the `await Task.WhenAll(tasks);` line. All the individual calls to the `E2_CopyFileToBlob` function were *not* awaited, which allows them to run in parallel. When we pass this array of tasks to `Task.WhenAll`, we get back a task that won't complete *until all the copy operations have completed*. If you're familiar with the Task Parallel Library (TPL) in .NET, then this is not new to you. The difference is that these tasks could be running on multiple virtual machines concurrently, and the Durable Functions extension ensures that the end-to-end execution is resilient to process recycling.
4850

49-
### JavaScript (Functions 2.0 only)
51+
After awaiting from `Task.WhenAll`, we know that all function calls have completed and have returned values back to us. Each call to `E2_CopyFileToBlob` returns the number of bytes uploaded, so calculating the sum total byte count is a matter of adding all those return values together.
5052

51-
[!code-javascript[Main](~/samples-durable-functions/samples/javascript/E2_BackupSiteContent/index.js)]
53+
# [JavaScript](#tab/javascript)
5254

53-
This orchestrator function essentially does the following:
55+
The function uses the standard *function.json* for orchestrator functions.
5456

55-
1. Takes a `rootDirectory` value as an input parameter.
56-
2. Calls a function to get a recursive list of files under `rootDirectory`.
57-
3. Makes multiple parallel function calls to upload each file into Azure Blob Storage.
58-
4. Waits for all uploads to complete.
59-
5. Returns the sum total bytes that were uploaded to Azure Blob Storage.
57+
[!code-json[Main](~/samples-durable-functions/samples/javascript/E2_BackupSiteContent/function.json)]
6058

61-
Notice the `await Task.WhenAll(tasks);` (C#) and `yield context.df.Task.all(tasks);` (JavaScript) lines. All the individual calls to the `E2_CopyFileToBlob` function were *not* awaited, which allows them to run in parallel. When we pass this array of tasks to `Task.WhenAll` (C#) or `context.df.Task.all` (JavaScript), we get back a task that won't complete *until all the copy operations have completed*. If you're familiar with the Task Parallel Library (TPL) in .NET or [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) in JavaScript, then this is not new to you. The difference is that these tasks could be running on multiple VMs concurrently, and the Durable Functions extension ensures that the end-to-end execution is resilient to process recycling.
59+
Here is the code that implements the orchestrator function:
60+
61+
[!code-javascript[Main](~/samples-durable-functions/samples/javascript/E2_BackupSiteContent/index.js)]
62+
63+
Notice the `yield context.df.Task.all(tasks);` line. All the individual calls to the `E2_CopyFileToBlob` function were *not* yielded, which allows them to run in parallel. When we pass this array of tasks to `context.df.Task.all`, we get back a task that won't complete *until all the copy operations have completed*. If you're familiar with [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) in JavaScript, then this is not new to you. The difference is that these tasks could be running on multiple virtual machines concurrently, and the Durable Functions extension ensures that the end-to-end execution is resilient to process recycling.
6264

6365
> [!NOTE]
6466
> Although tasks are conceptually similar to JavaScript promises, orchestrator functions should use `context.df.Task.all` and `context.df.Task.any` instead of `Promise.all` and `Promise.race` to manage task parallelization.
6567
66-
After awaiting from `Task.WhenAll` (or yielding from `context.df.Task.all`), we know that all function calls have completed and have returned values back to us. Each call to `E2_CopyFileToBlob` returns the number of bytes uploaded, so calculating the sum total byte count is a matter of adding all those return values together.
68+
After yielding from `context.df.Task.all`, we know that all function calls have completed and have returned values back to us. Each call to `E2_CopyFileToBlob` returns the number of bytes uploaded, so calculating the sum total byte count is a matter of adding all those return values together.
69+
70+
---
6771

68-
## Helper activity functions
72+
### Helper activity functions
6973

70-
The helper activity functions, as with other samples, are just regular functions that use the `activityTrigger` trigger binding. For example, the *function.json* file for `E2_GetFileList` looks like the following:
74+
The helper activity functions, as with other samples, are just regular functions that use the `activityTrigger` trigger binding.
7175

72-
[!code-json[Main](~/samples-durable-functions/samples/csx/E2_GetFileList/function.json)]
76+
#### E2_GetFileList activity function
7377

74-
And here is the implementation:
78+
# [C#](#tab/csharp)
79+
80+
[!code-csharp[Main](~/samples-durable-functions/samples/precompiled/BackupSiteContent.cs?range=44-54)]
7581

76-
### C#
82+
# [JavaScript](#tab/javascript)
7783

78-
[!code-csharp[Main](~/samples-durable-functions/samples/csx/E2_GetFileList/run.csx)]
84+
The *function.json* file for `E2_GetFileList` looks like the following:
7985

80-
### JavaScript (Functions 2.0 only)
86+
[!code-json[Main](~/samples-durable-functions/samples/javascript/E2_GetFileList/function.json)]
87+
88+
And here is the implementation:
8189

8290
[!code-javascript[Main](~/samples-durable-functions/samples/javascript/E2_GetFileList/index.js)]
8391

84-
The JavaScript implementation of `E2_GetFileList` uses the `readdirp` module to recursively read the directory structure.
92+
The function uses the `readdirp` module (version 2.x) to recursively read the directory structure.
93+
94+
---
8595

8696
> [!NOTE]
87-
> You might be wondering why you couldn't just put this code directly into the orchestrator function. You could, but this would break one of the fundamental rules of orchestrator functions, which is that they should never do I/O, including local file system access.
97+
> You might be wondering why you couldn't just put this code directly into the orchestrator function. You could, but this would break one of the fundamental rules of orchestrator functions, which is that they should never do I/O, including local file system access. For more information, see [Orchestrator function code constraints](durable-functions-code-constraints.md).
8898
89-
The *function.json* file for `E2_CopyFileToBlob` is similarly simple:
99+
#### E2_CopyFileToBlob activity function
90100

91-
[!code-json[Main](~/samples-durable-functions/samples/csx/E2_CopyFileToBlob/function.json)]
101+
# [C#](#tab/csharp)
92102

93-
The C# implementation is also straightforward. It happens to use some advanced features of Azure Functions bindings (that is, the use of the `Binder` parameter), but you don't need to worry about those details for the purpose of this walkthrough.
103+
[!code-csharp[Main](~/samples-durable-functions/samples/precompiled/BackupSiteContent.cs?range=56-81)]
94104

95-
### C#
105+
> [!NOTE]
106+
> You will need to install the `Microsoft.Azure.WebJobs.Extensions.Storage` NuGet package to run the sample code.
96107
97-
[!code-csharp[Main](~/samples-durable-functions/samples/csx/E2_CopyFileToBlob/run.csx)]
108+
The function uses some advanced features of Azure Functions bindings (that is, the use of the [`Binder` parameter](../functions-dotnet-class-library.md#binding-at-runtime)), but you don't need to worry about those details for the purpose of this walkthrough.
98109

99-
### JavaScript (Functions 2.0 only)
110+
# [JavaScript](#tab/javascript)
100111

101-
The JavaScript implementation does not have access to the `Binder` feature of Azure Functions, so the [Azure Storage SDK for Node](https://github.com/Azure/azure-storage-node) takes its place.
112+
The *function.json* file for `E2_CopyFileToBlob` is similarly simple:
113+
114+
[!code-json[Main](~/samples-durable-functions/samples/javascript/E2_CopyFileToBlob/function.json)]
115+
116+
The JavaScript implementation uses the [Azure Storage SDK for Node](https://github.com/Azure/azure-storage-node) to upload the files to Azure Blob Storage.
102117

103118
[!code-javascript[Main](~/samples-durable-functions/samples/javascript/E2_CopyFileToBlob/index.js)]
104119

120+
---
121+
105122
The implementation loads the file from disk and asynchronously streams the contents into a blob of the same name in the "backups" container. The return value is the number of bytes copied to storage, that is then used by the orchestrator function to compute the aggregate sum.
106123

107124
> [!NOTE]
108-
> This is a perfect example of moving I/O operations into an `activityTrigger` function. Not only can the work be distributed across many different VMs, but you also get the benefits of checkpointing the progress. If the host process gets terminated for any reason, you know which uploads have already completed.
125+
> This is a perfect example of moving I/O operations into an `activityTrigger` function. Not only can the work be distributed across many different machines, but you also get the benefits of checkpointing the progress. If the host process gets terminated for any reason, you know which uploads have already completed.
109126
110127
## Run the sample
111128

@@ -160,15 +177,6 @@ Content-Type: application/json; charset=utf-8
160177

161178
Now you can see that the orchestration is complete and approximately how much time it took to complete. You also see a value for the `output` field, which indicates that around 450 KB of logs were uploaded.
162179

163-
## Visual Studio sample code
164-
165-
Here is the orchestration as a single C# file in a Visual Studio project:
166-
167-
> [!NOTE]
168-
> You will need to install the `Microsoft.Azure.WebJobs.Extensions.Storage` NuGet package to run the sample code below.
169-
170-
[!code-csharp[Main](~/samples-durable-functions/samples/precompiled/BackupSiteContent.cs)]
171-
172180
## Next steps
173181

174182
This sample has shown how to implement the fan-out/fan-in pattern. The next sample shows how to implement the monitor pattern using [durable timers](durable-functions-timers.md).

0 commit comments

Comments
 (0)