LakeFlow宣言型パイプラインのイベントログ

これまでDelta Live Tables(DLT)と呼ばれていた機能は、LakeFlowカテゴリーのDeclarative Pipelines(宣言型パイプライン)にリニューアルされました。

https://docs.databricks.com/aws/ja/dlt/

# LakeFlow宣言型パイプラインとは

Databricks LakeFlow宣言型パイプライン(旧DLT: Delta Live Tables)は、SQLとPythonでバッチおよびストリーミングデータパイプラインを構築するための革新的なフレームワークです。複雑なデータ処理を数行のコードで実装でき、依存関係の自動解決、エラーからの自動リカバリ、増分処理の最適化などの機能により、データエンジニアリングの生産性を大幅に向上させます。クラウドストレージやメッセージバスからのデータ取り込み、変換、処理まで、エンドツーエンドのデータパイプラインを宣言的に構築できるETLソリューションです。

## 機能概要

LakeFlow宣言型パイプラインは、以下の3つの主要コンポーネントで構成されています：

### 1. フロー(Flows)
データを段階的に読み込んで処理するクエリです。各フローは以下で構成されます：

| 構成要素 | 説明 |
|---------|------|
| クエリ | データを取得・変換するSQL/Pythonコード |
| ターゲット | ストリーミングテーブルまたはマテリアライズドビュー |
| 処理モード | バッチまたはストリーミング（増分）処理 |

### 2. ストリーミングテーブル
新しいデータを継続的に追加するテーブルです：
- **追加専用**: 新しい行のみを追加（既存データは変更しない）
- **増分処理**: 前回処理以降の新規データのみを処理
- **自動スケーリング**: データ量に応じて自動的にリソースを調整

### 3. マテリアライズドビュー
データ変換の結果を保持するビューです：
- **完全更新**: 各実行時に全データを再計算
- **複雑な変換**: 集計、結合、フィルタリングなどに最適
- **最新性保証**: 常に最新のデータを反映

## メリット、嬉しさ

### 1. 開発効率の劇的な向上
- **宣言的アプローチ**: 「何を」したいかを記述するだけで、「どのように」実行するかはLakeFlowが自動で最適化
- **コード量の削減**: 従来のSpark開発と比較して、最大90%のコード削減を実現
- **自動依存関係解決**: テーブル間の依存関係を自動的に検出し、正しい順序で実行

### 2. 運用の簡素化
- **自動エラーリカバリ**: エラー発生時に自動的に再試行し、データの整合性を保証
- **増分処理の最適化**: 新規データのみを自動的に検出・処理し、処理時間とコストを削減
- **統一されたバッチ/ストリーミング**: 同じコードでバッチとストリーミング処理を切り替え可能

### 3. 品質とガバナンスの向上
- **データ品質の自動検証**: 期待値アサーションによる品質チェック
- **完全なデータリネージ**: データの流れを自動的に追跡・可視化
- **Unity Catalogとの統合**: 統一されたガバナンスとアクセス制御

### 4. コストとパフォーマンスの最適化

| 最適化機能 | 効果 |
|-----------|-----|
| サーバーレス実行 | インフラ管理不要、自動スケーリング |
| 増分処理 | 処理データ量の削減によるコスト削減 |
| 自動最適化 | クエリ実行計画の自動最適化 |
| リソース効率化 | 必要な時のみリソースを使用 |

# LakeFlow宣言型パイプラインのイベントログ

パイプラインの処理過程ではさまざまなイベントが発生します。これはUIでも確認できます。
![Screenshot 2025-06-23 at 15.06.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/336dafbd-7509-48d0-8df7-fc2a77428e53.png)

なお、Deltaテーブルにもイベントログが格納されています。深掘り分析を行う際にはテーブルデータを活用した方が好ましいです。以下のマニュアルにイベントログの説明があります。初期のDelta Live Tablesとは結構実装が変わっています。

https://docs.databricks.com/aws/ja/dlt/observability

