フィルタと API スクリプトを最適化して Shotgun サイトを高速化する

Shotgun の柔軟性により、フィルタ パネルを介したページのカスタマイズと高度な API ベースの統合スクリプトの開発が可能になっています。この柔軟性により、直観的で間違いのない複雑なフィルタや高度なフィルタを簡単に作成できますが、実際のところパフォーマンスは低下し、大量のシステム リソースを消費します。

このドキュメントの目的は、特に API スクリプトを作成する Shotgun ユーザが、高速かつ効率的に複雑なフィルタを作成する方法を理解できるようにすることです。適切に作成されたフィルタのほとんどは一瞬で完了します。ただし、まったく同じデータを返すような非効率的なフィルタは時間がかかり、完了まで数分かかることもあります。

フィルタのパフォーマンスを最適化するには、次のように操作することをお勧めします。

  • データベースがロードおよび処理する必要のあるテーブル行の数を最小限に抑えます。
    • リンク フィールド、マルチエンティティ フィールド、タグ、URL フィールドなど他のテーブルに属するフィールドではなく、メイン エンティティのテーブルの列でフィルタを使用するのが最も効率的です。
  • テキスト一致フィルタは、低速であり、同じ目的に対して自身で使用できないため、使うのを避けます。

低速なフィルタの例

フィルタの例

上記のバージョン ページ フィルタの例は、次のような理由で動作が低速です。

  1. すべてのフィルタが「テキスト一致」フィルタ([名前に含まれる](name contains)、[含む](contains)、[ 以下で開始](starts with)など)であるため、データベースを評価する速度が低下します。
  2. これらのテキスト一致フィルタに照らしてチェックする必要のあるバージョンの数が膨大で、すべてのバージョンが対象になります。
  3. 多くのフィルタでリンク フィールドを対象にしているため、データベースがフィルタの評価のために他のテーブルからデータを取得する必要があります。

さらに、バージョン テーブルは最も高速で拡大することが多いテーブルであるため、このフィルタの速度は時間が経つにつれて徐々に低下します。

複雑なフィルタのパフォーマンスを向上させるためには、検討対象から多くのバージョンを効率的に除外できるフィルタを追加します。これにより、多数のバージョンに関して低速のテキスト一致クエリーを評価する必要がなくなります。

ユーザに最適なフィルタはワークフローによって異なります。ワークフローがテキスト一致フィルタまたはリンク フィールド フィルタに大きく依存する場合は、バージョンまたは他のエンティティを効率的にフィルタできるようにワークフローを微調整する必要があります。ワークフローを効率的に変更する例として、ステータス フィールドの活用があります。

フィルタの改善例

改善されたフィルタの例

ここでは、効率的な新しい 3 つのフィルタ グループを追加しています。この 3 つのフィルタによりスタジオの大部分のバージョンが除外されることで、この複雑なフィルタが改善されます。このため、パターン一致とリンク フィールドを使用する低速なフィルタを評価するバージョンはかなり少なくなります。

  • Project: 「is」フィルタを使用してプロジェクトを明示的に制限しています。これにより、これにあてはまらない過去と現在のすべてのプロジェクトからすべてのバージョンが効率的に除外されるため、スタジオに多くのプロジェクトがある場合は大きな違いが生まれます。この場合、このページのユーザがプロジェクトに関係なくすべての VFX 編集バージョンを収集するため、プロジェクトが最新の状態になるようにワークフローの変更が必要になることがあります。
  • Status: レビューが完了したバージョンのステータスを Pending Review から Viewed に更新すると、優れた「ベスト プラクティス」になります。これにより、バージョン ページをフィルタして、関連する少数の新しいバージョンのみを簡単に表示できます。
  • [作成日](Date Created)(または[更新日](Date Updated)): すべてのエンティティが持つこのフィールドは、ページ コンテンツを効率的にフィルタして減らすのに便利です。

フィルタ パフォーマンスの詳細

1. テキスト一致フィルタが低速である

テキスト一致フィルタが低速な理由は、結果の一部を含む可能性のある各テーブル行に対してデータベースが複雑なテストを実行する必要があるためです。このフィルタ タイプに利用できる最適化の方法はありません。

このガイドラインは、テキスト一致フィルタを使用してはならないということではなく、テキスト一致フィルタに加えて、結果を減らすためのより効率的なフィルタを別に使用する必要があるということを示しています(これが 2 つ目のガイドラインです。以下で詳しく検討します)。このように他のフィルタを使用すると、テキスト一致フィルタが適用されるテーブル行が減るため、リソースの消費も少なくなります。

