Important
Lakeflowパイプラインの環境バージョンは パブリックプレビュー中です。
environment バージョンが設定されたパイプラインは、Spark Connect を介してPythonコードを実行します。 このページでは、何が互換性がないのか、何が異なる挙動をするのか、そしてDatabricksがどのようにパイプラインから影響を受けたパターンをスキャンするかを扱っています。
制限事項
環境バージョンは、すべてのパイプライン機能とまだ互換性がありません。 パイプラインのPython コードが次のいずれかの処理を行うと、環境バージョンが設定されたパイプラインの実行が失敗します。
- パイプライン デコレーターで修飾された関数内の Spark セッション状態を変更します。 たとえば、
spark.conf.set(...)、spark.sql("USE CATALOG ...")、createOrReplaceTempViewなどがあります。 -
SparkContext、RDD、SQLContext、Py4J API など、Spark Connect で使用できない PySpark API を使用します。 Spark Connect でサポートされている内容を参照してください。
パイプラインで環境バージョンを有効にすると失敗する場合、環境バージョンを無効にすると、パイプラインは以前の状態に戻ります。
動作の変更
Spark Connect には、従来の PySpark ランタイムと少数の動作の違いがあります。 完全なリファレンスについては、 Spark Connect とクラシック Spark を参照してください。 互換性スキャンはこれらのパターンを事前に検出し、対処されるまで移行をブロックするため、本番データに影響を与える前に発見・修正できます。
パイプラインでは、動作が異なる可能性がある最も一般的な状況は次のとおりです。
- インターリーブされた DataFrame の構築とセッションの変更
- 変更可能なPython状態を参照する UDF
インターリーブされた DataFrame の構築とセッションの変更
パイプラインが DataFrame を構築し、Spark セッションの状態を変更し (たとえば、既定のカタログまたはスキーマの変更、構成の設定、一時ビューの置き換え、UDF の再登録など)、DataFrame を使用します。
- 環境バージョンがない場合、DataFrame は ミューテーション前 のセッション状態を使用します。
- 環境バージョンでは、DataFrame は 変更後 のセッション状態を使用します。
例えば次が挙げられます。
from pyspark import pipelines as dp
spark.createDataFrame([(1, "Original Row")], ["id", "data"]) \
.createOrReplaceTempView("my_view")
df = spark.sql("SELECT * FROM my_view")
spark.createDataFrame([(2, "Replaced Row")], ["id", "data"]) \
.createOrReplaceTempView("my_view")
@dp.materialized_view
def mytable():
return df
環境バージョンがない場合、 mytable には [(1, "Original Row")]が含まれます。 環境バージョンでは、 mytable には [(2, "Replaced Row")]が含まれます。
変更可能なPython状態を参照する UDF
UDF が定義された後に値が変化するPythonグローバル変数を UDF が参照する場合:
- 環境バージョンがない場合、UDF は変数の 最新 の値を使用します。
- 環境バージョンでは、UDF は UDF が 定義された時点の値を使用します。
例えば次が挙げられます。
from pyspark import pipelines as dp
from pyspark.sql.functions import col, udf
suffix = "a"
@udf
def my_udf(s):
return s + suffix
suffix = "b"
@dp.materialized_view
def my_mv():
return spark.createDataFrame([("alex",)], ["name"]).select(my_udf(col("name")))
環境バージョンがない場合、 my_mv には [("alex_b",)]が含まれます。 環境バージョンでは、 my_mv には [("alex_a",)]が含まれます。
パイプラインがいずれかのパターンに依存している場合は、環境バージョンを有効にする前に監査します。
互換性スキャン
互換性スキャンは、環境バージョンによって異なる結果が出るコードパターンをパイプライン内で見つけ出し、 パイプラインが自動的に移行される前に修正できます。 パイプラインでスキャンが有効になっている場合:
- 各更新は検出されたパターンごとにパイプラインイベントログに1つの
BehaviorChangeInSparkConnectWARNイベントを発行します。 - パイプラインは環境バージョンに移行せず、前回の成功したアップデートによる互換性警告がすべて解決されるまでは、自分で環境バージョンを有効にすることもできません。
このチェックは、以前のアップデートがないパイプラインや、すでに環境バージョンセットが設定されているパイプラインには適用されません。
パイプラインでスキャンを有効にする
互換性スキャンを有効にするには、 pipelines.environmentVersion.enableCompatibilityScan パイプライン構成を追加します。 パイプライン エディター UI を使用するか、パイプライン構成 JSON にエントリを追加して、構成を追加できます。
UI を使用して次の操作を行います。
- パイプライン エディターで、[ 設定] をクリックします。
- パイプライン設定の [構成] セクションを見つけます。
- [
構成を追加します。
- キーとして
pipelines.environmentVersion.enableCompatibilityScanを入力し、値としてtrueします。 - パイプラインの設定を保存します。
パイプライン JSON で次の手順を実行します。
configuration ブロックに次のエントリを追加します。
"configuration": {
"pipelines.environmentVersion.enableCompatibilityScan": "true"
}
互換性警告の確認と解決
パイプライン上の環境バージョンを妨げるパターンを見つけて除去するには:
- パイプラインを ドライラン モードで実行し、パイプライン イベントログで イベント
BehaviorChangeInSparkConnectWARN検索します。 各イベントは、検出された 1 つのパターンを報告します。 問題コード、サンプル パターン、推奨される修正プログラムの完全な一覧については、 互換性イベント リファレンスを参照してください。 - 提案された修正後に検出されたパターンを除去するためにパイプラインコードを更新し、再度パイプラインを実行してください。
- この繰り返しを、成功したアップデートで互換性イベントが出なくなるまで続けます。 パイプラインは自動的に移行でき、 環境バージョンも自分で有効にできます。
環境バージョンを有効にすると、Databricksがパイプラインを自動的に移行するか、あなたが自分で設定するかの安全チェック environment_version 同じです。 未解決の互換性警告があるパイプラインは、警告が解決されるまで環境バージョンに移行しません。 移行が安全に完了できない場合や何らかの理由で失敗した場合、データを書き込む前に停止し、パイプラインは以前の実行時で実行を続けます。
これらの理由で更新が停止した場合、パイプラインイベントログと更新エラーメッセージが原因と解決手順を説明します。 その手順に従い、再度パイプラインを実行して移行を完了してください。 互換性警告が誤検知だと思われる場合は、フラグが立てられたパターンを解決するか、Azure Databricksサポートに連絡してください。
互換性イベントリファレンス
互換スキャンがパイプライン上で実行されると、検出されたパターンごとにパイプラインイベントログに1つのBehaviorChangeInSparkConnectWARNイベントを出力します。 前回の成功した更新でパターンが検出された場合、パターンが処理されるまでパイプラインは環境バージョンに移行されません。
各イベントは、検出された内容を識別する 1 つの問題コードを報告します。 コードを検索するには、[ 問題コード ] テーブルでコードを見つけます。各行は、パターンの例と修正候補を含むカテゴリ セクションにリンクします。
イベント図形
BehaviorChangeInSparkConnect イベントは、標準の パイプライン イベント ログ スキーマに従います。
-
event_typeはbehavior_change_in_spark_connectです。 -
levelはWARNです。 -
detailsには、単一のbehavior_change_in_spark_connectフィールドを持つissueオブジェクトが含まれています。 問題の値は、以下に示すコードの 1 つです。 -
messageは、検出されたパターンの人間が判読できる説明です。
問題コード
| カテゴリ | 問題コード | Description |
|---|---|---|
| データベースとカタログの変更 | USE_CATALOG_OUTSIDE_QUERY_FUNCTION_AFTER_DATAFRAME_COULD_CHANGE_BEHAVIOR |
既定のカタログは、DataFrame の作成後に変更されました。 既存の DataFrame では、新しい既定のカタログを使用してテーブルを解決できます。 |
| データベースとカタログの変更 | USE_CATALOG_OUTSIDE_QUERY_FUNCTION_COULD_CHANGE_BEHAVIOR |
USE CATALOG は、パイプライン デコレーターによって修飾された関数の外部で呼び出されました。 既定のカタログは、後続の操作で予期せず変更される可能性があります。 |
| データベースとカタログの変更 | USE_DATABASE_OUTSIDE_QUERY_FUNCTION_AFTER_DATAFRAME_COULD_CHANGE_BEHAVIOR |
既定のデータベースは、DataFrame の作成後に変更されました。 既存の DataFrame は、新しい既定のデータベースを使用してテーブルを解決できます。 |
| データベースとカタログの変更 | USE_DATABASE_OUTSIDE_QUERY_FUNCTION_COULD_CHANGE_BEHAVIOR |
USE DATABASE は、パイプライン デコレーターによって修飾された関数の外部で呼び出されました。 既定のデータベースは、後続の操作で予期せず変更される可能性があります。 |
| フロー関数内での一括実行 | CHECKPOINT_COMMAND_WITHIN_QUERY_FUNCTION_NOT_SUPPORTED |
フロー関数はチェックポイント コマンドを呼び出します。 |
| フロー関数内での一括実行 | CREATE_DATAFRAME_VIEW_WITHIN_QUERY_FUNCTION_NOT_SUPPORTED |
フロー関数は、DataFrame ビュー (createOrReplaceTempView など) を熱心に作成します。 |
| フロー関数内での一括実行 | CREATE_RESOURCE_PROFILE_COMMAND_WITHIN_QUERY_FUNCTION_NOT_SUPPORTED |
フロー関数は、リソース プロファイルを作成します。 |
| フロー関数内での一括実行 | GET_RESOURCES_COMMAND_WITHIN_QUERY_FUNCTION_NOT_SUPPORTED |
フロー関数は、 spark.resources または関連するリソース API を呼び出します。 |
| フロー関数内での一括実行 | MERGE_INTO_TABLE_COMMAND_WITHIN_QUERY_FUNCTION_NOT_SUPPORTED |
フロー関数は、ターゲット テーブルに対して一括 MERGE INTO を実行します。 |
| フロー関数内での一括実行 | ML_COMMAND_WITHIN_QUERY_FUNCTION_NOT_SUPPORTED |
フロー関数は、一括 Spark ML 操作を実行します。 |
| フロー関数内での一括実行 | REGISTER_DATA_SOURCE_WITHIN_QUERY_FUNCTION_NOT_SUPPORTED |
フロー関数は、Pythonデータ ソースを登録します。 |
| フロー関数内での一括実行 | STREAMING_QUERY_COMMAND_WITHIN_QUERY_FUNCTION_NOT_SUPPORTED |
フロー関数は、アクティブなストリーミング クエリ ハンドルで動作します。 |
| フロー関数内での一括実行 | STREAMING_QUERY_LISTENER_BUS_COMMAND_WITHIN_QUERY_FUNCTION_NOT_SUPPORTED |
フロー関数は、ストリーミング クエリ リスナーを登録または削除します。 |
| フロー関数内での一括実行 | STREAMING_QUERY_MANAGER_COMMAND_WITHIN_QUERY_FUNCTION_NOT_SUPPORTED |
フロー関数は、ストリーミング クエリを管理するために spark.streams を呼び出します。 |
| フロー関数内での一括実行 | WRITE_OPERATION_V2_WITHIN_QUERY_FUNCTION_NOT_SUPPORTED |
フロー関数は、一括 DataFrameWriterV2 操作を実行します。 |
| フロー関数内での一括実行 | WRITE_OPERATION_WITHIN_QUERY_FUNCTION_NOT_SUPPORTED |
フロー関数は、一括 DataFrame.write 操作を実行します。 |
| フロー関数内での一括実行 | WRITE_STREAM_OPERATION_START_WITHIN_QUERY_FUNCTION_NOT_SUPPORTED |
フロー関数は、ストリーミング クエリ (writeStream.start()) を開始します。 |
| Spark 構成の変更 | CHANGE_CONF_INSIDE_QUERY_FUNCTION_NOT_SUPPORTED |
spark.conf.set() または、パイプライン デコレーターによって修飾された関数内で呼び出された spark.conf.unset() 。 これは、環境バージョンではサポートされていません。 |
| Spark 構成の変更 | SET_CONF_AFTER_DATAFRAME_COULD_CHANGE_BEHAVIOR |
spark.conf.set() は、DataFrame の作成後にパイプライン デコレーターによって修飾された関数の外部で呼び出されました。 構成の変更は、実行時に既存の DataFrame に影響する可能性があります。 |
| Spark 構成の変更 | UNSET_CONF_AFTER_DATAFRAME_COULD_CHANGE_BEHAVIOR |
spark.conf.unset() は、DataFrame の作成後にパイプライン デコレーターによって修飾された関数の外部で呼び出されました。 構成の変更は、実行時に既存の DataFrame に影響する可能性があります。 |
| 一時的なビューの置換 | REPLACE_GLOBAL_TEMP_VIEW_AFTER_DATAFRAME_COULD_CHANGE_BEHAVIOR |
グローバル一時ビューは、それを参照する DataFrame の作成後に置き換えられました。 置換は、既存の DataFrame に反映される場合があります。 |
| 一時的なビューの置換 | REPLACE_TEMP_VIEW_AFTER_DATAFRAME_COULD_CHANGE_BEHAVIOR |
一時ビューは、それを参照する DataFrame の作成後に置き換えられました。 置換は、既存の DataFrame に反映される場合があります。 |
| UDF と UDTF の変更 | OVERWRITE_SESSION_UDF_AFTER_DATAFRAME_COULD_CHANGE_BEHAVIOR |
UDF は、作成されたデータフレームを参照した後、同じ名前で再登録されました。 既存の DataFrame では、新しい UDF 定義を使用できます。 |
| UDF と UDTF の変更 | OVERWRITE_SESSION_UDTF_AFTER_DATAFRAME_COULD_CHANGE_BEHAVIOR |
UDTF は、それを参照する DataFrame が作成された後、同じ名前で再登録されました。 既存の DataFrame では、新しい UDTF 定義を使用できます。 |
| UDF と UDTF の変更 | UDF_REFERENCES_GLOBAL_VARIABLE_COULD_CHANGE_BEHAVIOR |
UDF は、グローバル変更可能なPython変数を参照します。 環境バージョンでは、UDF は、呼び出し時ではなく、UDF が定義された時点で変数の値を使用します。 |
| UDF と UDTF の変更 | UDTF_REFERENCES_GLOBAL_VARIABLE_COULD_CHANGE_BEHAVIOR |
UDTF は、グローバル変更可能なPython変数を参照します。 環境バージョンでは、UDTF は、UDTF が定義された時点で、呼び出し時ではなく変数の値を使用します。 |
データベースとカタログの変更
これらの問題は、パイプライン コードが既定のデータベースまたはカタログを変更するときに生成されます。 環境バージョンでは、変更の前に構築された DataFrame は、新しいデータベースまたはカタログを使用してテーブルを解決できます。
イベントをトリガーするパターンの例:
from pyspark import pipelines as dp
spark.sql("USE CATALOG marketing")
df = spark.read.table("events")
spark.sql("USE CATALOG sales") # changes the default catalog after df was created
@dp.materialized_view
def events_summary():
return df.groupBy("region").count()
環境バージョンがない場合、dfはevents カタログからのmarketingを解決します。 環境バージョンでは、dfはevents カタログからのsalesを解決します。
推奨される修正: テーブル名を完全修飾して、解決が既定のカタログまたはデータベースに依存しないようにし、DataFrame の作成と使用の間で既定のカタログまたはデータベースを変更しないようにします。
from pyspark import pipelines as dp
df = spark.read.table("marketing.default.events")
@dp.materialized_view
def events_summary():
return df.groupBy("region").count()
Spark 構成の変更
これらの問題は、パイプライン コードが環境バージョンで DataFrame の動作を変更できる方法で Spark 構成を変更するときに生成されます。
イベントをトリガーするパターンの例:
from pyspark import pipelines as dp
df = spark.read.table("events")
spark.conf.set("spark.sql.ansi.enabled", "true") # changes session conf after df was created
@dp.materialized_view
def events_strict():
return df.selectExpr("CAST(price AS INT) AS price")
環境バージョンがない場合、キャストは DataFrame 作成時に conf 値を使用します。 環境バージョンでは、キャストは spark.sql.ansi.enabled=true を使用し、無効な入力で失敗する可能性があります。
推奨される修正: DataFrame が作成される前に、必要なすべての Spark 構成をパイプライン ファイルの先頭に設定します。 クエリごとの構成の場合は、パイプライン 仕様でパイプラインの configuration 設定を使用します。
一時的なビューの置換
これらの問題は、パイプライン コードが、それを参照する DataFrame の作成後に一時ビューを置き換えるときに生成されます。 環境バージョンでは、既存の DataFrame に新しいビューの内容が反映される場合があります。
イベントをトリガーするパターンの例:
from pyspark import pipelines as dp
spark.createDataFrame([(1, "Original Row")], ["id", "data"]) \
.createOrReplaceTempView("my_view")
df = spark.sql("SELECT * FROM my_view")
spark.createDataFrame([(2, "Replaced Row")], ["id", "data"]) \
.createOrReplaceTempView("my_view")
@dp.materialized_view
def mytable():
return df
環境バージョンがない場合、 mytable には [(1, "Original Row")]が含まれます。 環境バージョンでは、 mytable には [(2, "Replaced Row")]が含まれます。
推奨される修正: 各一時ビューを 1 回作成し、置き換えないでください。 関連データを含む複数のビューが必要な場合は、それぞれに個別の名前を付けます。
UDF と UDTF の変更
これらの問題は、パイプライン コードが環境バージョンでの動作を変更する方法で UDF または UDTF を変更するときに生成されます。
イベントをトリガーするパターンの例:
from pyspark import pipelines as dp
from pyspark.sql.functions import col, udf
suffix = "a"
@udf
def my_udf(s):
return s + suffix
suffix = "b"
@dp.materialized_view
def my_mv():
return spark.createDataFrame([("alex",)], ["name"]).select(my_udf(col("name")))
環境バージョンがない場合、 my_mv には [("alex_b",)]が含まれます。 環境バージョンでは、 my_mv には [("alex_a",)]が含まれます。
Suggested fix: 値をPythonグローバルからキャプチャするのではなく、引数として UDF に渡すか、UDF を定義する前にグローバルを設定し、後で変更しないでください。
from pyspark import pipelines as dp
from pyspark.sql.functions import col, lit, udf
@udf
def append_suffix(s, suffix):
return s + suffix
@dp.materialized_view
def my_mv():
return spark.createDataFrame([("alex",)], ["name"]).select(append_suffix(col("name"), lit("b")))
フロー関数内での一括実行
これらの問題は、パイプライン コードがパイプライン デコレーター (@table、 @materialized_view など) によって装飾された関数内で一括 Spark コマンドを実行すると生成されます。 フロー関数は、DataFrame を定義して返す必要があります。データの書き込み、ストリーミング クエリの管理、リソースの登録、または ML 操作の実行を行う一括コマンドは、環境バージョン セットを持つフロー関数内では許可されません。
推奨される修正: 一括操作をフロー関数の外部に移動し、代わりにフロー関数から DataFrame を返します。 テーブルへの書き込み、ストリーミング クエリの開始などの副作用は、パイプライン定義の外部に属します。パイプライン エンジンは、フロー関数によって返される DataFrame の具体化を処理します。
イベント ログで互換性イベントを検索する
次のクエリは、パイプラインのすべての互換性イベントを返します。最新の順序が最初に並べ替えられます。
SELECT
timestamp,
message,
details:behavior_change_in_spark_connect:issue AS issue
FROM event_log(<pipeline-id>)
WHERE event_type = 'behavior_change_in_spark_connect'
AND level = 'WARN'
ORDER BY timestamp DESC;
最近の更新で問題コード別にイベントをカウントするには:
SELECT
details:behavior_change_in_spark_connect:issue AS issue,
COUNT(*) AS occurrences
FROM event_log(<pipeline-id>)
WHERE event_type = 'behavior_change_in_spark_connect'
AND level = 'WARN'
GROUP BY 1
ORDER BY occurrences DESC;
イベント ログのクエリを実行する方法については、イベント ログのクエリを参照してください。
その他のリソース
- パイプラインの環境バージョンを設定する — 機能概要、自動移行、そして環境バージョンを自分で有効化する方法。
- パイプライン イベント ログ スキーマ — パイプライン イベント ログ スキーマ全体。
- パイプライン イベント ログ — パイプライン イベント ログに対してクエリを実行する方法。