まずは、パイプラインを実装します。

```py
# NYC Taxi Data DLT Pipeline
# データソース: /databricks-datasets/nyctaxi/tables/nyctaxi_yellow

import dlt
from pyspark.sql.functions import *
from pyspark.sql.types import *
from datetime import datetime

# ===== 1. ブロンズレイヤー: 生データの読み込み =====

@dlt.table(
    comment="NYC Yellow Taxi - 生データ",
    table_properties={
        "quality": "bronze",
        "pipelines.autoOptimize.managed": "true"
    }
)
def bronze_nyc_taxi():
    """
    NYC Yellow Taxiの生データを読み込む
    """
    return (
        spark.table("delta.`/databricks-datasets/nyctaxi/tables/nyctaxi_yellow`")
        .select(
            "*",
            current_timestamp().alias("ingestion_timestamp")
        )
    )

# ===== 2. シルバーレイヤー: データクレンジングと変換 =====

@dlt.table(
    comment="NYC Yellow Taxi - クレンジング済みデータ",
    table_properties={
        "quality": "silver",
        "pipelines.autoOptimize.zOrderCols": "pickup_date,pickup_hour"
    }
)
@dlt.expect_or_drop("valid_fare", "fare_amount >= 0")
@dlt.expect_or_drop("valid_distance", "trip_distance >= 0 AND trip_distance < 1000")
@dlt.expect_or_drop("valid_passenger", "passenger_count > 0 AND passenger_count <= 8")
@dlt.expect_or_drop("valid_coordinates", 
    "pickup_longitude BETWEEN -80 AND -70 AND pickup_latitude BETWEEN 35 AND 45")
def silver_nyc_taxi():
    """
    データクレンジングと特徴量エンジニアリング
    """
    return (
        dlt.read("bronze_nyc_taxi")
        .withColumn("pickup_date", to_date(col("pickup_datetime")))
        .withColumn("pickup_hour", hour(col("pickup_datetime")))
        .withColumn("pickup_day_of_week", dayofweek(col("pickup_datetime")))
        .withColumn("pickup_month", month(col("pickup_datetime")))
        .withColumn("pickup_year", year(col("pickup_datetime")))
        .withColumn("trip_duration_minutes", 
            (unix_timestamp(col("dropoff_datetime")) - 
             unix_timestamp(col("pickup_datetime"))) / 60)
        .withColumn("average_speed_mph",
            when(col("trip_duration_minutes") > 0,
                 col("trip_distance") / (col("trip_duration_minutes") / 60))
            .otherwise(0))
        .withColumn("total_amount_per_mile",
            when(col("trip_distance") > 0,
                 col("total_amount") / col("trip_distance"))
            .otherwise(0))
        .filter(col("trip_duration_minutes") > 0)
        .filter(col("trip_duration_minutes") < 480)  # 8時間以下の乗車のみ
        .filter(col("average_speed_mph") < 100)  # 時速100マイル以下
    )

# ===== 3. ゴールドレイヤー: ビジネス分析用集計 =====

@dlt.table(
    comment="時間別乗車統計",
    table_properties={
        "quality": "gold"
    }
)
def gold_hourly_statistics():
    """
    時間別の乗車統計
    """
    return (
        dlt.read("silver_nyc_taxi")
        .groupBy("pickup_date", "pickup_hour")
        .agg(
            count("*").alias("total_trips"),
            avg("trip_distance").alias("avg_distance"),
            avg("trip_duration_minutes").alias("avg_duration_minutes"),
            avg("fare_amount").alias("avg_fare"),
            avg("tip_amount").alias("avg_tip"),
            avg("total_amount").alias("avg_total_amount"),
            sum("total_amount").alias("total_revenue"),
            avg("passenger_count").alias("avg_passengers"),
            percentile_approx("total_amount", 0.5).alias("median_total_amount")
        )
        .orderBy("pickup_date", "pickup_hour")
    )

@dlt.table(
    comment="日別統計サマリー",
    table_properties={
        "quality": "gold"
    }
)
def gold_daily_summary():
    """
    日別の詳細統計
    """
    return (
        dlt.read("silver_nyc_taxi")
        .groupBy("pickup_date")
        .agg(
            count("*").alias("total_trips"),
            sum("trip_distance").alias("total_distance"),
            sum("total_amount").alias("total_revenue"),
            avg("trip_distance").alias("avg_distance"),
            avg("fare_amount").alias("avg_fare"),
            avg("tip_amount").alias("avg_tip"),
            avg("total_amount").alias("avg_total_amount"),
            max("trip_distance").alias("longest_trip"),
            min("trip_distance").alias("shortest_trip"),
            sum("passenger_count").alias("total_passengers"),
            stddev("total_amount").alias("revenue_stddev"),
            # チップ率の計算
            avg(when(col("fare_amount") > 0, 
                    col("tip_amount") / col("fare_amount") * 100)
                .otherwise(0)).alias("avg_tip_percentage")
        )
        .orderBy("pickup_date")
    )

@dlt.table(
    comment="曜日別・時間帯別パフォーマンス",
    table_properties={
        "quality": "gold"
    }
)
def gold_dow_hour_performance():
    """
    曜日別・時間帯別のパフォーマンス分析
    """
    return (
        dlt.read("silver_nyc_taxi")
        .withColumn("day_name",
            when(col("pickup_day_of_week") == 1, "Sunday")
            .when(col("pickup_day_of_week") == 2, "Monday")
            .when(col("pickup_day_of_week") == 3, "Tuesday")
            .when(col("pickup_day_of_week") == 4, "Wednesday")
            .when(col("pickup_day_of_week") == 5, "Thursday")
            .when(col("pickup_day_of_week") == 6, "Friday")
            .when(col("pickup_day_of_week") == 7, "Saturday"))
        .withColumn("time_period",
            when((col("pickup_hour") >= 6) & (col("pickup_hour") < 10), "Morning Rush")
            .when((col("pickup_hour") >= 10) & (col("pickup_hour") < 16), "Midday")
            .when((col("pickup_hour") >= 16) & (col("pickup_hour") < 20), "Evening Rush")
            .when((col("pickup_hour") >= 20) & (col("pickup_hour") < 24), "Night")
            .otherwise("Late Night"))
        .groupBy("day_name", "pickup_day_of_week", "time_period")
        .agg(
            count("*").alias("trip_count"),
            avg("total_amount").alias("avg_revenue"),
            avg("trip_distance").alias("avg_distance"),
            avg("average_speed_mph").alias("avg_speed"),
            avg("trip_duration_minutes").alias("avg_duration")
        )
        .orderBy("pickup_day_of_week", "time_period")
    )

@dlt.table(
    comment="月別トレンド分析",
    table_properties={
        "quality": "gold"
    }
)
def gold_monthly_trends():
    """
    月別のトレンド分析
    """
    return (
        dlt.read("silver_nyc_taxi")
        .groupBy("pickup_year", "pickup_month")
        .agg(
            count("*").alias("total_trips"),
            sum("total_amount").alias("total_revenue"),
            avg("total_amount").alias("avg_trip_revenue"),
            avg("trip_distance").alias("avg_trip_distance"),
            sum("passenger_count").alias("total_passengers"),
            # 前月比の計算用
            collect_list(struct(
                col("pickup_date"),
                col("total_amount")
            )).alias("daily_data")
        )
        .withColumn("year_month", 
            concat_ws("-", col("pickup_year"), 
                      lpad(col("pickup_month"), 2, "0")))
        .select("year_month", "pickup_year", "pickup_month", 
                "total_trips", "total_revenue", "avg_trip_revenue", 
                "avg_trip_distance", "total_passengers")
        .orderBy("pickup_year", "pickup_month")
    )

@dlt.table(
    comment="距離帯別料金分析",
    table_properties={
        "quality": "gold"
    }
)
def gold_distance_fare_analysis():
    """
    距離帯別の料金分析
    """
    return (
        dlt.read("silver_nyc_taxi")
        .withColumn("distance_category",
            when(col("trip_distance") <= 1, "0-1 mile")
            .when(col("trip_distance") <= 3, "1-3 miles")
            .when(col("trip_distance") <= 5, "3-5 miles")
            .when(col("trip_distance") <= 10, "5-10 miles")
            .when(col("trip_distance") <= 20, "10-20 miles")
            .otherwise("20+ miles"))
        .groupBy("distance_category")
        .agg(
            count("*").alias("trip_count"),
            avg("fare_amount").alias("avg_fare"),
            avg("tip_amount").alias("avg_tip"),
            avg("total_amount").alias("avg_total"),
            avg("trip_duration_minutes").alias("avg_duration"),
            avg("average_speed_mph").alias("avg_speed"),
            percentile_approx("total_amount", 0.25).alias("p25_total"),
            percentile_approx("total_amount", 0.50).alias("p50_total"),
            percentile_approx("total_amount", 0.75).alias("p75_total")
        )
    )

# ===== 4. データ品質レポート =====

@dlt.table(
    comment="データ品質メトリクス",
    table_properties={
        "quality": "gold"
    }
)
def gold_data_quality_report():
    """
    データ品質のサマリーレポート
    """
    bronze_count = dlt.read("bronze_nyc_taxi").count()
    silver_count = dlt.read("silver_nyc_taxi").count()

    if bronze_count != 0:
        drop_rate_percentage = ((bronze_count - silver_count) / bronze_count) * 100
    else:
        drop_rate_percentage = 0
    
    return spark.createDataFrame([
        ("total_raw_records", bronze_count),
        ("total_clean_records", silver_count),
        ("records_dropped", bronze_count - silver_count),
        ("drop_rate_percentage", drop_rate_percentage)
    ], ["metric_name", "metric_value"])
```