次に、基本的に低速なテキスト一致フィルタを示します。

  • 含む
  • 含まない(not_contains)
  • 以下で開始(starts_with)
  • 以下で終了(ends_with)
  • 名前に含まれる(name_contains)
  • 名前に含まれない(name_not_contains)

次の場合、テキスト一致クエリーは特に低速になります。

  • エンティティ検索で適用される唯一のフィルタである
  • 「or」(「いずれか」)フィルタ グループに含まれる

この場合、結果数の削減のために別の効率的なフィルタ タイプを使用する複雑なフィルタよりも、何百倍または何千倍も遅くなる可能性があります。

API スクリプトでテキスト一致フィルタを使用していてパフォーマンスが不足している場合は、「統合アプローチを変更してテキスト一致を回避する」のセクションを参照することをお勧めします。

2. エンティティの独自フィールドで機能するシンプルで効率的なフィルタを使用して返されるエンティティ数を削減する

これは、複雑なフィルタのパフォーマンスを向上する上で最も重要な原則です。

エンティティの独自フィールドで効率的にフィルタを実行すると、データベースは検討対象からエンティティの大部分のテーブル行をすばやく除外できます。したがって、それ以降の手順でデータベースが処理するデータが少なくなります。その結果、他のフィルタ、ソート、またはグループ化など、複雑なフィルタの残りすべての処理が高速化するというメリットが生まれます。

最終的な結果数が少なくなると、API スクリプトまたは Shotgun ページに対する応答のデータ フォーマットとネットワークのリソース消費が少なくなり、さらにパフォーマンスが向上します。

この方法でパフォーマンスを効率的に向上できるフィルタの種類

  1. 効率的に「絞り込む」フィルタを使用すると、検討対象のエンティティ数を大幅に削減できる。
    100 万個のバージョンがある場合、8,000 個のバージョンしか含まれないプロジェクトをフィルタするほうが効率的です。反対に、Viewed のようなステータスをフィルタして 80 万個のバージョンが含まれる場合、パフォーマンスが向上する可能性はありません。
  2. フィルタが AND (「~のすべて」)フィルタ グループに含まれる必要がある。
    Python API でフィルタ条件の上位配列は AND グループです。次の例では、「entity」に対するフィルタで行が適切に削減されています。
    EFFICIENT_filters = [[ "entity", "is", { "type": "Asset", "id": 9 } ]]

    ただし、フィルタが他のフィルタ グループ内にあるサブグループに含まれる場合、その上位のレベルで OR (「~のいずれか」)フィルタ グループ内に含まれないようにしてください。次の例では、「entity」に対するフィルタにより、パフォーマンスが向上しない可能性があります。
    SLOW_filters = [ 
      {
        "filter_operator":"or",
        "filters":[
          [ "code", "contains", "e" ],
          [ "entity", "is", { "type": "Asset", "id": 9 } ]
        ]
      }
    ]

    任意のレベルの OR グループに含まれるフィルタは、必ずしもすべてのテーブル行に適用されないため、検討対象の行数が確実に削減されるわけではありません。(例外については、以下の「高度なパフォーマンス」セクションを参照してください。)
  3. フィルタ フィールドがリンク フィールドではない。
    リンク フィールドにフィルタを適用すると、データベースが別のエンティティのテーブルの行を検索および評価する必要があるため、行をすばやく除外できません。リンク フィールドに対するフィルタが必要な場合は、行を効率的に除外するために、エンティティ自身に属するフィールドに対する追加のフィルタも使用してください。(例外については、以下の「高度なパフォーマンス」セクションを参照してください。)
  4. フィルタ フィールドのデータ タイプがマルチエンティティやタグ リストではない。
    マルチエンティティとタグのフィールドはリンク フィールドと似ているため高速ではありません。これらのフィールドにフィルタを適用すると、データベースで他のテーブルの行を評価する必要があるため、メイン テーブルから行を効率的に除外できません。
    マルチエンティティ フィールドやタグ フィールドにフィルタを適用することに何も問題はありませんが、パフォーマンスを向上するため、エンティティ数を効率的に削減できるフィルタを追加する必要があります。
    マルチエンティティ フィールドとは異なり、シングルエンティティ フィールドは高速で、効率的な「絞り込み」フィルタになります。これはメイン エンティティ テーブルに属しているためです。
  5. フィルタがテキスト一致フィルタ タイプではない。
    テキスト一致フィルタは低速で、データベースで適切に最適化できません。
    [等しい](is)、[等しくない](is_not)、[より大きい](greater than)、[より後](is after)、[__週後に含まれる](in the next __ weeks)など、他のすべてのフィルタ タイプは、フィルタが膨大な数の行を除外する限り、比較的効率的に動作します。

