2022/01/13

@GetDocField 関数を多用するフォームでパフォーマンス悪化の例

@GetDocField 関数を使って別の文書にあるフィールドの値を参照することがあります。

次のような感じです。

@GetDocField( UNID; "Subject" );

UNID には親文書の UNID がテキスト形式でセットされていたりします。

主要な値は親文書に保持しておき、子文書・孫文書には値を持たずに @GetDocField を使って親文書を参照する。こうすることで、主要な値を変えたい場合は親文書だけ変更すればよく、子文書・孫文書を変更する必要がなくなります。


ずいぶん前ですが @GetDocField を「使い過ぎちゃいます?」と言いたくなるほど多く使っているフォームがありました。少なくとも 50 のフィールドで使っていたと記憶しています。

リモートのアプリに対してアクセスする場合は @GetDocField は新たな NRPC コマンドを発行するんだろうなと想像できますが、このフォームでは何度も同じ文書を参照していたこともあって、次のどちらになるのか気になりました。

・NRPC コマンドは最初の1度だけ発行される?

・それともコマンドの数だけ発行される?


そこで notes.ini へ次の3行を設定して NRPC コマンドの記録を取って確認してみることにしました。

CLIENT_CLOCK=1
CONSOLE_LOG_ENABLED=1
DEBUG_THREADID=1

※Notes クライアントは 12.0.1 、Domino は 12.0.1 または 12.0 を使用してます。


まずは、1つの @GetDocField を式に設定したフィールドが1つだけあるフォームを開いてみました。すると console.log に次の1行が記録されました。

[7C8C:0002-5DB8] (5956-5335 [6435]) OPEN_NOTE_UNIDNAME_RQST(REP492587B3:0040D090-UNIDED7FB824:844179CF-492587B3:004126DE,03400000): 0 ms. [82+2546=2628]


OPEN_NOTE_UNIDNAME_RQST というNRPC コマンドが親文書の UNID を参照しています。時間は 1 ミリ秒より小さく、送信 82 バイト + 受信 2546 バイトであったことを示しています。なお、この環境は Notes クライアントと同じPC内で Domino サーバーが稼働しています。そのため時間は無視できるほど小さくなりました。


ところで Notes では、@DbLookup 関数は "Cache" というパラメータがあったり、一度読み込んだ文書(設計要素)をキャッシュして 2 度目以降は素早く開いたりと、キャッシュを上手に使うイメージがあります。


そこで、@GetDocField を同じ UNID に対して何度もコールすると 2 度目以降はどうなるか見ていきます。

@GetDocField を使用したフィールドをフォーム内に 4 つコピーして、合計 5 つのフィールドで同じ式を指定している状態にします。ここで期待するのは、1 度目の NRPC コマンド発行の後(クライアント側にキャッシュされたものを参照するため)2 度目の NRPC コマンドを発行しないことです。

このフォームで文書を開いてみたところ console.log には同じ UNID の文書を 5 回読み込んだことを示す、次の 5 行が記録されました。

[7C8C:0002-5DB8] (6950-6299 [7526]) OPEN_NOTE_UNIDNAME_RQST(REP492587B3:0040D090-UNIDED7FB824:844179CF-492587B3:004126DE,03400000): 1 ms. [82+2546=2628]

[7C8C:0002-5DB8] (6951-6299 [7527]) OPEN_NOTE_UNIDNAME_RQST(REP492587B3:0040D090-UNIDED7FB824:844179CF-492587B3:004126DE,03400000): 0 ms. [82+2546=2628]

[7C8C:0002-5DB8] (6952-6299 [7528]) OPEN_NOTE_UNIDNAME_RQST(REP492587B3:0040D090-UNIDED7FB824:844179CF-492587B3:004126DE,03400000): 1 ms. [82+2546=2628]

[7C8C:0002-5DB8] (6953-6299 [7529]) OPEN_NOTE_UNIDNAME_RQST(REP492587B3:0040D090-UNIDED7FB824:844179CF-492587B3:004126DE,03400000): 1 ms. [82+2546=2628]