私がいうのもあれですが、開発体験がとても改善されています。画面の行ったり来たりが全くないです。DAGの縦横表示が切り替えられるのが地味に嬉しい。
![Screenshot 2025-06-23 at 14.48.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/4cf78d08-a93c-4405-bde0-c81465e02bf2.png)

パイプラインの処理が完了すると、カタログエクスプローラからテーブルを確認できます。しかし、イベントログを格納しているテーブルは非表示になっています。

![Screenshot 2025-06-23 at 15.10.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/e0d61855-fb03-4e34-a720-bf1dc714ce78.png)

[イベントログの説明](https://docs.databricks.com/aws/ja/dlt/observability#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88-%E3%83%AD%E3%82%B0%E3%81%AE%E3%82%AF%E3%82%A8%E3%83%AA)に従ってアクセスします。

イベントログのテーブルはテーブルを公開したスキーマに存在しています。以下のように`event_log_<パイプラインID>`というテーブルになります。パイプラインIDの**ダッシュはアンダースコアに置き換えます**。

```sql
select
  timestamp
  ,event_type
  ,details:maintenance_progress.state
  ,message
  ,id
  ,origin.pipeline_name
  ,origin.cluster_id
  ,origin.maintenance_id
from takaakiyayoi_catalog.dlt.event_log_0b077717_303f_4be1_b2c0_80a4fd8c27ec
```

推奨に従ってビューを作成します。

```sql
-- イベントログに対するビューを作成
CREATE VIEW takaakiyayoi_catalog.dlt.event_log_raw
AS SELECT * FROM takaakiyayoi_catalog.dlt.event_log_0b077717_303f_4be1_b2c0_80a4fd8c27ec;
```

最新の更新処理を取得します。

```sql
-- 最新のアップデートを参照するビューを作成
CREATE OR REPLACE VIEW latest_update AS SELECT origin.update_id AS id FROM event_log_raw WHERE event_type = 'create_update' ORDER BY timestamp DESC LIMIT 1;
```

## オートスケールのイベントの取得

[こちら](https://docs.databricks.com/aws/ja/dlt/observability#%E3%82%B5%E3%83%BC%E3%83%90%E3%83%AC%E3%82%B9%E3%81%8C%E6%9C%89%E5%8A%B9%E3%81%AB%E3%81%AA%E3%81%A3%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E3%83%91%E3%82%A4%E3%83%97%E3%83%A9%E3%82%A4%E3%83%B3%E3%81%AE%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%83%AD%E3%82%B0%E3%81%8B%E3%82%89%E6%8B%A1%E5%BC%B5%E3%82%AA%E3%83%BC%E3%83%88%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%AB%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E7%9B%A3%E8%A6%96%E3%81%99%E3%82%8B)にあるように、非サーバレスのクラスター(Advanced/Pro/Core)の場合、クラスターのオートスケールのイベントを取得することができます。サーバレスの場合はオートスケールのイベントは取得できません。

```sql
SELECT
  timestamp,
  Double(
    case
      when details :autoscale.status = 'RESIZING' then details :autoscale.requested_num_executors
      else null
    end
  ) as starting_num_executors,
  Double(
    case
      when details :autoscale.status = 'SUCCEEDED' then details :autoscale.requested_num_executors
      else null
    end
  ) as succeeded_num_executors,
  Double(
    case
      when details :autoscale.status = 'PARTIALLY_SUCCEEDED' then details :autoscale.requested_num_executors
      else null
    end
  ) as partially_succeeded_num_executors,
  Double(
    case
      when details :autoscale.status = 'FAILED' then details :autoscale.requested_num_executors
      else null
    end
  ) as failed_num_executors
FROM
  event_log_raw,
  latest_update
WHERE
  event_type = 'autoscale'
  AND
  origin.update_id = latest_update.id
```

|timestamp|starting_num_executors|succeeded_num_executors|partially_succeeded_num_executors|failed_num_executors|
|---|---|---|---|---|
|2025-06-23T05:58:32.644+00:00|null|5|null|null|
|2025-06-23T05:53:32.041+00:00|5|null|null|null|
|2025-06-23T06:08:32.030+00:00|3|null|null|null|
|2025-06-23T06:11:12.278+00:00|null|3|null|null|

## コンピュートリソース利用状況

[こちら](https://docs.databricks.com/aws/ja/dlt/observability#%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E5%88%A9%E7%94%A8%E7%8A%B6%E6%B3%81%E3%81%AE%E3%83%A2%E3%83%8B%E3%82%BF%E3%83%AA%E3%83%B3%E3%82%B0)のクエリーを用いることでキュータスクサイズの履歴を表示できます。

```sql
SELECT
  timestamp,
  Double(details :cluster_resources.avg_num_queued_tasks) as queue_size
FROM
  event_log_raw,
  latest_update
WHERE
  event_type = 'cluster_resources'
  AND
  origin.update_id = latest_update.id
```

|timestamp|queue_size|
|---|---|
|2025-06-23T05:52:31.774+00:00|0|
|2025-06-23T05:59:31.740+00:00|56.406023433723895|
|2025-06-23T06:02:31.738+00:00|108.47918333333334|
|2025-06-23T06:03:31.739+00:00|103.99345|
|2025-06-23T05:58:31.738+00:00|120.90056500941682|
|2025-06-23T05:54:31.740+00:00|289.80731345522423|
|2025-06-23T05:56:31.738+00:00|140.91008183469725|
|2025-06-23T05:57:31.738+00:00|47.24628333333333|
|2025-06-23T06:10:31.739+00:00|0|
|2025-06-23T06:09:31.739+00:00|0|
|2025-06-23T05:55:31.738+00:00|208.4818|
|2025-06-23T06:17:31.737+00:00|0|
|2025-06-23T05:53:31.738+00:00|308.21358333333336|
|2025-06-23T06:04:31.738+00:00|1.8332972216203602|
|2025-06-23T06:00:31.738+00:00|226.39612326872114|
|2025-06-23T06:16:31.738+00:00|0|
|2025-06-23T06:08:31.738+00:00|0|
|2025-06-23T06:01:31.738+00:00|86.24281666666667|
|2025-06-23T06:05:31.738+00:00|0|
|2025-06-23T06:07:31.738+00:00|0|
|2025-06-23T06:15:31.738+00:00|0|
|2025-06-23T06:12:31.737+00:00|0|
|2025-06-23T06:13:31.737+00:00|0|
|2025-06-23T06:11:31.738+00:00|0|
|2025-06-23T06:06:31.738+00:00|0|
|2025-06-23T06:14:31.738+00:00|0|

以下のクエリーでスロットの使用率を確認できます。

```sql
SELECT
  timestamp,
  Double(details :cluster_resources.avg_task_slot_utilization) as utilization
FROM
  event_log_raw,
  latest_update
WHERE
  event_type = 'cluster_resources'
  AND
  origin.update_id = latest_update.id
```

|timestamp|utilization|
|---|---|
|2025-06-23T05:52:31.774+00:00|null|
|2025-06-23T05:59:31.740+00:00|0.7832797213286888|
|2025-06-23T06:02:31.738+00:00|0.835345|
|2025-06-23T06:03:31.739+00:00|0.9998666666666667|
|2025-06-23T05:58:31.738+00:00|0.8832380539675662|
|2025-06-23T05:54:31.740+00:00|0.9998999983333056|
|2025-06-23T05:56:31.738+00:00|0.9999333322222037|
|2025-06-23T05:57:31.738+00:00|0.9998833333333333|
|2025-06-23T06:10:31.739+00:00|0|
|2025-06-23T06:09:31.739+00:00|0|
|2025-06-23T05:55:31.738+00:00|0.9999|
|2025-06-23T06:17:31.737+00:00|0|
|2025-06-23T05:53:31.738+00:00|0.8333541666666666|
|2025-06-23T06:04:31.738+00:00|0.18123635393923232|
|2025-06-23T06:00:31.738+00:00|0.9998833313888564|
|2025-06-23T06:16:31.738+00:00|0|
|2025-06-23T06:08:31.738+00:00|0|
|2025-06-23T06:01:31.738+00:00|0.8999162500000001|
|2025-06-23T06:05:31.738+00:00|0|
|2025-06-23T06:07:31.738+00:00|0|
|2025-06-23T06:15:31.738+00:00|0|
|2025-06-23T06:12:31.737+00:00|0|
|2025-06-23T06:18:31.738+00:00|0|
|2025-06-23T06:19:31.738+00:00|0|
|2025-06-23T06:13:31.737+00:00|0|
|2025-06-23T06:11:31.738+00:00|0|
|2025-06-23T06:06:31.738+00:00|0|
|2025-06-23T06:14:31.738+00:00|0|

以下のクエリーでエグゼキューター数などのメトリクスを取得できます。

```sql
SELECT
  timestamp,
  Double(details :cluster_resources.num_executors) as current_executors,
  Double(details :cluster_resources.latest_requested_num_executors) as latest_requested_num_executors,
  Double(details :cluster_resources.optimal_num_executors) as optimal_num_executors,
  details :cluster_resources.state as autoscaling_state
FROM
  event_log_raw,
  latest_update
WHERE
  event_type = 'cluster_resources'
  AND
  origin.update_id = latest_update.id
```

|timestamp|current_executors|latest_requested_num_executors|optimal_num_executors|autoscaling_state|
|---|---|---|---|---|
|2025-06-23T05:52:31.774+00:00|1|null|1|CLUSTER_AT_DESIRED_SIZE|
|2025-06-23T05:59:31.740+00:00|5|null|11|BLOCKED_FROM_SCALING_UP_BY_CONFIGURATION|
|2025-06-23T06:02:31.738+00:00|5|null|15|BLOCKED_FROM_SCALING_UP_BY_CONFIGURATION|
|2025-06-23T06:03:31.739+00:00|5|null|12|BLOCKED_FROM_SCALING_UP_BY_CONFIGURATION|
|2025-06-23T05:58:31.738+00:00|5|5|7|SCALE_UP_IN_PROGRESS_WAITING_FOR_EXECUTORS|
|2025-06-23T05:54:31.740+00:00|1|5|11|SCALE_UP_IN_PROGRESS_WAITING_FOR_EXECUTORS|
|2025-06-23T05:56:31.738+00:00|1|5|17|SCALE_UP_IN_PROGRESS_WAITING_FOR_EXECUTORS|
|2025-06-23T05:57:31.738+00:00|1|5|12|SCALE_UP_IN_PROGRESS_WAITING_FOR_EXECUTORS|
|2025-06-23T06:10:31.739+00:00|3|3|2|SCALE_DOWN_IN_PROGRESS_WAITING_FOR_EXECUTOR_REMOVAL|
|2025-06-23T06:09:31.739+00:00|4|3|3|SCALE_DOWN_IN_PROGRESS_WAITING_FOR_DECOMMISSIONING|
|2025-06-23T05:55:31.738+00:00|1|5|20|SCALE_UP_IN_PROGRESS_WAITING_FOR_EXECUTORS|
|2025-06-23T06:17:31.737+00:00|2|2|2|SCALE_DOWN_IN_PROGRESS_WAITING_FOR_EXECUTOR_REMOVAL|
|2025-06-23T05:53:31.738+00:00|1|null|1|CLUSTER_AT_DESIRED_SIZE|
|2025-06-23T06:04:31.738+00:00|5|null|12|BLOCKED_FROM_SCALING_UP_BY_CONFIGURATION|
|2025-06-23T06:00:31.738+00:00|5|null|11|BLOCKED_FROM_SCALING_UP_BY_CONFIGURATION|
|2025-06-23T06:16:31.738+00:00|3|null|3|CLUSTER_AT_DESIRED_SIZE|
|2025-06-23T06:08:31.738+00:00|5|null|5|CLUSTER_AT_DESIRED_SIZE|
|2025-06-23T06:01:31.738+00:00|5|null|14|BLOCKED_FROM_SCALING_UP_BY_CONFIGURATION|
|2025-06-23T06:05:31.738+00:00|5|null|9|BLOCKED_FROM_SCALING_UP_BY_CONFIGURATION|
|2025-06-23T06:07:31.738+00:00|5|null|5|CLUSTER_AT_DESIRED_SIZE|
|2025-06-23T06:15:31.738+00:00|3|null|3|CLUSTER_AT_DESIRED_SIZE|
|2025-06-23T06:12:31.737+00:00|3|null|3|CLUSTER_AT_DESIRED_SIZE|
|2025-06-23T06:20:31.738+00:00|2|null|2|CLUSTER_AT_DESIRED_SIZE|
|2025-06-23T06:18:31.738+00:00|2|null|2|CLUSTER_AT_DESIRED_SIZE|
|2025-06-23T06:19:31.738+00:00|2|null|2|CLUSTER_AT_DESIRED_SIZE|
|2025-06-23T06:21:31.738+00:00|2|null|2|CLUSTER_AT_DESIRED_SIZE|
|2025-06-23T06:13:31.737+00:00|3|null|3|CLUSTER_AT_DESIRED_SIZE|
|2025-06-23T06:11:31.738+00:00|3|null|3|CLUSTER_AT_DESIRED_SIZE|
|2025-06-23T06:06:31.738+00:00|5|null|5|CLUSTER_AT_DESIRED_SIZE|
|2025-06-23T06:14:31.738+00:00|3|null|3|CLUSTER_AT_DESIRED_SIZE|



### はじめてのDatabricks

[はじめてのDatabricks](https://qiita.com/taka_yayoi/items/8dc72d083edb879a5e5d)

### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)