3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Azure Functions】【C#】インスタンスが実行中に入れ替わることがある

Posted at

ごく普通のBlobトリガーを作成します。

public static class BlobTriggerHoge {
    private static ILogger logger;
    private static Foo foo;

    [FunctionName("BlobTriggerHoge")]
    public static void Run(
        [BlobTrigger("container/{fileName}")] BlockBlobClient myBlobClient,
        string fileName,
        ILogger log,
    ) {
        logger = log;
    	foo = new Foo(fileName);
    	a();
    	a();
    	a();
    }

    public static void a() {
    	logger.LogInformation($"fileName:{foo.fileName} hashCode:{foo.GetHashCode()}");
    }
}

class Foo{
    public string fileName;
    public Foo(string fileName) {
    	this.fileName = fileName;
    }
}

同じログを3回出しているだけに見えますね。

ところがこのFoo、たまに途中から別のインスタンスになることがあります。

[2024-01-01T00:00:00.001Z] fileName:hoge.png hashCode:123456789
[2024-01-01T00:00:00.002Z] fileName:hoge.png hashCode:123456789
[2024-01-01T00:00:00.003Z] fileName:fuga.png hashCode:987654321

なんで????????????

大量のファイルを同時にアップロードして、BLOBトリガーが同時に複数起動したときに起きたり起きなかったりします。

対策

Fooクラスに状態を持たせない。

よし、staticおじさんになろう。

原因

いやまあよくあるstaticの罠なのですが、これAzure Fucntionsのドキュメントに従って作ってたら絶対引っかかるだろ。
なにせありとあらゆるドキュメントでstatic Run()とか書いてあるので、トリガーは静的でなければならないと思い込まされてしまうわけですよ。

実際はトリガーは静的でなくても動きます。
ドキュメントではたいへんひっそりと触れられていました。

https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-class-library?tabs=v4%2Ccmd
https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-dotnet-class-library?tabs=v4%2Ccmd

The above example shows a static method being used, but functions aren't required to be static.
上記の例では静的メソッドが使用されていますが、関数を静的にする必要はありません。

先に言えや。

ということでBlobTriggerHogeクラスからstaticを全部消して普通のクラス・インスタンスにしたところ、インスタンスが入れ替わることがなくなりました。

実のところはインスタンスが入れ替わっているのではなく書き替えられています。
private static Foo foofooは一見privateであり他所からは全く見えないようになっていると思えますが、実際は中身が複数のリクエストで共有されているみたいです。
そのせいで、トリガー1がhoge.pngを書きこんだ後でトリガー2がfuga.pngを書きこむと、その後はトリガー1からもfuga.pngになってしまうわけです。

え、そんなの常識だって?
そうですね。

感想

私はC#をAzure Functions上でしか使ったことがありません。
そもそもC#は必要にかられて仕方なくやっているだけなので、とりあえず動かすのに必要なところをつまみ食いしているせいで、体系的な学習を一切していません。
なので言語の基礎や定石や風習といったところが全然わかっていないんですよね。
そんなわけで見事にstaticの罠に引っかかってしまったという話でした。
PHPerはマルチスレッドに弱いのだ。

ところでこれ、static Run()としつつ正しい処理にするにはどうすればいいんですかね?

3
1
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?