外部関数を作成するためのプラットフォームに依存しない情報

プロキシサービスとリモートサービスの開発に関連する情報の多くは、プラットフォームによって異なります。ただし、プラットフォームに依存しない追加の詳細が多数あります。

このトピックでは、これらのプラットフォームに依存しない詳細について説明します。

このトピックの内容:

リモートサービス

データ形式

Snowflakeは、 HTTP POST リクエストを発行して、プロキシサービスのリソースを呼び出します。このPOST には、ヘッダーとともに特定の形式のデータが含まれています。POST の結果として返されるデータも、特定の形式に準拠している必要があります。両方のフォーマットについて以下に説明します。

Snowflakeから送信されたデータ形式

Snowflakeからの各 HTTP リクエストは POSTです。POST リクエストにはヘッダーと本文が含まれています。メタデータは HTTP ヘッダーで渡されます。データはバッチ処理され、 POST のリクエスト本文に渡されます。

ヘッダーの情報には次が含まれます。

  • CREATE EXTERNAL FUNCTION の「ヘッダー」および「コンテキストヘッダー」プロパティで説明されているメタデータ。

  • 次の HTTP ヘッダー:

    • リクエストの本文でデータがシリアル化される方法を説明するヘッダー:

      • 「sf-external-function-format」:現在、常に「json」に設定されています。

      • 「sf-external-function-format-version」:現在、常に「1.0」に設定されています。

    • 「sf-external-function-current-query-id」:これには、この外部関数を呼び出したクエリのクエリ ID が含まれています。これを使用して、Snowflakeクエリをリモートサービスの呼び出しに関連付け、たとえば問題のデバッグに役立てることができます。

POST リクエストの本文には、 JSON 形式でシリアル化されたデータが含まれます。JSON のスキーマは次のとおりです。

  • トップレベルの JSON オブジェクトは、名前と値のペアの辞書です。

  • 現在、辞書には1つのアイテムしかありません。そのアイテムのキーの名前は「データ」です。

  • その「データ」アイテムの値は JSON 配列で、配列の各要素は1行のデータです。

  • データの各行は、1つ以上の列の JSON 配列です。

  • 最初の列は常に行番号です(つまり、バッチ内における行の0ベースインデックス)。

  • 残りの列には、関数の引数が含まれています。

  • データ型は次のようにシリアル化されます。

    • 番号は JSON 番号としてシリアル化されます。

    • ブール値は JSON ブール値としてシリアル化されます。

    • 文字列は JSON 文字列としてシリアル化されます。

    • バリアントは JSON オブジェクトとしてシリアル化されます。

    • サポートされている他のすべてのデータ型は、 JSON 文字列としてシリアル化されます。

    • NULL は JSON nullとしてシリアル化されます。

これは、署名 f(integer, integer, varchar, timestamp) を使用したリモートサービスのシリアル化されたリクエストの例です。

{
    "data": [
                [0, 10, "Alex", "Wed, 01 Jan 2014 16:00:00 -0800"],
                [1, 20, "Steve", "Wed, 01 Jan 2015 16:00:00 -0800"],
                [2, 30, "Alice", "Wed, 01 Jan 2016 16:00:00 -0800"],
                [3, 40, "Adrian", "Wed, 01 Jan 2017 16:00:00 -0800"]
            ]
}

オプションで、ネットワークを介して送信するために JSON を圧縮できます。圧縮については、 CREATE EXTERNAL FUNCTION に記載されています。

Snowflakeはこのデータを直接リモートサービスではなく、プロキシサービスに送信します。したがって、プロキシサービスは、Snowflake互換の形式でデータを受信(および返す)する必要があります。通常、プロキシサービスはデータを変更せずに渡しますが、リモートサービスとSnowflakeの両方のニーズを満たすために、プロキシはデータを再フォーマット(送信と受信の両方)できます。

簡単にするためとSnowflakeが送受信する形式を説明するために、このセクションのほとんどの例では、リモートサービスがSnowflakeが期待する形式と同じ形式でデータを読み書きし、プロキシサービスが両方向でデータを変更せずに渡すことを想定しています。

Snowflakeが受信するデータ形式

