外部関数のベストプラクティス¶
このトピックでは、効率を向上させ、リモートサービスがSnowflakeと互換性があるように設計されていない場合に発生する可能性のある、予期しない結果を防ぐためのベストプラクティスについて説明します。
次のドキュメントで追加のベストプラクティスを見つけることができます。
AzureFunctionsのパフォーマンスと信頼性を最適化する
(これはMicrosoft Azureドキュメントですが、その中のアドバイスの多くは、あらゆるクラウドプラットフォーム上のリモートサービスに適用されます。)
このトピックの内容:
可能な場合は、リモートサービスのバッチ API を使用¶
一部のリモートサービスでは、バッチモードと単一行モードの両方を提供します。外部関数を使用するクエリが複数の行を送信することが予想される場合、Snowflakeは、パフォーマンスを向上させるためにリモートサービスのバッチモードを使用することをお勧めします。
次の場合は、このルールが必ずしも適用されるとは限りません。
各行が非常に大きい(例: 数百キロバイト以上)。
リモートサービスは、行をバッチで受信する場合と個別に受信する場合で、行の処理方法が異なります。(詳細については、 1度に1行ずつ処理 を参照。)
1度に1行ずつ処理¶
ネットワークのオーバーヘッドを最小限に抑えるために、Snowflakeは通常、行をバッチ処理してリモートサービスに送信します。バッチの数と各バッチのサイズは異なる場合があります。
さらに、バッチの順序、およびバッチ内の行の順序は異なる場合があります。クエリに ORDER BY 句が含まれている場合でも、通常、 ORDER BY は外部関数が呼び出された後に適用されます。
バッチサイズと行の順序は保証されないため、このバッチまたは以前のバッチの他の行に依存する行の値を返す関数を作成すると、非決定的な結果が生成される可能性があります。
Snowflakeは、リモートサービスにおいて各行を個別に処理することを強くお勧めします。各入力行の戻り値は、他の入力行ではなく、その入力行のみに依存する必要があります。(たとえば現在、外部関数は ウィンドウ関数 をサポートしていません。)
また、バッチサイズは保証されていないため、バッチのカウントは意味がありません。
外部関数がステートレスであることを確認 もご参照ください。
リモートサービスが各行に1回だけ渡されると思い込まない¶
Snowflakeがリモートサービスを呼び出し、リモートサービスがリクエストを受信して結果を返したが、一時的なネットワークの問題によりSnowflakeが結果を受信しない場合、Snowflakeはリクエストを繰り返す可能性があります。Snowflakeが再試行すると、リモートサービスは同じ行を2回(またはそれ以上)表示する可能性があります。
これにより、予期しない影響が生じる可能性があります。たとえば、同じ値に対してリモートサービスが複数回呼び出される可能性があるため、一意の IDs を割り当てるリモートサービスでは、それらの IDs のシーケンスにギャップが生じる場合があります。場合によっては、リクエストヘッダーの sf-external-function-query-batch-id
フィールドのバッチ ID を追跡して、特定の行のバッチが以前に処理されたかどうかを判断することで、このような影響を減らすことができます。Snowflakeが特定のバッチのリクエストを再試行すると、Snowflakeは同じバッチに対して以前と同じバッチIDを使用します。
Snowflakeは、次のエラーを受け取ったときに再試行します。
すべての一時的なネットワーク転送エラー。
429ステータスコードで失敗するすべてのリクエスト。
5XXステータスコードで失敗するすべてのリクエスト。
再試行タイムアウトの合計に達するまで、要求は再試行されます。ユーザーは再試行タイムアウトの合計を構成できません。Snowflakeは将来この制限を調整する可能性があります。
再試行が成功せずに合計再試行タイムアウトに達すると、クエリは失敗します。
リモートサービスが機能しているときに外部関数呼び出しがタイムアウトし、Snowflakeとリモートサービス間のすべての要素が機能しているように見える場合は、バッチサイズを小さくして、タイムアウトエラーが減少するかどうかを試してください。
最大バッチサイズを設定する方法については、 CREATE EXTERNAL FUNCTION をご覧ください。
外部関数がステートレスであることを確認¶
一般的に、外部関数(リモートサービスを含む)は次の両方の状態情報の保存を回避する必要があります。
内部状態(リモートサービスが内部的に保存する状態)。
外部状態(リモートサービスの外部に保存されている状態。たとえば、状態を保持する別のリモートサービスとの間で送受信される状態情報)。
リモートサービスが状態情報を変更し、その情報を使用して将来の出力に影響を与える場合、関数は予想とは異なる値を返す可能性があります。
たとえば、内部カウンターを含み、リモートサービスが最初に開始されてから受信した行数を返す単純なリモートサービスを考えます。一時的なネットワークの問題があり、Snowflakeが同じデータで要求を繰り返す場合、リモートサービスは再送信された行を2回(またはそれ以上)カウントします。
外部状態を含む例については、 副作用の回避 をご参照ください。
関数がステートレスではないまれなケースでは、呼び出し元のドキュメントにおいて、関数がステートレスではなく、関数が揮発性としてマークされている必要があることを明確に示す必要があります。
リモートサービスがリクエストを 非同期に 処理する場合、リモートサービスの作成者は、ある状態を一時的に保存および管理するためにリモートサービスを作成する必要があります。たとえば、リモートサービスはHTTP POSTリクエストのバッチIDを保存する必要があります。これにより、同じバッチIDでHTTP GETを受信した場合、リモートサービスは指定されたバッチが処理中でもHTTPコード202を返すことができます。
クエリはさまざまな理由で中止される可能性があることに注意してください。つまり、リモートサービスが結果の生成を終了した後、最終的なGETが到着する保証はありません。非同期リクエストの状態を保存するリモートサービスは、最終的にタイムアウトになり、その内部状態をクリーンアップする必要があります。最適なタイムアウトは将来変更される可能性がありますが、Snowflakeは、非同期リクエストに関する情報を削除する前に少なくとも10分、できれば12時間保存することを現在推奨しています。
副作用の回避¶
外部関数(リモートサービスを含む)では、外部状態(リモートサービスの外部に保存されている情報)の変更などの副作用を回避する必要があります。
たとえば、リモートサービスが範囲外の値を政府機関に報告する場合、それは副作用です。
副作用は有用ですが、外部関数を呼び出すことによる副作用は常に予測できるとは限りません。たとえば、匿名化されたヘルスレコードを分析して診断を返すリモートサービスを呼び出すとします。また、診断が患者に伝染病があることが診断された場合、その診断はその疾患の症例数を記録する機関に報告されると仮定します。これは便利な副作用です。ただし、次のような問題に対して脆弱です。
外部関数呼び出しがロールバックされるトランザクション内にある場合、副作用はロールバックされません。
リモートサービスが同じ行で複数回呼び出された場合(例:一時的なネットワーク障害と再試行が原因)、副作用が複数回発生する可能性があります。たとえば、感染した患者は統計で2回カウントされる可能性があります。
行が過大にカウントされるのではなく、過小にカウントされる状況もあります。
関数に副作用がある非常にまれなケースでは、呼び出し元のドキュメントに副作用が何であるかを明確に記述し、関数に揮発性のマークを付ける必要があります。
関数を揮発性または不変として分類¶
関数は、揮発性または不変に分類できます。(CREATE EXTERNAL FUNCTION ステートメントを使用すると、関数の揮発性または不変の指定が可能。)
外部関数が不変であると見なされるには、次の基準を満たす必要があります。
同じ入力値が指定された場合、関数は同じ出力値を返します。(たとえば、 SQRT 関数は同じ入力が与えられたときに同じ出力を返しますが、 CURRENT_TIMESTAMP 関数は同じ入力が与えられたときに必ずしも同じ出力を返すとは限らない。)
この関数には副作用がありません。(詳細については、 副作用の回避 を参照。)
関数がこれらの2つの基準を満たす場合、Snowflakeは特定のタイプの最適化を使用して、リモートサービスに送信される行またはバッチの数を減らすことができます。(これらの最適化は時間とともに進化する可能性があるため、詳細の説明を省略。)
Snowflakeは、不変性、または不変性に影響を与える要因(たとえば、副作用)を検出または強制できません。リモートサービスの作成者は、リモートサービスが不変のラベルを付ける基準を満たすかどうかを文書化する必要があります。リモートサービスに副作用がある場合は、関数呼び出しが同じ入力値に対して同じ出力値を返す場合でも、そのリモートサービスを呼び出す外部関数を揮発性としてマークする必要があります。リモートサービスが不変であるかどうかわからない場合は、そのリモートサービスを呼び出す外部関数に揮発性のラベルを付ける必要があります。
タイムアウトエラーの説明¶
外部関数呼び出しには、Snowflake、リモートサービス、プロキシサービス、および場合によってはチェーン内の他の要素が含まれます。これらの要素ではいずれも、特定の関数呼び出しにかかる時間を把握していないため、待機を停止してタイムアウトエラーを返すタイミングを正確に把握できません。各ステップには、独自のタイムアウトがあります。タイムアウトと再試行の詳細については、 タイムアウトエラーの説明 および 再試行 をご参照ください。
遅延を最小化する¶
遅延を最小限に抑え、外部関数呼び出しのパフォーマンスを向上させるために、Snowflakeは可能であれば以下を実行することをお勧めします。
API Gatewayを最も頻繁に(または最大量のデータで)呼び出すSnowflakeインスタンスと同じクラウドプラットフォームおよび地域に、Gatewayを配置します。
リモートサービスを作成した場合は(既存のサービスを使用するのではなく)、呼び出し元と同じクラウドプラットフォームと地域でそのリモートサービスを展開します。
できるだけ少ないデータを送信します。たとえば、リモートサービスが入力値を調べてそれらのサブセットのみを操作する場合、通常、すべての行をリモートサービスに送信するよりも、 SQL でフィルタリングして関連する行のみをリモートサービスに送信する方が効率的です。リモートサービスを使用して、フィルタリングします。
別の例として、大きな半構造化データ値を含む列を処理していて、リモートサービスはこれらの各データ値のごく一部のみを使用する場合、通常は、処理の前に列全体を送信してリモートサービスにより小さな断片を抽出するよりも、Snowflake SQL を使用して関連する部分のみを抽出して送信する方が効率的です。
一度に1ステップずつ外部機能を開発およびテストする¶
Snowflakeでテストする前に、Snowflakeなしでテストすることをお勧めします。
外部関数の開発の初期ステージで、クラウドプラットフォームプロキシサービスコンソール(例: Amazon API Gatewayコンソール)およびリモートサービス開発コンソール(例: AWS Lambdaコンソール)を使用して、プロキシサービスとリモートサービスの開発とテストを支援します。
例えば、Lambda関数を開発した場合は、Snowflakeから呼び出してテストする前に、Lambdaコンソールで広範囲にテストすることができます。
プロキシサービスコンソールとリモートサービスコンソールによるテストには、通常、次の利点があります。
問題の原因を探す箇所が限られるため、問題の診断が容易になります。
データペイロードを表示すると、有用なデバッグ情報が得られる場合があります。Snowflakeでは、データペイロードのどの部分もエラーメッセージに表示しません。これによりセキュリティが強化されますが、デバッグが遅くなる可能性があります。
Snowflakeは HTTP 5xxエラーの場合に自動で再試行するため、状況によってはデバッグが遅くなったり困難になったりする可能性があります。
Snowflakeによるテストでは、クラウドプラットフォームクレジットに加えて、Snowflakeクレジットが使用されます。
もちろん、リモートサービスとプロキシサービスをSnowflakeなしでできる限りテストした後は、Snowflakeでテストする必要があります。Snowflakeでテストする利点は次のとおりです。
外部関数に関連するすべてのステップをテストしています。
データソースとしてSnowflakeテーブルを使用すると、大量のデータで簡単にテストして、外部関数のパフォーマンスの現実的な見積もりを取得できます。
次のテストケースを検討します。
NULL 値。
「空」の値(たとえば、空の文字列、空の半構造化データ型)。
非常に長い VARCHAR および BINARY 値(該当する場合)。
リモートサービスを非同期化¶
リモートサービスを作成していて、リモートサービスが予想されるタイムアウト内に結果を返さない可能性がある場合は、リモートサービスを 非同期 にすることを検討してください。詳細については、 非同期リモートサービス対同期リモートサービス をご参照ください。
外部関数の引数がリモートサービスにより解析される引数に対応していることを確認¶
外部関数との間で引数を渡すときは、データ型が適切であることを確認してください。送信された値が受信中のデータ型に適合しない場合、値が切り捨てられるか、破損する可能性があり、あるいはリモートサービスの呼び出しが失敗する可能性があります。
たとえば、一部のSnowflake SQL 数値データ型は、一般的に使用される JavaScript データ型よりも大きな値を保存できるため、 JSON からの大きな数値の逆シリアル化は JavaScript で特に敏感です。
リモートサービスへの引数の数、データ型、または順序を変更する場合は、外部関数に対応する変更を加えることを忘れないでください。現在、 ALTER FUNCTION コマンドにはパラメーターを変更するオプションがないため、引数を変更するには、外部関数をドロップして再作成する必要があります。