1. Durable Functions

Durable Functions

Version:
日本語翻訳に関する免責事項

このページの翻訳はAIによって自動的に行われました。可能な限り正確な翻訳を心掛けていますが、原文と異なる表現や解釈が含まれる場合があります。正確で公式な情報については、必ず英語の原文をご参照ください。

概要

Durable Functions は、Azure Functions と Azure WebJobs の拡張機能であり、サーバーレス環境でステートフル関数を記述できます。 この拡張機能が管理するのは、状態、チェックポイント、および再起動です。

この拡張機能により、オーケストレーター関数と呼ばれる新しいタイプの関数でステートフル ワークフローを定義できます。 次に、オーケストレーター関数の利点をいくつか示します。

  • コードでワークフローを定義します。 JSON スキーマやデザイナーは必要ありません。
  • 他の関数を同期的および非同期的に呼び出すことができます。 呼び出された関数からの出力は、ローカル変数に保存できます。
  • 関数が待機するたびに、進行状況に自動的にチェックポイントを設定します。 プロセスがリサイクルされたり、VM が再起動したりしても、ローカルの状態が失われることはありません。

Durable Functions の主なユース ケースは、サーバーレス アプリケーションにおける複雑でステートフルな調整の問題を単純化することです。

先に進む前に、Microsoft Azure の公式ドキュメント ポータルで、このトピックを読んでおくことを強くお勧めします。

開始方法

以下の例は、Durable Function と HttpTrigger を組み合わせて使用する方法を示したものです。 以下の関数は POST ステートメントを介して呼び出され、DurableOrchestrationClient によって実行される関数の名前がルートの一部として渡されます。 重要: 以下のすべての関数は、同じ Function App の一部である必要があります。

たとえば、以下の JSON を http://myDurableFunctionUrl/api/orchestrators/LifeCycleSequence に送信すると、指定された ID に関連付けられているアセット エンティティがアーカイブされます。 渡すことができるその他のステータスは、「Approved」または「UnderReview」です。 指定する ID は、前の関数 (特定のアセットのセットをクエリし、その結果を HttpTrigger に渡す関数) の結果にすることもできます。

{
    "status": "Archived",
    "ids": [1111, 2222, 3333]
}
public static class HttpStart
{
    [FunctionName("HttpStart")]
    public static async Task<HttpResponseMessage> Run(
        [HttpTrigger(AuthorizationLevel.Function, methods: "post", Route = "orchestrators/{functionName}")] HttpRequestMessage req,
        [OrchestrationClient] DurableOrchestrationClient starter,
        string functionName,
        TraceWriter log)
    {
        // Extract request body
        var data = await req.Content.ReadAsStringAsync();

        // Invoke an OrchestrationTrigger associated with the functionName and pass along the request body
        var instanceId = await starter.StartNewAsync(functionName, data);

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

        // Return CheckStatusResponse
        return starter.CreateCheckStatusResponse(req, instanceId);
    }
}

DurableOrchestrationClient が新しい OrchestrationTrigger、この場合は LifeCycleSequence を開始します。 POST 要求に伴って発生する要求のペイロードは、DurableOrchestrationContext を介して OrchestrationTrigger に渡されます。 以下の例は、DurableOrchestrationContext を使用して、アクティビティと呼ばれる複数の関数をチェーン化する方法を示したものです。

例で使用されている 2 つのアクティビティは、GetLifeCycleStatusIdSetLifeCycleStatus です。

[FunctionName("LifeCycleSequence")]
public static async Task<List<string>> Run(
    [OrchestrationTrigger] DurableOrchestrationContext context)
{
    // Parse data
    var data = JObject.Parse(context.GetInput<string>());
    var status = data["status"].Value<string>();

    var output = new List<string>();

    // Retrieve lifecycle status id
    var lifeCycleId = await context.CallActivityAsync<long>("GetLifeCycleStatusId", status);
    output.Add($"Lifecycle id is {lifeCycleId}.");

    // Split ids in batches
    var ids = data["ids"].ToObject<List<long>>();
    var batches = ids.SplitIntoBatches(25);

    foreach (var batch in batches)
    {
        if (!batch.Any()) continue;

        // Process all entries in a single batch in parallel
        var parallelTasks = new List<Task<IList<string>>>();

        foreach (var id in batch)
        {
            var obj = new JObject()
            {
                new JProperty("lifeCycleId", lifeCycleId),
                new JProperty("id", id)
            };

            var value = JsonConvert.SerializeObject(obj);

            // Set LifeCycle Status
            var task = context.CallActivityAsync<IList<string>>("SetLifeCycleStatus", value);
            parallelTasks.Add(task);
        }

        // Execute tasks in parallel
        await Task.WhenAll(parallelTasks);

        // Gather output from executed tasks
        parallelTasks.Select(d => d.Result).ToList().ForEach(d => output.AddRange(d));
    }

    return output;
}

GetLifeCycleStatusId は 1 回だけ実行され、指定されたステータスに関連付けられているエンティティ ID をフェッチするために使用されます。 この ID は、最終的なライフサイクル ステータスをアセット レベルで設定するために必要です。

[FunctionName("GetLifeCycleStatusId")]
public static async Task<long> GetLifeCycleStatusId([ActivityTrigger] string name, TraceWriter log)
{
    MClient.Logger = new TraceWriterLogger(log);

    // Get final lifecycle status id for provided name
    var lifeCycleQuery = Query.CreateIdsQuery(entities =>
            (from e in entities
                where e.DefinitionName == "M.Final.LifeCycle.Status" && e.Property("StatusValue") == name
                select e).Take(1));

    var lifeCycleQueryResult = await MConnector.Client.Querying.Query(lifeCycleQuery);
    return lifeCycleQueryResult.Ids.First();
}

SetLifeCycleStatus は、アセット ID ごとに実行する必要があります。アセット ID は、JSON ペイロードで ID の配列を介して提供されます。 このプロセスを高速化するために、SetLifeCycleStatus 関数は複数のインスタンスを並行して実行できます。 Sitecore Content Hub API のオーバーロードを回避するために、並列タスクの実行はバッチに分割することをお勧めします。

[FunctionName("SetLifeCycleStatus")]
public static async Task<IList<string>> SetLifeCycleStatus([ActivityTrigger] string value, TraceWriter log)
{
    MClient.Logger = new TraceWriterLogger(log);

    var obj = JObject.Parse(value);
    var id = obj["id"].Value<long>();

    // Load the entity
    var entity = await MConnector.Client.Entities.Get(id);

    // Set the provided lifecycle status
    var lifeCycleId = obj["lifeCycleId"].Value<long>();
    var lifeCycleRelation = await entity.GetRelation("FinalLifeCycleStatusToAsset");
    await lifeCycleRelation.SetParentId(lifeCycleId);

    var result = new List<string>();
    result.Add($"Updating lifecycle for entity {entity.Resource.Id}.");

    try
    {
        // Save the entity
        await MConnector.Client.Entities.Update(entity);
    }
    catch (WebApiValidationException ex)
    {
        // Catch validation exceptions
        foreach (var failure in ex.Failures)
        {
            result.Add(string.Concat(failure.Key, " - ", failure.Message));
        }
    }

    return result;
}
この記事を改善するための提案がある場合は、 お知らせください!