Snowflakeが受信するデータの形式は、Snowflakeが送信するデータの形式と似ています。戻り値は JSON 形式です。

{
    "data":
        [
            [ 0, 1995 ],
            [ 1, 1974 ],
            [ 2, 1983 ],
            [ 3, 2001 ]
        ]
}

Snowflakeに返される JSON 応答には、Snowflakeによって送信された各行に1行が含まれている必要があります。返された各行には2つの値が含まれます。

  • 行番号(つまり、バッチ内における行の0ベースインデックス)。

  • その行の関数から返された値。値は複合値(たとえば、 VARIANT)にすることができますが、Snowflakeのスカラー関数すべて(外部またはその他)が単一の値を返すため、正確に単一の値でなければなりません。

返されるデータの行番号は、Snowflakeが送信したデータの行番号に対応している必要があり、受信した順序と同じ順序で返される必要があります。

この JSON は HTTP 応答の本文です。応答には HTTP ヘッダーとステータスコードも含まれます(例:ステータスコード200は成功を示す)。

オプションで、ネットワークを介して送信するために JSON を圧縮できます。圧縮については、 CREATE EXTERNAL FUNCTION に記載されています。

Snowflakeは、次の HTTP ステータスコードを認識します。

コード

説明

200

エラーなし

その他の値はエラーとして扱われます。

このステータスコードのリストは、時間の経過とともに拡大する可能性があります。

タイムアウトと再試行の詳細については、 タイムアウトエラーの説明 および リモートサービスが各行に1回だけ渡されると思い込まない をご参照ください。

ベストプラクティス

次のベストプラクティスは、ほとんどの外部機能、特にリモートサービス部分に適用されます。

1度に1行ずつ処理

ネットワークのオーバーヘッドを最小限に抑えるために、Snowflakeは通常、行をバッチ処理してリモートサービスに送信します。バッチの数と各バッチのサイズは異なる場合があります。

さらに、バッチの順序、およびバッチ内の行の順序は異なる場合があります。クエリに ORDER BY 句が含まれている場合でも、通常、 ORDER BY は外部関数が呼び出された後に適用されます。

バッチサイズと行の順序は保証されないため、このバッチまたは以前のバッチの他の行に依存する行の値を返す関数を作成すると、非決定的な結果が生成される可能性があります。

Snowflakeは、リモートサービスにおいて各行を個別に処理することを強くお勧めします。各入力行の戻り値は、他の入力行ではなく、その入力行のみに依存する必要があります。(たとえば現在、外部関数は ウィンドウ関数 をサポートしていません。)

また、バッチサイズは保証されていないため、バッチのカウントは意味がありません。

外部関数がステートレスであることを確認 もご参照ください。

リモートサービスが各行に1回だけ渡されると思い込まない

Snowflakeがリモートサービスを呼び出し、リモートサービスがリクエストを受信して結果を返したが、一時的なネットワークの問題によりSnowflakeが結果を受信しない場合、Snowflakeはリクエストを繰り返す可能性があります。Snowflakeが再試行すると、リモートサービスは同じ行を2回(またはそれ以上)表示する可能性があります。

これにより、予期しない影響が生じる可能性があります。たとえば、同じ値に対してリモートサービスが複数回呼び出される可能性があるため、一意の IDs を割り当てるリモートサービスでは、それらの IDs のシーケンスにギャップが生じる場合があります。

Snowflakeは、次のエラーを受け取ったときに再試行します。

  • すべての一時的なネットワーク転送エラー。

  • 429ステータスコードで失敗するすべてのリクエスト。

  • 5XX ステータスコードで失敗するすべてのリクエスト。

合計再試行タイムアウトに達するまで、要求は指数バックオフで再試行されます。合計再試行タイムアウトは現在文書化されておらず、ユーザーが構成することはできません。Snowflakeは将来この制限を調整する可能性があります。

再試行が成功せずに合計再試行タイムアウトに達すると、クエリは失敗します。

リモートサービスが機能しているときに外部関数呼び出しがタイムアウトし、Snowflakeとリモートサービス間のすべての要素が機能しているように見える場合は、バッチサイズを小さくして、タイムアウトエラーが減少するかどうかを試してください。

最大バッチサイズを設定する方法については、 CREATE EXTERNAL FUNCTION をご覧ください。