次に、多くのバージョンを含むサイトではパフォーマンスが不足する可能性のあるフィルタを示します。

SLOW_filters = [
  {
    "filter_operator":"or",
    "filters":[
      [ "code", "contains", "_rev_" ],
      [ "code", "contains", "_redo_" ]
    ]
  }
]

行を除外できる可能性がある明確な方法は、プロジェクトまたは日付の範囲を制限することです。次に、両方の場合を試した例を示します。

上部レベルのフィルタは OR (「いずれか」)フィルタです。新しい絞り込みフィルタは OR フィルタ グループ外になるようにしてください。

EFFICIENT_filters = [
  [ "project", "is", { "type": "Project", "id": 49 } ],
  [ "updated_at", "in_last", [ 1, "WEEK" ] ],
  {
    "filter_operator":"or",
    "filters":[
      [ "code", "contains", "_rev_" ],
      [ "code", "contains", "_redo_" ]
    ]
  }
]

データベース インデックスのメリット

一部のフィールドはデータベースにインデックス化されているため、特に効率的なフィルタリングを提供できます。インデックス化されたフィールドをフィルタすると、データベースがインデックス化されたフィールドの値の効率的なハッシュ インデックスを参照して、検討対象の行を特定できます。

注: インデックス化されたフィールドを使用しても、指定した他のフィルタ ガイドラインに従わない限り、効果はまったくありません。目的は処理する行の数を効率的に削減することで、インデックス化されたフィールドを使用すると便利です。

データベースのインデックス化により、データベースからの読み込みのパフォーマンスは向上しますが、書き込みごとにインデックスを更新する必要があるため、データベースへの書き込み速度は低下します。したがって、Shotgun はすべてのテーブルのすべてのフィールドをインデックス化するわけではありません。

エンティティ タイプの場合、既定では、Shotgun は次の共通フィールドをインデックス化します。

  • Id (id)
  • Project (project)
  • Status (sg_status_list)
  • Date Created (created_at)
  • Date Updated (updated_at)

[等しい](is)や、[より大きい](greater than)など、上記のフィールドのいずれかを使用する肯定的なフィルタは、インデックス化されていないフィールドのフィルタよりもかなり迅速に検討対象から行を除外することで、パフォーマンスが大幅に向上する場合があります。

is not などの否定的なフィルタは、データベースによるインデックスの使用を通常は許可していません。ただし、否定的なフィルタが処理の必要な行数を大幅に削減する限り、パフォーマンスの向上に役立ちます。

インデックスの効果の例として、API イベント ポーリング スクリプトはイベント ログ エントリ ID フィールドでフィルタを使用して、イベント ログ エントリ テーブルの行を小数の新しい行だけに制限します(「Id is greater than N」など)。イベント ログ エントリ テーブルが最大のテーブルでも、このフィルタは非常に大きなクライアント データベースで非常に高速に動作します。

ただし、データベースは複雑なヒューリスティックと統計情報を使用し、インデックスを適用するタイミングと使用するインデックスを決定するためにインデックスごとに保持されます。その結果、インデックス化されたこのフィールドのいずれかでフィルタを使用すると、パフォーマンス向上のタイミングを予想するのが難しくなります。

そのため、必ずしもインデックス化されたフィールドに対するフィルタでなくても、検討対象の行数を大幅に削減する効率的なフィルタを提供することが重要です。インデックス化されたフィールドの使用に余分な手間をかけないでください。実際は手元の状況に適していない可能性があります。

3. リンク フィールドではフィルタごとのコストとフィルタされた行ごとのコストが累積される

リンク フィールドは、他のテーブルの行を評価するためにオーバーヘッドが少し追加されますが、特に低速なわけではありません。

メイン エンティティ テーブルから多くの行を除外する効率的なフィルタが存在する場合、パフォーマンスに大きな悪影響を与えずに、複雑なフィルタでリンク フィールドに他のフィルタを適用できる可能性があります。