[7C8C:0002-5DB8] (6954-6299 [7530]) OPEN_NOTE_UNIDNAME_RQST(REP492587B3:0040D090-UNIDED7FB824:844179CF-492587B3:004126DE,03400000): 1 ms. [82+2546=2628]

NRPC コマンドは、5 つのフィールドの式が同じ文書(かつ同じフィールド)を参照しているにも関わらず 5 回発行されることがわかりました。時間はほぼ同じで、送信と受信のバイト数も同じです。


次に、さきほどの
「5 つのフィールドに @GetDocField を 1 つずつ」を
「1 つのフィールドに @GetDocField を 5 つ」
に変更してみます。元からあるフィールドの式を次のように変更して、さきほど追加した 4 つのフィールドは削除します。

@GetDocField( UNID; "Subject" ) + @GetDocField( UNID; "Subject" ) + @GetDocField( UNID; "Subject" ) + @GetDocField( UNID; "Subject" ) + @GetDocField( UNID; "Subject" )

参照先の文書もフィールド名も同じ関数が 5 つあるという意味では、直前の実験と同じです。しかしながらこのフォームで文書を開いてみると、console.log には NRPC コマンドが 1 行しか記録されなかったのです。

[7C8C:0002-5DB8] (7343-6689 [8006]) OPEN_NOTE_UNIDNAME_RQST(REP492587B3:0040D090-UNIDED7FB824:844179CF-492587B3:004126DE,03400000): 1 ms. [82+2546=2628]


では、同じ文書にある別のフィールドを参照してみるとどうなるのでしょうか。

さきほどの式を少し書き換えて親文書にある 2 つのフィールド("Subject"と"Form")を参照させます。

@GetDocField( UNID; "Subject" ) + @GetDocField( UNID; "Form" ) + @GetDocField( UNID; "Subject" ) + @GetDocField( UNID; "Form" ) + @GetDocField( UNID; "Subject" )

このフォームで開いてみても、NRPC コマンドは 1 回発行されただけでした。

[7C8C:0002-5DB8] (8983-9032 [9719]) OPEN_NOTE_UNIDNAME_RQST(REP492587B3:0040D090-UNIDED7FB824:844179CF-492587B3:004126DE,03400000): 1 ms. [82+2546=2628]

異なるフィールドを参照してみましたが、受信のサイズは 1 つのフィールドの時と同じ 2546 バイトになりました。

受信のサイズが、参照するフィールド名に依存しないことを確かめるため、@GetDocField で参照している親文書へ 32,000 バイトの値を持つアイテムを新たに追加してみて、受信のサイズが変わるのかどうか試してみました。式の内容は変更せず、追加した 32,000 バイトのフィールド名は @GetDocField で指定していません。


フォームで開いた後 console.log を確認すると、受信サイズは 32,000 バイト+α 増加しました。※ちなみにこのサイズは「ネットワークデータの圧縮」を無効にした場合のものですが、有効化するとサイズは700バイト弱(2%程度)になりました

[2C78:0002-C228] (1162-1114 [1255]) OPEN_NOTE_UNIDNAME_RQST(REP492587B3:0040D090-UNIDED7FB824:844179CF-492587B3:004126DE,03400000): 0 ms. [82+34684=34766]

受信のサイズは、参照する文書が同じなら参照するフィールドによらず同じになりました。

@GetDocField で 1 バイトの値を持つフィールドを参照する場合でも、他のフィールドを含むサイズのデータを受信することがわかりました。

ちなみにこの受信サイズですが、文書のプロパティに表示されるサイズとは異なります。上のプロパティ画面をよく見ると $FILE アイテムがありますが、実はサイズが 200 メガバイト以上もあるファイルを添付しています。にもかかわらず受信サイズは 34684 バイトでした。添付へアクセスした場合は GETOBJECT_RQST といった別の NRPC コマンドが発行されますが、今回のケースではそのコマンドが発行された形跡はありません。