外部関数がステートレスであることを確認

外部関数(リモートサービスを含む)のあらゆる面で、次の両方における状態情報の保存を回避する必要があります。

  • 内部状態(リモートサービスが内部的に保存する状態)。

  • 外部状態(リモートサービスの外部に保存されている状態。たとえば、状態を保持する別のリモートサービスとの間で送受信される状態情報)。

リモートサービスが状態情報を変更し、その情報を使用して将来の出力に影響を与える場合、関数は予想とは異なる値を返す可能性があります。

たとえば、内部カウンターを含み、リモートサービスが最初に開始されてから受信した行数を返す単純なリモートサービスを考えます。一時的なネットワークの問題があり、Snowflakeが同じデータで要求を繰り返す場合、リモートサービスは再送信された行を2回(またはそれ以上)カウントします。

外部状態を含む例については、 副作用の回避 をご参照ください。

関数がステートレスではない非常にまれなケースでは、呼び出し元のドキュメントにおいて、関数がステートレスではなく、関数が揮発性としてマークされている必要があることを明確に示す必要があります。

副作用の回避

外部関数(リモートサービスを含む)では、外部状態(リモートサービスの外部に保存されている情報)の変更などの副作用を回避する必要があります。

たとえば、リモートサービスが範囲外の値を政府機関に報告する場合、それは副作用です。

副作用は有用ですが、外部関数を呼び出すことによる副作用は常に予測できるとは限りません。たとえば、匿名化されたヘルスレコードを分析して診断を返すリモートサービスを呼び出すとします。また、診断が患者に伝染病があることが診断された場合、その診断はその疾患の症例数を記録する機関に報告されると仮定します。これは便利な副作用です。ただし、次のような問題に対して脆弱です。

  • 外部関数呼び出しがロールバックされるトランザクション内にある場合、副作用はロールバックされません。

  • リモートサービスが同じ行で複数回呼び出された場合(例:一時的なネットワーク障害と再試行が原因)、副作用が複数回発生する可能性があります。たとえば、感染した患者は統計で2回カウントされる可能性があります。

行が過大にカウントされるのではなく、過小にカウントされる状況もあります。

関数に副作用がある非常にまれなケースでは、呼び出し元のドキュメントに副作用が何であるかを明確に記述し、関数に揮発性のマークを付ける必要があります。

関数を揮発性または不変として分類

関数は、揮発性または不変に分類できます。(CREATE EXTERNAL FUNCTION ステートメントを使用すると、関数の揮発性または不変の指定が可能。)

外部関数が不変であると見なされるには、次の基準を満たす必要があります。

  • 同じ入力値が指定された場合、関数は同じ出力値を返します。(たとえば、 SQRT 関数は同じ入力が与えられたときに同じ出力を返しますが、 CURRENT_TIMESTAMP 関数は同じ入力が与えられたときに必ずしも同じ出力を返すとは限らない。)

  • この関数には副作用がありません。

関数がこれらの2つの基準を満たす場合、Snowflakeは特定のタイプの最適化を使用して、リモートサービスに送信される行またはバッチの数を減らすことができます。(これらの最適化は時間とともに進化する可能性があるため、詳細の説明を省略。)

Snowflakeは、不変性、または不変性に影響を与える要因(たとえば、副作用)を検出または強制できません。リモートサービスの作成者は、リモートサービスが不変のラベルを付ける基準を満たすかどうかを文書化する必要があります。リモートサービスに副作用がある場合は、関数呼び出しが同じ入力値に対して同じ出力値を返す場合でも、そのリモートサービスを呼び出す外部関数を揮発性としてマークする必要があります。リモートサービスが不変であるかどうかわからない場合は、そのリモートサービスを呼び出す外部関数に揮発性のラベルを付ける必要があります。

タイムアウトエラーの説明

外部関数呼び出しには、Snowflake、リモートサービス、プロキシサービス、および場合によってはチェーン内の他の要素が含まれます。これらの要素ではいずれも、特定の関数呼び出しにかかる時間を把握していないため、待機を停止してタイムアウトエラーを返すタイミングを正確に把握できません。各ステップには、独自のタイムアウトがあります。タイムアウトの詳細については、 リモートサービスが各行に1回だけ渡されると思い込まない をご参照ください。

