BEACHSIDE BLOG

Azure と GitHub と C# が好きなエンジニアの個人メモ ( ・ㅂ・)و ̑̑

Durable Functions で instanceId を確認したい (C# / Typescript / Python)

Durable Functions でたまに instanceId を取得したいときがあります。例えば Activity function で external Event の URL を作りたいときとか。私的にはあまり使わない Durable functions の Python ではどうだっけって気になったので、C# と Typescript も一緒にメモしておきます。

C#

まずは C# からです。

C# > Starter

以下のコードは Http の Starter のテンプレートのコードです。ここでは instanceId を自動生成させるか自分で作るかってところになります。

  • StartNewAsync メソッドの第二引数でパラメーターをセットしない場合は、instanceId が自動生成されます。
  • StartNewAsync メソッドの第二引数でパラメーターをセットした場合は、それが instanceId となります。外部で instanceId を生成してここでも使いたいときは使える感じです。
[FunctionName("Function1_HttpStart")]
public static async Task<HttpResponseMessage> HttpStart(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestMessage req,
    [DurableClient] IDurableOrchestrationClient starter,
    ILogger log)
{
    var instanceId = await starter.StartNewAsync("Function1");
    //  第二引数にセットするとその値が instanceId となる。
    // var instanceId = await starter.StartNewAsync("Function1", "なんらかの ID");

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

    return starter.CreateCheckStatusResponse(req, instanceId);
}

C# > Orchestrator

Orchestrator は IDurableOrchestrationContext のプロパティから取得が可能です。

public static async Task<List<string>> RunOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var instanceId = context.InstanceId;
// 以下省略

C# > Activity

以下はテンプレートからコードを生成した際の Activiy trigger のコードです。[ActivityTrigger] の直後に適当な型を定義することで、Orchestrator からのパラメーターを受け取ることができるコードになっています。

[FunctionName(nameof(SayHello))]
public static string SayHello([ActivityTrigger] string name, ILogger log)
{
    log.LogInformation($"Saying hello to {name}.");
    return $"Hello {name}!";
}

これをちょっと変えていきます。

InstanceIdIDurableActivityContext が管理しているので、以下のようコードを変更することで IDurableActivityContext を受け取ることができます。そうすると Orchestrator からの渡されたパラメーターは context.GetInput<>() で取得するようになります。

InstanceId の取得とは全く無関係ですが、DurableClient のバインド方法も書きました。ブログ冒頭で instanceId を使うのが「external Event の URL を生成したいとき」って書いたのですが、それには DurableClient を使うのでついでに書きました。

[FunctionName(nameof(SayHello2))]
public static string SayHello2(
    [ActivityTrigger] IDurableActivityContext context,
    [DurableClient] IDurableOrchestrationClient orchestrationClient,
    ILogger log)
{
    var instanceId = context.InstanceId;
    var name = context.GetInput<string>();

    return $"Hello {name}-{instanceId}!";
}

Javascript (Typescript)

TS > Starter

以下のコードは Http の Starter のテンプレートのコードです。ここでは instanceId を自動生成させるか自分で作るかってところになります。

  • startNew メソッドの第二引数でパラメーターをセットしない場合は、instanceId が自動生成されます。
  • startNew メソッドの第二引数でパラメーターをセットした場合は、それが instanceId となります。外部で instanceId を生成してここでも使いたいときは使える感じです。
const httpStart: AzureFunction = async function (context: Context, req: HttpRequest): Promise<any> {
    const client = df.getClient(context);
    
    const instanceId = await client.startNew(req.params.functionName, undefined, req.body);
    context.log(`Started orchestration with ID = '${instanceId}'.`);

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

export default httpStart;

C# とほぼ同じこと書きましたね...

TS > Orchestrator

以下のコードは orchestrator function のテンプレートのコードです。
instanceIdcontext の中でいくつか存在していますが、context.bindingData.instanceId でとるのがよいかなと。理由は Activity Function の context と一緒の方法で取得できるのでってだけですが。

const orchestrator = df.orchestrator(function* (context) {
    // instanceId の取得
    context.log(`instanceId (orchestrator): ${context.bindingData.instanceId}`);

    context.log(`Orchestrator: instanceId: ${context.bindingData.instanceId}`)
    context.log(`Orchestrator: instanceId: ${context.bindings.context.instanceId}`)
    context.log(`Orchestrator: instanceId: ${context.bindingData.context.instanceId}`)
    return "Hello";
});

export default orchestrator;

TS > Activity

以下のコードは Activity function のテンプレートのコードです。
instanceId は context.bindingData.instanceId で取得できます。

const activityFunction: AzureFunction = async function (context: Context): Promise<string> {
    const instanceId = context.bindingData.instanceId;
    return instanceId ;
};

export default activityFunction;

ちなみに Typescript で durableClient を使うには、orchestrationClient or DurableClient を bind して df.getClient(instanceId) みたいな感じでやります (面倒でここではかかなかった...)。

Python

py > Starter

starter での instanceId の話は基本的に他の言語と一緒なので内容が冗長なので省略します。

py > Orchestrator

Orchestrator では他の言語同様 context から取得します。変数名は instance_id です。

def orchestrator_function(context: df.DurableOrchestrationContext):

    logging.info(f"instanceId: {context.instance_id}")

py > Activity

Node 同様に Python でもどうにかできるかと思いましたが、シンプルに関数の引数で instanceId 渡すのがよいのかなという結果に。

import azure.durable_functions as df して試行錯誤しようとおもったけど、いい感じに出来そうにないので諦めました。

まとめ

Activity や Orchestrator へパラメーターとして渡せば楽に解決するのでそれでも悪くないのですが、無理やりじゃないお作法でのやりかたをメモしておきましたが、Python の Activity function はチーンでした。

そんなことはさておき Durable Functions 便利なので使いどころもたくさんですが、なんとなく概要がわかったから実装してみようって段階になったら、まずこのドキュメント読みましょう...っていう本題と関係ないオチで今回は締めくくります。

learn.microsoft.com