Java系エンジニアがチャレンジする Salesforce「Apexのstaticはstatic じゃない?」-東京システムリサーチ

こんにちは。東京システムリサーチBigDataソリューション部の深澤です。現役のSE(システムエンジニア)です。そんな私が、日々のIT業務で感じたことをつらつらと記載していきます。なお、内容についてはあくまでも深澤個人の意見なので、その点はご了承ください。

では、さっそく本題です。

以前、Salesforce Apexでのstatic変数の扱いで「本当に?」と思うことがありました。

まずは、経緯から話します。Salesforce開発でこんな要件に対応する場面がありました。「あるユーザはオブジェクトのレコードを削除できないようにしたい。」という内容でした。権限セットかなと、最初は思ったのですが、「レコード削除して良い場面と、レコード削除をしてはいけない場面とを細かく制御したい」ということでした。つまり、ある画面では削除OKで、ある画面では削除NGにするイメージです。

Salesforceで上記のような要件に対応する場合、トリガー設定を駆使して対応するのですが、ここである疑問が生じました。どの画面がレコード削除をキックしたのか、トリガー側でどうやって察知するのでしょう?

幸いにして解決方法はすぐに分かりました。先人が気の利いた解決方法を持っていました。会社に Salesforce開発経験者がいると、悩んだ時に助かります。

「static変数にフラグ立てれば良いですよ。トリガーキックする前に画面ごとにtrueやfalseをセットしておくのです。」

static変数を使う?すかさず質問しました。

「static変数を使ったら、スレッドセーフ(複数のスレッドが同時並行的に実行しても問題が発生しないこと)にならないのでは?」

すると、こんな回答が返って来ました。

「大丈夫です!Salesforceのstatic変数はトランザクションごとなので!」

公式のドキュメントを調べてみました。結論は回答の通り、static変数はトランザクションごとってヘルプに書いてありました。

Apex 開発者ガイド「静的メソッドとインスタンスメソッド、変数、初期化コード」

静的変数は、Apexトランザクションの範囲内でのみ静的です。サーバ全体または組織全体で静的なわけではありません。静的変数の値は、1回のトランザクションのコンテキスト内で保持され、トランザクションの境界を超えたときにリセットされます。たとえば、Apex DML要求によってトリガーが何回も起動される場合、これらのトリガー呼び出しを通して静的変数は保持されます。 初めて知りました。

でも、それは、static(静的)ではないのでは?!

staticとは、日本語で「静的な」という意味です。例えば Javaの場合、staticに宣言された変数は、JVM が再起動されない限りずっとメモリー上に常駐して値を保持し続けます。文字通り「静的な」変数です。 この「常に値を保持し続ける」というのが厄介な時があります。例えばJava Servletのようなマルチタスクで動作する場合、同じstatic変数にアクセスするメソッドが同時並行して動いてしまうことがあります。すると、片方のメソッドではそのstatic変数が「true」であることを期待して動いているのに、他のメソッドが同じstatic変数を「false」に変更できるとなると、途端に矛盾が生じて、再現性の低いバグを生み出し、解析が困難となるケースがあります。

よって、Java系エンジニアは static変数をあまり使わないようにすることが多いです。かっこいい言い方をすると、「スレッドセーフな書き方に徹する」とも言えます。マルチスレッドで動いても矛盾が起きないようにしておくのです。

一方、Salesforceではstatic変数のスコープがトランザクションごとというのは、それは「静的」ではなくて「動的」に近い概念です。

どちらかというと、JavaでいうとSession変数みたいな概念です。正確に言うとSession変数よりもっとスコープが狭いので、名前を付けるとするとトランザクション変数みたいな名前かなと思います。私が文法を決められるとしたらキーワードは「トランザクション」がぴったりかなと思います。

と、以上のような書き込みを社内の掲示板でしたところ、気の利いた返しがありました。

トランザクションごとにJVMインスタンスを立ち上げていると予想してみる。

なるほど!リクエストが来るたびにサービスを立ち上げる!CGI (Common Gateway Interface)のようです!(←古い)

最近のアーキテクチャー(構造・構成)だとAWS Lambdaに近い概念とも言えます。cloudの進化のおかげで、その都度VMインスタンスを起動するのもそれほど処理コストがかからなくなってきたということもあるかもしれません。Salesforceも似たような仕組みで実装されているのでしょう。推測になりますが、static周りの文法が決まった後に、サービスがLambdaのように(イベントが起きたら、プログラムが実行される)動くようになったのではないかと思います。そのまま文法が残っている状態になっているので、ヘルプに少しだけ記載されている形なのだと思います。

今まで書いてきたように、Java系エンジニアにとってSalesforceの文法にはJavaとは違う概念があるので、開発の際は十分に気を付けなければならない、という開発現場で経験した話でした。