待ち時間を最小限に抑制

遅延を最小限に抑え、外部関数呼び出しのパフォーマンスを向上させるために、可能な場合は以下を実行することをお勧めします。

  • API Gatewayを最も頻繁に(または最大量のデータで)呼び出すSnowflakeインスタンスと同じクラウドプラットフォームおよび地域に、Gatewayを配置します。

  • リモートサービスを作成した場合は(既存のサービスを使用するのではなく)、呼び出し元と同じクラウドプラットフォームと地域でそのリモートサービスを展開します。

  • できるだけ少ないデータを送信します。たとえば、リモートサービスが入力値を調べてそれらのサブセットのみを操作する場合、通常、すべての行をリモートサービスに送信するよりも、 SQL でフィルタリングして関連する行のみをリモートサービスに送信する方が効率的です。リモートサービスを使用して、フィルタリングします。

    別の例として、大きな半構造化データ値を含む列を処理していて、リモートサービスはこれらの各データ値のごく一部のみを使用する場合、通常は、処理の前に列全体を送信してリモートサービスにより小さな断片を抽出するよりも、Snowflake SQL を使用して関連する部分のみを抽出して送信する方が効率的です。

外部関数の開発とテスト

Snowflakeでテストする前に、Snowflakeなしでテストすることをお勧めします。

外部関数の開発の初期段階で、クラウドプラットフォームプロキシサービスコンソール(例: AWS API Gatewayコンソール)およびリモートサービス開発コンソール(例: AWS Lambdaコンソール)を使用して、プロキシサービスとリモートサービスの開発とテストを支援します。

たとえば、Lambda関数を開発した場合は、Snowflakeから呼び出してテストする前に、Lambdaコンソールで広範囲にテストすることができます。

プロキシサービスコンソールとリモートサービスコンソールによるテストには、通常、次の利点があります。

  • 問題の原因を探す箇所が限られるため、問題の診断が容易になります。

  • データペイロードを表示すると、有用なデバッグ情報が得られる場合があります。Snowflakeでは、データペイロードのどの部分もエラーメッセージに表示しません。これによりセキュリティが強化されますが、デバッグが遅くなる可能性があります。

  • Snowflakeは HTTP 5xxエラーの場合に自動で再試行するため、状況によってはデバッグが遅くなったり困難になったりする可能性があります。

  • Snowflakeによるテストでは、クラウドプラットフォームクレジットに加えて、Snowflakeクレジットが使用されます。

もちろん、リモートサービスとプロキシサービスをSnowflakeなしでできる限りテストした後は、Snowflakeでテストする必要があります。Snowflakeでテストする利点は次のとおりです。

  • 外部関数に関連するすべてのステップをテストしています。

  • データソースとしてSnowflakeテーブルを使用すると、大量のデータで簡単にテストして、外部関数のパフォーマンスの現実的な見積もりを取得できます。

次のテストケースを検討します。

  • NULL 値。

  • 「空」の値(たとえば、空の文字列、空の半構造化データ型)。

  • 非常に長い VARCHAR および BINARY 値(該当する場合)。

プロキシサービス

プロキシサービスエンドポイントの保護

Snowflakeでは、プロキシサービスのエンドポイントを保護することを強くお勧めします。

Snowflakeは、資格情報のない API 統合オブジェクトを使用して、プロキシサービスエンドポイントへの認証を行います。資格情報なしの API 統合は、管理者とユーザー間の責任を分離します。管理者は API 統合により、クラウドプロバイダーのネイティブ認証および承認メカニズムを使用して、Snowflakeとクラウドプロバイダーの間に信頼ポリシーを作成できます。Snowflakeがクラウドプロバイダーに接続すると、クラウドプロバイダーはこの信頼ポリシーを通じてアクセスを認証および承認します。管理者は特定の API 統合を使用して、Snowflakeが使用できるプロキシサービスとリソースを制限することもできます。これにより、管理者はデータの出入りに組織のポリシーを適用できます。

AWS API Gatewayなどの特定のプロキシサービスエンドポイントを保護するための詳細な手順は、プラットフォーム固有の手順にあります。