Durable Functions のパターンと技術概念 (Azure Functions)Durable Functions patterns and technical concepts (Azure Functions)

Durable Functions は Azure FunctionsAzure WebJobs の拡張機能です。Durable Functions is an extension of Azure Functions and Azure WebJobs. Durable Functions を使用して、サーバーレス環境でステートフル関数を記述できます。You can use Durable Functions to write stateful functions in a serverless environment. この拡張機能は状態、チェックポイント、再起動を管理します。The extension manages state, checkpoints, and restarts for you.

この記事では、Azure Functions 用の Durable Functions 拡張機能の動作と一般的な実装パターンについての詳細な情報を示します。This article gives you detailed information about the behaviors of the Durable Functions extension for Azure Functions and common implementation patterns. この情報は、開発上の課題を解決するために Durable Functions を使用する方法を決定するのに役立ちます。The information can help you determine how to use Durable Functions to help solve your development challenges.

注意

Durable Functions は Azure Functions の高度な拡張機能であり、すべてのアプリケーションに適しているわけではありません。Durable Functions is an advanced extension for Azure Functions that isn't appropriate for all applications. この記事は、開発者が Azure Functions のコンセプトを十分に理解しており、サーバーレス アプリケーションの開発に関わる課題があることを前提としています。This article assumes that you have a strong familiarity with concepts in Azure Functions and the challenges involved in serverless application development.

パターンPatterns

このセクションでは、Durable Functions が有用である、いくつかの典型的なアプリケーションのパターンについて説明します。This section describes some common application patterns where Durable Functions can be useful.

パターン #1: 関数チェーンPattern #1: Function chaining

関数チェーン パターンでは、一連の関数が特定の順序で実行されます。In the function chaining pattern, a sequence of functions executes in a specific order. このパターンでは、ある関数の出力が、別の関数の入力に適用されます。In this pattern, the output of one function is applied to the input of another function.

関数チェーン パターンの図

次の例に示すように、Durable Functions を使用して、関数チェーン パターンを簡潔に実装できます。You can use Durable Functions to implement the function chaining pattern concisely as shown in the following example:

C# スクリプトC# script

public static async Task<object> Run(DurableOrchestrationContext context)
{
    try
    {
        var x = await context.CallActivityAsync<object>("F1");
        var y = await context.CallActivityAsync<object>("F2", x);
        var z = await context.CallActivityAsync<object>("F3", y);
        return  await context.CallActivityAsync<object>("F4", z);
    }
    catch (Exception)
    {
        // Error handling or compensation goes here.
    }
}

注意

C# でのプリコンパイル済みの永続関数の記述と C# スクリプトでのプリコンパイル済みの永続関数の記述との間には、この例には示されていないわずかな違いがあります。There are subtle differences between writing a precompiled durable function in C# and writing a precompiled durable function in the C# script that's shown in the example. C# のプリコンパイル済みの関数では、対応する属性で永続パラメーターを修飾する必要があります。In a C# precompiled function, durable parameters must be decorated with respective attributes. 一例として、DurableOrchestrationContext パラメーターの [OrchestrationTrigger] 属性があります。An example is the [OrchestrationTrigger] attribute for the DurableOrchestrationContext parameter. C# のプリコンパイル済みの関数では、パラメーターが適切に修飾されていない場合、実行時に関数に変数を挿入できないことでエラーが発生します。In a C# precompiled durable function, if the parameters aren't properly decorated, the runtime can't inject the variables into the function, and an error occurs. 例については、GitHub の azure-functions-durable-extension のサンプルを参照してください。For more examples, see the azure-functions-durable-extension samples on GitHub.

JavaScript (Functions 2.x のみ)JavaScript (Functions 2.x only)

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

module.exports = df.orchestrator(function*(context) {
    const x = yield context.df.callActivity("F1");
    const y = yield context.df.callActivity("F2", x);
    const z = yield context.df.callActivity("F3", y);
    return yield context.df.callActivity("F4", z);
});

この例では、F1F2F3、および F4 という値が、関数アプリ内の他の関数の名前です。In this example, the values F1, F2, F3, and F4 are the names of other functions in the function app. 通常の命令型のコーディング構造を使用して、制御フローを実装できます。You can implement control flow by using normal imperative coding constructs. コードは、上から下に実行されます。Code executes from the top down. コードに条件文やループなどの既存言語の制御フロー セマンティクスを含めることができます。The code can involve existing language control flow semantics, like conditionals and loops. try/catch/finally ブロックに、エラー処理ロジックを含めることができます。You can include error handling logic in try/catch/finally blocks.