その一方で、評価するテーブル行の数が多い場合は、リンク フィールドでフィルタ、ソート、またはグループ化を実行するたびに、複雑なフィルタに大幅なコストが追加されます。加えて、「ダブル ホップ」リンク フィールドは多くのテーブルの行を評価する必要があるため、さらに大きな影響があります。

統合アプローチを変更してテキスト一致を回避する

API 統合が、2 番目のガイドラインに従って高速タイプのフィルタを適用せずにテキスト一致フィルタまたはリンク フィールドのフィルタに大きく依存している場合、制作活動の増加に合わせてその統合を拡大できない可能性があります。エンティティ自身に属するフィールドの完全一致に依存する手法を探す必要があります。いくらかの作業が必要になったとしても、効果が得られます。たとえば、ステータスの自動更新や、完全一致の値を使用できる新しいリストやテキスト フィールドの追加や設定が必要になる場合があります。

次に、仮定のアニメーション ワークフローに含まれるレポートの PublishedFile アニメーション ファイル タイプを特定するためにテキスト一致に依存している API サマリー フィルタの例を示します。

filters = [
  {
    "filter_operator":"or","filters":[
      [ "path_cache", "contains", "/anim_face" ],
      [ "path_cache", "contains", "/anim_fine" ]
    ]
  }
]
summaries = sg.summarize(entity_type='PublishedFile', filters, summary_fields=[{'field':'id', 'type':'count'}])

プロジェクト、日付範囲、または PublishedFileType を制限するなど、このフィルタを制限できる可能性は数多くあります。

ただし、特定のアニメーションのファイル タイプの識別がワークフローで利用可能な唯一の方法の場合、リソース タイプに新しいカスタム リスト列(カスタム パブリッシュ スクリプトまたは他の方法で常に設定される)を追加すると、パフォーマンスが向上します。ここで、「My Resource Subtype」と呼ばれるフィールドを使用すると、フィルタは次のようになります。

filters = [
  {
    "filter_operator":"or",
    "filters":[
      [ "sg_my_resource_subtype", "is", "anim_face" ],
      [ "sg_my_resource_subtype", "is", "anim_fine" ]
    ]
  }
]
summaries = sg.summarize(entity_type='PublishedFile', filters, summary_fields=[{'field':'id', 'type':'count'}])

anim_face と anim_fine の PublishedFiles が非常に多く存在する場合はフィルタが低速になる可能性がありますが、以前のテキスト一致バージョンよりもはるかに高速になります。(この OR (「~のいずれか」)フィルタによってパフォーマンスが向上する理由が分からない場合は、以下の「高度なパフォーマンス」セクションを参照してください。)

高度なパフォーマンスの注意事項

OR (「~のいずれか」)グループを使用する

実際には、OR グループでいくつかのフィルタを組み合わせると、テーブル行が効率的に除外されるため、複雑なフィルタのパフォーマンスが十分に向上します。これは、特定のフィルタとそのフィルタによりテーブル行がどれだけ除外されるかに応じて異なります。上記の「統合アプローチを変更してテキスト一致を回避する」セクションの例を参照してください。

リンク フィールドを使用する

実際には、エンティティ フィールドを介してリンク フィールドでフィルタを使用すると、適切な結果が得られることがあります。これは、リンク フィールドがリンク フィールドの値にエンティティ タイプを指定するためです。エンティティ フィールドを介してリンクされたフィルタは、リンク エンティティ タイプに属するエンティティ フィールドの値が非常に少ない場合、親エンティティ テーブルのほとんどの行を除外できます。これはマルチエンティティ フィールドには適用されません。

次に、タスクのフィルタ例を示します。

filters = [[ "entity.Sequence.sg_status_list", "is", "hld" ]]

たとえば、「エンティティ」フィールドにシーケンスが格納されたタスクが非常に少ない場合、上記のタスク用のフィルタが実際は適切に動作することがあります。これは、エンティティ列にシーケンスが格納された、少数のタスク行しか処理されないためです。

一方で、何百万ものタスクがシーケンスにリンクされている場合は、この数百万ものタスク行を処理する必要があるため、これらのフィルタのパフォーマンスは低下します。

Shotgun を最大限活用する方法の詳細については、「ベスト プラクティスのチェックリスト」を参照してください。

フォローする

0 コメント

ログインしてコメントを残してください。