これまでクライアント側で見てきましたが、文書のキャッシュはサーバー側で行われる場合もあります。HCL Ambassador 特典で無償貸与されている、クラウド上の Domino サーバーに @GetDocField を使用した(上で使用したものとは別の)アプリケーションを設置してアクセスしてみました。ネットワークの遅延などの影響を加えることでコマンドの時間を大きくし、よりわかりやすくするためです。ここで NRPC コマンドの時間に注目してみます。

初回 466 ms、2 回目以降 310 ms前後でした。最初のコールは 2 回目以降よりも少し遅い結果となりました。初回のコマンド実行によってアプリケーション内を検索してヒットした親文書がDomino側でキャッシュされ、2 回目以降のコールではキャッシュが参照されたように思います。※この時間は「ネットワークデータの圧縮」を有効にしても10%未満短縮されただけでした

検証の結果をまとめます。

  • @GetDocField を使用するフィールドが多数あると、NRPC コマンドはフィールドの数だけ発行された。
  • 1 つのフィールド内に複数の @GetDocField をまとめて使用すると、NRPC コマンドは1度だけ発行された。
  • @GetDocField で受信するデータのサイズは、指定するフィールドによらず同じだった。
  • 同一文書を参照する場合の NRPC コマンドの時間は、初回のコールでDomino側に文書がキャッシュされたため、2 回目以降のコールでは短縮された。

なお、メーカーサポートに聞いたところ、異なるフィールドにある式から @GetDocField で同一文書を参照する場合にクライアント側へ明示的にキャッシュするようなオプションは現状では無いとのこと。


これらを踏まえると、同一文書を参照する @GetDocField を多数使いたい場合、なるべく 1 つのフィールド内でまとめて使うことが良い、と言えます。


下のサンプルでは「temp」というフィールドの式で@GetDocField で参照した値を別のフィールドへセットすることを繰り返し行っています。


このサンプルでは「temp」フィールドの式で @GetDocField を 61 回使用していますが、実行してみたところ NRPC コマンドの発行は 1 度だけでした。NRPC コマンドの発行回数が抑えられたことで、フォームを開く際のパフォーマンスを改善できました。
※あくまで @GetDocField を使うことにこだわった改善例ですが、改善の方法は他にもありそうです


以上の結果を踏まえ、仕様面での改善依頼をアイデアポータルへ投稿しました。


@GetDocField は、「のの会」の話題にもありましたが、一つの使い方の例は、@Picklist([CUSTOM]) 関数から戻すカラムの値を文書の UNID にしておき、その UNID を @GetDocField に指定することで、@Picklist で選択した文書にある複数のフィールド値にアクセスすることができる、というものでした。

この話題がのの会で上がっている時、過去に気になっていたものが何なのか思い出せなくて実はモヤモヤしていたのですが、先日同様のフォームを見かけてようやく思い出すことができました。


それから今回の検証で久しぶりに体感したことがありました。それは NRPC コマンドの時間ですが、Notes クライアントと同じ PC 内に構築した Domino サーバーへのアクセスでは初回も 2 回目以降も同じ 1 ms程度の値を示していました。ところがアプリケーションをクラウド上にある Domino サーバーへ移設した後、ビューでダブルクリックして文書が開くまでの時間が明らかに遅くなったのです。このときの NRPC コマンドの記録が上で示したスクリーンショットですが、@GetDocField を使うフィールドが 61 個あるフォームを開くのに 20 秒弱でした。移設前はサクッと開いていたのですが、速いネットワークにすっかり慣れてしまっていたせいか、今後 HCL Nomad などで多用されると思われるインターネット越しの Domino へのアクセスでここまで遅くなった事実に愕然としました。

@GetDocField を使ったフィールドが多数あるフォームを含むアプリケーションを、狭帯域だったり遅延の大きいネットワーク越しにある Domino サーバーに置く場合も想定して開発しなきゃならないなと思いました。

0 件のコメント:

コメントを投稿