context パラメーターの DurableOrchestrationContext ((.NET)) と context.df オブジェクト (JavaScript) を使用して、名前によって他の関数を呼び出し、パラメーターを渡し、関数の出力を返すことができます。You can use the context parameter DurableOrchestrationContext (.NET) and the context.df object (JavaScript) to invoke other functions by name, pass parameters, and return function output. コードで await (C#) または yield (JavaScript) が呼び出されるたびに Durable Functions フレームワークによって、現在の関数インスタンスの進行状況に対するチェックポイントが設定されます。Each time the code calls await (C#) or yield (JavaScript), the Durable Functions framework checkpoints the progress of the current function instance. プロセスまたは VM が実行途中でリサイクルされる場合、関数インスタンスは直前の await または yield 呼び出しから再開されます。If the process or VM recycles midway through the execution, the function instance resumes from the preceding await or yield call. 詳細については、次のセクション (パターン #2: ファンアウト/ファンイン) を参照してください。For more information, see the next section, Pattern #2: Fan out/fan in.

注意

JavaScript の context オブジェクトは、DurableOrchestrationContext パラメーターだけではなく、関数コンテキスト全体を表しています。The context object in JavaScript represents the entire function context, not only the DurableOrchestrationContext parameter.

パターン #2: ファンアウト/ファンインPattern #2: Fan out/fan in

ファンアウト/ファンイン パターンでは、複数の関数を並列で実行し、すべての関数が完了するまで待機します。In the fan out/fan in pattern, you execute multiple functions in parallel and then wait for all functions to finish. 複数の関数から返される結果に基づいて集計作業が行われることは、よくあることです。Often, some aggregation work is done on the results that are returned from the functions.

ファンアウト/ファンイン パターンの図

通常の関数では、複数のメッセージを 1 つのキューに送信することでファンアウトできます。With normal functions, you can fan out by having the function send multiple messages to a queue. ファンインして戻ることはずっと難しくなります。Fanning back in is much more challenging. 通常の関数でファンインするには、キューによってトリガーされた関数の終了を追跡した後、関数の出力を格納するコードを記述する必要があります。To fan in, in a normal function, you write code to track when the queue-triggered functions end, and then store function outputs.

Durable Functions 拡張機能では、比較的単純なコードでこのパターンを処理します。The Durable Functions extension handles this pattern with relatively simple code:

C# スクリプトC# script

public static async Task Run(DurableOrchestrationContext context)
{
    var parallelTasks = new List<Task<int>>();

    // Get a list of N work items to process in parallel.
    object[] workBatch = await context.CallActivityAsync<object[]>("F1");
    for (int i = 0; i < workBatch.Length; i++)
    {
        Task<int> task = context.CallActivityAsync<int>("F2", workBatch[i]);
        parallelTasks.Add(task);
    }

    await Task.WhenAll(parallelTasks);

    // Aggregate all N outputs and send the result to F3.
    int sum = parallelTasks.Sum(t => t.Result);
    await context.CallActivityAsync("F3", sum);
}

JavaScript (Functions 2.x のみ)JavaScript (Functions 2.x only)

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

module.exports = df.orchestrator(function*(context) {
    const parallelTasks = [];

    // Get a list of N work items to process in parallel.
    const workBatch = yield context.df.callActivity("F1");
    for (let i = 0; i < workBatch.length; i++) {
        parallelTasks.push(context.df.callActivity("F2", workBatch[i]));
    }

    yield context.df.Task.all(parallelTasks);

    // Aggregate all N outputs and send the result to F3.
    const sum = parallelTasks.reduce((prev, curr) => prev + curr, 0);
    yield context.df.callActivity("F3", sum);
});

ファンアウト作業は、F2 関数の複数のインスタンスに分散されます。The fan-out work is distributed to multiple instances of the F2 function. 動的タスク リストを使用して、この作業が追跡されます。The work is tracked by using a dynamic list of tasks. .NET Task.WhenAll API または JavaScript context.df.Task.all API が呼び出され、呼び出された関数がすべて終了するまで待機します。The .NET Task.WhenAll API or JavaScript context.df.Task.all API is called, to wait for all the called functions to finish. その後、F2 関数の出力が動的タスク リストから集計され、F3 関数に渡されます。Then, the F2 function outputs are aggregated from the dynamic task list and passed to the F3 function.

Task.WhenAll または context.df.Task.allawait 呼び出しまたは yield 呼び出しの際に設定される自動チェックポイントによって、実行途中でクラッシュや再起動が発生した場合でも、既に完了したすべてのタスクをやり直す必要がなくなります。The automatic checkpointing that happens at the await or yield call on Task.WhenAll or context.df.Task.all ensures that a potential midway crash or reboot doesn't require restarting an already completed task.

パターン #3: 非同期 HTTP APIPattern #3: Async HTTP APIs

非同期 HTTP API パターンでは、外部クライアントとの間の実行時間の長い操作の状態を調整するという問題に対処します。The async HTTP APIs pattern addresses the problem of coordinating the state of long-running operations with external clients. このパターンを実装する一般的な方法は、HTTP 呼び出しによって実行時間の長いアクションをトリガーすることです。A common way to implement this pattern is by having an HTTP call trigger the long-running action. その後、ポーリングによって操作が完了したことを認識できる状態エンドポイントにクライアントをリダイレクトします。Then, redirect the client to a status endpoint that the client polls to learn when the operation is finished.

HTTP API パターンの図

Durable Functions には、実行時間の長い関数とやり取りするためのコードをシンプルにする組み込みの API が用意されています。Durable Functions provides built-in APIs that simplify the code you write to interact with long-running function executions. Durable Functions のクイック スタート サンプル (C#JavaScript) に、新しいオーケストレーター関数のインスタンスを開始するために使用できる、シンプルな REST コマンドが示されています。The Durable Functions quickstart samples (C# and JavaScript) show a simple REST command that you can use to start new orchestrator function instances. インスタンスが開始されると、この拡張機能によって、オーケストレーター関数の状態をクエリする Webhook HTTP API が公開されます。After an instance starts, the extension exposes webhook HTTP APIs that query the orchestrator function status.

次の例は、オーケストレーターを開始し、その状態をクエリする REST コマンドを示しています。The following example shows REST commands that start an orchestrator and query its status. わかりやすくするため、この例では細部をいくらか省略しています。For clarity, some details are omitted from the example.

> curl -X POST https://myfunc.azurewebsites.net/orchestrators/DoWork -H "Content-Length: 0" -i
HTTP/1.1 202 Accepted
Content-Type: application/json
Location: https://myfunc.azurewebsites.net/admin/extensions/DurableTaskExtension/b79baf67f717453ca9e86c5da21e03ec

{"id":"b79baf67f717453ca9e86c5da21e03ec", ...}

> curl https://myfunc.azurewebsites.net/admin/extensions/DurableTaskExtension/b79baf67f717453ca9e86c5da21e03ec -i
HTTP/1.1 202 Accepted
Content-Type: application/json
Location: https://myfunc.azurewebsites.net/admin/extensions/DurableTaskExtension/b79baf67f717453ca9e86c5da21e03ec

{"runtimeStatus":"Running","lastUpdatedTime":"2017-03-16T21:20:47Z", ...}

> curl https://myfunc.azurewebsites.net/admin/extensions/DurableTaskExtension/b79baf67f717453ca9e86c5da21e03ec -i
HTTP/1.1 200 OK
Content-Length: 175
Content-Type: application/json

{"runtimeStatus":"Completed","lastUpdatedTime":"2017-03-16T21:20:57Z", ...}

状態は Durable Functions ランタイムによって管理されるため、自分で状態追跡メカニズムを実装する必要はありません。Because the Durable Functions runtime manages state, you don't need to implement your own status-tracking mechanism.

Durable Functions 拡張機能には、長時間実行されるオーケストレーションを管理する Webhook が組み込まれています。The Durable Functions extension has built-in webhooks that manage long-running orchestrations. 独自の関数トリガー (HTTP、キュー、Azure Event Hubs など) と orchestrationClient バインドを使用して、このパターンを自分で実装できます。You can implement this pattern yourself by using your own function triggers (such as HTTP, a queue, or Azure Event Hubs) and the orchestrationClient binding. たとえば、キュー メッセージを使用して終了をトリガーできます。For example, you might use a queue message to trigger termination. または、生成されたキーを認証で使用する組み込みの Webhook の代わりに、Azure Active Directory の認証ポリシーによって保護される HTTP トリガーを使用できます。Or, you might use an HTTP trigger that's protected by an Azure Active Directory authentication policy instead of the built-in webhooks that use a generated key for authentication.

HTTP API パターンを使用する方法の例をいくつか次に示します。Here are some examples of how to use the HTTP API pattern:

C#C#

// An HTTP-triggered function starts a new orchestrator function instance.
public static async Task<HttpResponseMessage> Run(
    HttpRequestMessage req,
    DurableOrchestrationClient starter,
    string functionName,
    ILogger log)
{
    // The function name comes from the request URL.
    // The function input comes from the request content.
    dynamic eventData = await req.Content.ReadAsAsync<object>();
    string instanceId = await starter.StartNewAsync(functionName, eventData);

    log.LogInformation($"Started orchestration with ID = '{instanceId}'.");

    return starter.CreateCheckStatusResponse(req, instanceId);
}

JavaScript (Functions 2.x のみ)JavaScript (Functions 2.x only)

// An HTTP-triggered function starts a new orchestrator function instance.
const df = require("durable-functions");

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

    // The function name comes from the request URL.
    // The function input comes from the request content.
    const eventData = req.body;
    const instanceId = await client.startNew(req.params.functionName, undefined, eventData);

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

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

.NET における DurableOrchestrationClient starterパラメーターは、Durable Functions 拡張機能の一部である orchestrationClient 出力バインドからの値です。In .NET, the DurableOrchestrationClient starter parameter is a value from the orchestrationClient output binding, which is part of the Durable Functions extension. JavaScript では、df.getClient(context) を呼び出すことによって、このオブジェクトが返されます。In JavaScript, this object is returned by calling df.getClient(context). これらのオブジェクトには、新しいまたは既存のオーケストレーター関数インスタンスの開始、インスタンスへのイベントの送信、インスタンスの停止、およびインタンスに対するクエリの送信を行うためのメソッドがあります。These objects provide methods you can use to start, send events to, terminate, and query for new or existing orchestrator function instances.

上記の例では、HTTP によってトリガーされる関数が、受信 URL から functionName 値を受け取り、その値を StartNewAsync に渡します。In the preceding examples, an HTTP-triggered function takes in a functionName value from the incoming URL and passes the value to StartNewAsync. 次に CreateCheckStatusResponse バインド API が、Location ヘッダーとインスタンスの追加情報を含む応答を返します。The CreateCheckStatusResponse binding API then returns a response that contains a Location header and additional information about the instance. この情報を後で使用して、開始されたインスタンスの状態を調べたり、インスタンスを終了したりできます。You can use the information later to look up the status of the started instance or to terminate the instance.

パターン #4: 監視Pattern #4: Monitor

監視パターンは、ワークフロー内の柔軟な繰り返しプロセスを指します。The monitor pattern refers to a flexible, recurring process in a workflow. 一例は、特定の条件が満たされるまでポーリングすることです。An example is polling until specific conditions are met. 通常のタイマー トリガーを使用して、定期的なクリーンアップ ジョブなどの基本的なシナリオに対処できますが、その間隔は静的であり、インスタンスの有効期間の管理は複雑になります。You can use a regular timer trigger to address a basic scenario, such as a periodic cleanup job, but its interval is static and managing instance lifetimes becomes complex. Durable Functions を使用して、柔軟な繰り返し間隔の作成、タスクの有効期間の管理、単一のオーケストレーションからの複数の監視プロセスの作成を実行できます。You can use Durable Functions to create flexible recurrence intervals, manage task lifetimes, and create multiple monitor processes from a single orchestration.

監視パターンの一例は、前述の非同期 HTTP API シナリオを逆にすることです。An example of the monitor pattern is to reverse the earlier async HTTP API scenario. 外部クライアント用のエンドポイントを公開して実行時間の長い操作を監視する代わりに、長時間実行される監視で外部エンドポイントを使用して、状態が変化するまで待機します。Instead of exposing an endpoint for an external client to monitor a long-running operation, the long-running monitor consumes an external endpoint, and then waits for a state change.

監視パターンの図

数行のコードで、Durable Functions を使用して任意のエンドポイントを観察する複数のモニターを作成できます。In a few lines of code, you can use Durable Functions to create multiple monitors that observe arbitrary endpoints. 条件が満たされたときにこれらのモニターによって実行を終了するか、DurableOrchestrationClient によってモニターを終了できます。The monitors can end execution when a condition is met, or the DurableOrchestrationClient can terminate the monitors. 特定の条件に基づいてモニターの wait 間隔を変更できます (指数関数的バックオフなど)。You can change a monitor's wait interval based on a specific condition (for example, exponential backoff.)

次のコードでは、基本的なモニターが実装されます。The following code implements a basic monitor:

C# スクリプトC# script

public static async Task Run(DurableOrchestrationContext context)
{
    int jobId = context.GetInput<int>();
    int pollingInterval = GetPollingInterval();
    DateTime expiryTime = GetExpiryTime();

    while (context.CurrentUtcDateTime < expiryTime)
    {
        var jobStatus = await context.CallActivityAsync<string>("GetJobStatus", jobId);
        if (jobStatus == "Completed")
        {
            // Perform an action when a condition is met.
            await context.CallActivityAsync("SendAlert", machineId);
            break;
        }

        // Orchestration sleeps until this time.
        var nextCheck = context.CurrentUtcDateTime.AddSeconds(pollingInterval);
        await context.CreateTimer(nextCheck, CancellationToken.None);
    }

    // Perform more work here, or let the orchestration end.
}

JavaScript (Functions 2.x のみ)JavaScript (Functions 2.x only)

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

module.exports = df.orchestrator(function*(context) {
    const jobId = context.df.getInput();
    const pollingInternal = getPollingInterval();
    const expiryTime = getExpiryTime();

    while (moment.utc(context.df.currentUtcDateTime).isBefore(expiryTime)) {
        const jobStatus = yield context.df.callActivity("GetJobStatus", jobId);
        if (jobStatus === "Completed") {
            // Perform an action when a condition is met.
            yield context.df.callActivity("SendAlert", machineId);
            break;
        }

        // Orchestration sleeps until this time.
        const nextCheck = moment.utc(context.df.currentUtcDateTime).add(pollingInterval, 's');
        yield context.df.createTimer(nextCheck.toDate());
    }

    // Perform more work here, or let the orchestration end.
});

要求が受信されると、そのジョブ ID 用の新しいオーケストレーション インスタンスが作成されます。When a request is received, a new orchestration instance is created for that job ID. インスタンスは、条件が満たされてループが終了するまで、状態をポーリングします。The instance polls a status until a condition is met and the loop is exited. ポーリング間隔は、永続タイマーによって制御されます。A durable timer controls the polling interval. その後、さらに作業を実行するか、オーケストレーションを終了できます。Then, more work can be performed, or the orchestration can end. context.CurrentUtcDateTime (.NET) または context.df.currentUtcDateTime (JavaScript) が expiryTime を超えると、モニターは終了します。When the context.CurrentUtcDateTime (.NET) or context.df.currentUtcDateTime (JavaScript) exceeds the expiryTime value, the monitor ends.

パターン #5: 人による操作Pattern #5: Human interaction

多くの自動化されたプロセスには、何らかの人による操作が含まれます。Many automated processes involve some kind of human interaction. 自動化されたプロセスに人による操作が含まれる場合に問題になるのが、人は必ずしもクラウド サービスのように可用性と応答性が高くないということです。Involving humans in an automated process is tricky because people aren't as highly available and as responsive as cloud services. 自動化されたプロセスでは、タイムアウトと補正ロジックを使用して、これを許容する必要があります。An automated process might allow for this by using timeouts and compensation logic.

承認プロセスは、人による操作を含むビジネス プロセスの一例です。An approval process is an example of a business process that involves human interaction. 特定の金額を超えた経費報告書は、上司の承認が必要な場合があります。Approval from a manager might be required for an expense report that exceeds a certain dollar amount. 上司が (休暇中などで) その経費報告書を 72 時間以内に承認しない場合は、他の誰か (おそらくは上司の上司) から承認を得るためにエスカレーション プロセスを開始します。If the manager doesn't approve the expense report within 72 hours (maybe the manager went on vacation), an escalation process kicks in to get the approval from someone else (perhaps the manager's manager).

人による操作が含まれるパターンの図

オーケストレーター関数を使用して、この例のパターンを実装できます。You can implement the pattern in this example by using an orchestrator function. オーケストレーターが永続タイマーを使用して承認を要求します。The orchestrator uses a durable timer to request approval. オーケストレーターは、タイムアウトが発生した場合はエスカレーションを行います。The orchestrator escalates if timeout occurs. オーケストレーターは、外部イベント (人による操作によって生成される通知など) が発生するまで待機します。The orchestrator waits for an external event, such as a notification that's generated by a human interaction.

次の例では、人による操作パターンを示すための承認プロセスを作成しています。These examples create an approval process to demonstrate the human interaction pattern:

C# スクリプトC# script

public static async Task Run(DurableOrchestrationContext context)
{
    await context.CallActivityAsync("RequestApproval");
    using (var timeoutCts = new CancellationTokenSource())
    {
        DateTime dueTime = context.CurrentUtcDateTime.AddHours(72);
        Task durableTimeout = context.CreateTimer(dueTime, timeoutCts.Token);

        Task<bool> approvalEvent = context.WaitForExternalEvent<bool>("ApprovalEvent");
        if (approvalEvent == await Task.WhenAny(approvalEvent, durableTimeout))
        {
            timeoutCts.Cancel();
            await context.CallActivityAsync("ProcessApproval", approvalEvent.Result);
        }
        else
        {
            await context.CallActivityAsync("Escalate");
        }
    }
}

JavaScript (Functions 2.x のみ)JavaScript (Functions 2.x only)

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

module.exports = df.orchestrator(function*(context) {
    yield context.df.callActivity("RequestApproval");

    const dueTime = moment.utc(context.df.currentUtcDateTime).add(72, 'h');
    const durableTimeout = context.df.createTimer(dueTime.toDate());

    const approvalEvent = context.df.waitForExternalEvent("ApprovalEvent");
    if (approvalEvent === yield context.df.Task.any([approvalEvent, durableTimeout])) {
        durableTimeout.cancel();
        yield context.df.callActivity("ProcessApproval", approvalEvent.result);
    } else {
        yield context.df.callActivity("Escalate");
    }
});

永続タイマーを作成するために、context.CreateTimer (.NET) または context.df.createTimer (JavaScript) が呼び出されます。To create the durable timer, call context.CreateTimer (.NET) or context.df.createTimer (JavaScript). 通知は context.WaitForExternalEvent (.NET) または context.df.waitForExternalEvent (JavaScript) が受け取ります。The notification is received by context.WaitForExternalEvent (.NET) or context.df.waitForExternalEvent (JavaScript). その後、エスカレーションする (タイムアウトが先に発生した場合) か承認を処理する (タイムアウト前に承認を得た場合) かを決定するために、Task.WhenAny (.NET) または context.df.Task.any (JavaScript) が呼び出されます。Then, Task.WhenAny (.NET) or context.df.Task.any (JavaScript) is called to decide whether to escalate (timeout happens first) or process the approval (the approval is received before timeout).

外部クライアントは、組み込みの HTTP API を使用するか、別の関数からの DurableOrchestrationClient.RaiseEventAsync API を使用して、待機中のオーケストレーター関数にイベント通知を配信できます。An external client can deliver the event notification to a waiting orchestrator function by using either the built-in HTTP APIs or by using the DurableOrchestrationClient.RaiseEventAsync API from another function:

public static async Task Run(string instanceId, DurableOrchestrationClient client)
{
    bool isApproved = true;
    await client.RaiseEventAsync(instanceId, "ApprovalEvent", isApproved);
}
const df = require("durable-functions");

module.exports = async function (context) {
    const client = df.getClient(context);
    const isApproved = true;
    await client.raiseEvent(instanceId, "ApprovalEvent", isApproved);
};

パターン #6:アグリゲーター (プレビュー)Pattern #6: Aggregator (preview)

6 番目のパターンは、ある期間のイベント データを 1 つのアドレス可能なエンティティ に集計することに関連しています。The sixth pattern is about aggregating event data over a period of time into a single, addressable entity. このパターンでは、集計されるデータは、複数のソースから取得されるか、バッチで配信されるか、または長期間にわたって分散される可能性があります。In this pattern, the data being aggregated may come from multiple sources, may be delivered in batches, or may be scattered over long-periods of time. アグリゲーターがイベント データの到着時にイベント データに対してアクションを行ったり、外部クライアントが集計されたデータをクエリする必要が生じたりする場合があります。The aggregator might need to take action on event data as it arrives, and external clients may need to query the aggregated data.

アグリゲーターの図

このパターンを通常のステートレス関数で実装しようとする際に注意が必要な点は、同時実行制御が大きな課題となることです。The tricky thing about trying to implement this pattern with normal, stateless functions is that concurrency control becomes a huge challenge. 複数のスレッドが同時に同じデータを変更することに注意する必要があるだけでなく、アグリゲーターが一度に 1 つの VM 上でのみ実行されるようにすることも注意する必要があります。Not only do you need to worry about multiple threads modifying the same data at the same time, you also need to worry about ensuring that the aggregator only runs on a single VM at a time.

Durable Entity 関数を使用すれば、このパターンを 1 つの関数として簡単に実装できます。Using a Durable Entity function, one can implement this pattern easily as a single function.

public static async Task Counter(
    [EntityTrigger(EntityClassName = "Counter")] IDurableEntityContext ctx)
{
    int currentValue = ctx.GetState<int>();
    int operand = ctx.GetInput<int>();

    switch (ctx.OperationName)
    {
        case "add":
            currentValue += operand;
            break;
        case "subtract":
            currentValue -= operand;
            break;
        case "reset":
            await SendResetNotificationAsync();
            currentValue = 0;
            break;
    }

    ctx.SetState(currentValue);
}

クライアントは、orchestrationClient バインディングを使用して、エンティティ関数の操作 をエンキューすることができます (「シグナル通知」とも呼ばれる)。Clients can enqueue operations for (also known as "signaling") an entity function using the orchestrationClient binding.

[FunctionName("EventHubTriggerCSharp")]
public static async Task Run(
    [EventHubTrigger("device-sensor-events")] EventData eventData,
    [OrchestrationClient] IDurableOrchestrationClient entityClient)
{
    var metricType = (string)eventData.Properties["metric"];
    var delta = BitConverter.ToInt32(eventData.Body, eventData.Body.Offset);

    // The "Counter/{metricType}" entity is created on-demand.
    var entityId = new EntityId("Counter", metricType);
    await entityClient.SignalEntityAsync(entityId, "add", delta);
}

同様に、クライアントは、orchestrationClient バインディングのメソッドを使用して、エンティティ関数の状態をクエリできます。Similarly, clients can query for the state of an entity function using methods on the orchestrationClient binding.

注意

エンティティ関数は、現在 Durable Functions 2.0 プレビューでのみ使用できます。Entity functions are currently only available in the Durable Functions 2.0 preview.

テクノロジThe technology

Durable Functions 拡張機能の背後には、永続的なタスクのオーケストレーションを構築するために使用される GitHub のオープン ソース ライブラリである Durable Task Framework があり、この拡張機能はその上に構築されています。Behind the scenes, the Durable Functions extension is built on top of the Durable Task Framework, an open-source library on GitHub that's used to build durable task orchestrations. Azure Functions が Azure WebJobs のサーバーレスな進化形であるのと同じように、Durable Functions は Durable Task Framework のサーバーレスな進化形です。Like Azure Functions is the serverless evolution of Azure WebJobs, Durable Functions is the serverless evolution of the Durable Task Framework. Microsoft や他の組織は、ミッション クリティカルなプロセスを自動化するために、Durable Task Framework を広範囲にわたって使用しています。Microsoft and other organizations use the Durable Task Framework extensively to automate mission-critical processes. サーバーレスな Azure Functions 環境には自然に適合します。It's a natural fit for the serverless Azure Functions environment.

イベント ソーシング、チェックポイント設定、リプレイEvent sourcing, checkpointing, and replay

オーケストレーター関数は、イベント ソーシング設計パターンを使用して、この関数の実行状態を確実に管理します。Orchestrator functions reliably maintain their execution state by using the event sourcing design pattern. Durable Functions 拡張機能では、オーケストレーションの現在の状態を直接格納する代わりに追加専用ストアを使用して、関数オーケストレーションが実行するすべてのアクションが記録されます。Instead of directly storing the current state of an orchestration, the Durable Functions extension uses an append-only store to record the full series of actions the function orchestration takes. 追加専用ストアには、実行時のすべての状態の "ダンプ" に比べて、多くのメリットがあります。An append-only store has many benefits compared to "dumping" the full runtime state. メリットには、パフォーマンス、スケーラビリティ、および応答性の向上が含まれます。Benefits include increased performance, scalability, and responsiveness. トランザクション データの最終的な一貫性、完全な監査証跡、および監査履歴も取得できます。You also get eventual consistency for transactional data and full audit trails and history. 監査証跡では、信頼性の高い補正アクションがサポートされます。The audit trails support reliable compensating actions.

Durable Functions では、イベント ソーシングが透過的に使用されます。Durable Functions uses event sourcing transparently. バックグラウンドで、オーケストレーター関数内の await (C#) または yield (JavaScript) 演算子によって、オーケストレーター スレッドのコントロールが Durable Task Framework ディスパッチャーに戻されます。Behind the scenes, the await (C#) or yield (JavaScript) operator in an orchestrator function yields control of the orchestrator thread back to the Durable Task Framework dispatcher. 次に、このディスパッチャーは、オーケストレーター関数がスケジュールした新しいアクション (1 つ以上の子関数を呼び出す、永続タイマーをスケジュールする、など) をストレージにコミットします。The dispatcher then commits any new actions that the orchestrator function scheduled (such as calling one or more child functions or scheduling a durable timer) to storage. この透過的なコミット アクションが、オーケストレーション インスタンスの実行履歴に追加されます。The transparent commit action appends to the execution history of the orchestration instance. この履歴はストレージ テーブルに格納されます。The history is stored in a storage table. 次に、このコミット アクションはキューにメッセージを追加して実際の作業をスケジュールします。The commit action then adds messages to a queue to schedule the actual work. この時点では、メモリからオーケストレーター関数をアンロードできます。At this point, the orchestrator function can be unloaded from memory.

オーケストレーター関数に対する課金は、Azure Functions の従量課金プランを使用している場合はここで停止します。Billing for the orchestrator function stops if you're using the Azure Functions consumption plan. 処理すべき作業がさらにある場合は、関数が再起動され、関数の状態が再構築されます。When there's more work to do, the function restarts, and its state is reconstructed.

オーケストレーション関数にさらに処理すべき作業 (応答メッセージの受信や永続タイマーの終了など) が与えられると、オーケストレーターが起動し、関数全体が始めから再実行され、ローカルの状態が再構築されます。When an orchestration function is given more work to do (for example, a response message is received or a durable timer expires), the orchestrator wakes up and re-executes the entire function from the start to rebuild the local state.

このリプレイ中にコードが関数を呼び出そうとする (または他の非同期作業を行おうとする) と、Durable Task Framework によって現在のオーケストレーションの実行履歴が確認されます。During the replay, if the code tries to call a function (or do any other async work), the Durable Task Framework consults the execution history of the current orchestration. アクティビティ関数が既に実行され、いくつかの結果が生成されていることがわかった場合は、関数の結果をリプレイし、オーケストレーター コードの実行を続行します。If it finds that the activity function has already executed and yielded a result, it replays that function's result and the orchestrator code continues to run. リプレイは、関数コードが完了するまで、または新しい非同期作業がスケジュールされるまで続行されます。Replay continues until the function code is finished or until it has scheduled new async work.

オーケストレーター コードの制約Orchestrator code constraints

オーケストレーター コードのリプレイ動作によって、オーケストレーター関数に記述できるコードの種類に制約が生まれます。The replay behavior of orchestrator code creates constraints on the type of code that you can write in an orchestrator function. たとえば、オーケストレーター コードは複数回リプレイされるため、確定的である必要があり、毎回同じ結果を出す必要があります。For example, orchestrator code must be deterministic because it will be replayed multiple times, and it must produce the same result each time. 制約の完全な一覧については、オーケストレーター コードの制約に関する記事を参照してください。For the complete list of constraints, see Orchestrator code constraints.

監視と診断Monitoring and diagnostics

Durable Functions 拡張機能では、関数アプリが Azure Application Insights インストルメンテーション キーを使用して設定される場合、構造化された追跡データを Application Insights に自動的に出力します。The Durable Functions extension automatically emits structured tracking data to Application Insights if you set up your function app with an Azure Application Insights instrumentation key. この追跡データを使用して、オーケストレーションのアクションと進行状況を監視できます。You can use the tracking data to monitor the actions and progress of your orchestrations.

Application Insights Analytics を使用したときに、Application Insights ポータル内で Durable Functions の追跡イベントがどのように表示されるかの例を次に示します。Here's an example of what the Durable Functions tracking events look like in the Application Insights portal when you use Application Insights Analytics:

Application Insights クエリ結果

各ログ エントリの customDimensions フィールドで、有用な構造化データを見つけることができます。You can find useful structured data in the customDimensions field in each log entry. 完全に展開されたエントリの例を次に示します。Here's an example of an entry that is fully expanded:

Application Insights クエリの customDimensions フィールド

Durable Task Framework ディスパッチャーのリプレイ動作のおかげで、リプレイされたアクションの豊富なログ エントリを確認できます。Because of the replay behavior of the Durable Task Framework dispatcher, you can expect to see redundant log entries for replayed actions. 冗長なログ エントリは、コア エンジンのリプレイ動作を理解するのに役立ちます。Redundant log entries can help you understand the replay behavior of the core engine. 診断に関する記事に、リプレイ ログをフィルターで除外するサンプル クエリが示されています。それによって "リアルタイム" ログだけを確認できます。The Diagnostics article shows sample queries that filter out replay logs, so you can see just the "real-time" logs.

ストレージとスケーラビリティStorage and scalability

Durable Functions 拡張機能では、Azure Storage のキュー、テーブル、および BLOB を使用して実行履歴の状態を保持し、関数の実行をトリガーします。The Durable Functions extension uses queues, tables, and blobs in Azure Storage to persist execution history state and trigger function execution. 関数アプリの既定のストレージ アカウントを使用するか、別のストレージ アカウントを構成できます。You can use the default storage account for the function app, or you can configure a separate storage account. ストレージのスループット制限に基づいて、別のアカウントを使用することもできます。You might want a separate account based on storage throughput limits. 記述するオーケストレーター コードは、これらのストレージ アカウント内のエンティティとやりとりをすることはありません。The orchestrator code you write doesn't interact with the entities in these storage accounts. Durable Task Framework では、エンティティは実装の詳細として直接管理されます。The Durable Task Framework manages the entities directly as an implementation detail.

オーケストレーター関数はアクティビティ関数をスケジュールし、内部キュー メッセージを介して応答を受け取ります。Orchestrator functions schedule activity functions and receive their responses via internal queue messages. 関数アプリが Azure Functions の従量課金プランで実行されている場合、これらのキューは Azure Functions Scale Controller によって監視されます。When a function app runs in the Azure Functions consumption plan, the Azure Functions scale controller monitors these queues. 必要に応じて、新しいコンピューティング インスタンスが追加されます。New compute instances are added as needed. 複数の VM にスケールアウトされた場合、オーケストレーター関数は 1 つの VM 上で実行され、オーケストレーター関数によって呼び出されるアクティビティ関数は別の VM で実行されることがあります。When scaled out to multiple VMs, an orchestrator function might run on one VM, while activity functions that the orchestrator function calls might run on several different VMs. Durable Functions のスケーリング動作の詳細については、パフォーマンスとスケールに関する記事をご覧ください。For more information about the scale behavior of Durable Functions, see Performance and scale.

オーケストレーター アカウントの実行履歴は、テーブル ストレージに格納されます。The execution history for orchestrator accounts is stored in table storage. インスタンスが特定の VM 上に復元される場合、オーケストレーターは、テーブルから実行履歴をフェッチして、そのローカル状態を再構築できるようにします。Whenever an instance rehydrates on a particular VM, the orchestrator fetches its execution history from table storage so it can rebuild its local state. テーブル ストレージの履歴を使用できることの便利な面は、Microsoft Azure Storage Explorer などのツールを使用してオーケストレーションの履歴を確認できることです。A convenient aspect of having the history available in table storage is that you can use tools like Azure Storage Explorer to see the history of your orchestrations.

Storage BLOB は、主にオーケストレーション インスタンスの複数の VM 間のスケールアウトを調整するためのリース メカニズムとして使用されます。Storage blobs are primarily used as a leasing mechanism to coordinate the scale-out of orchestration instances across multiple VMs. Storage BLOB は、テーブルまたはキューに直接保存することができない大きなメッセージのデータを保持するために使用されます。Storage blobs hold data for large messages that can't be stored directly in tables or queues.

Azure Storage Explorer のスクリーンショット

注意

テーブル ストレージで実行履歴を確認できるのは簡単で便利ですが、このテーブルには依存関係を作成しないでください。Although it's easy and convenient to see execution history in table storage, don't make any dependencies on this table. このテーブルは、Durable Functions 拡張機能の発展に伴って変更される可能性があります。The table might change as the Durable Functions extension evolves.

既知の問題Known issues

すべての既知の問題は、GitHub の Issue 一覧で追跡されています。All known issues should be tracked in the GitHub issues list. 問題が発生したときに、その問題が GitHub で見つからない場合は、新しい Issue を開いてください。If you run into a problem and can't find the issue in GitHub, open a new issue. 問題の詳細な説明を含めてください。Include a detailed description of the problem.

次の手順Next steps

Durable Functions の詳細については、Durable Functions の関数の種類と機能に関する記事を参照してください。To learn more about Durable Functions, see Durable Functions function types and features.

作業を開始するには:To get started: