BigQueryで使用されているZetaSQLをビルドする ~ 技術とマインドの両面で学びがあった ~

# 1. はじめに
こんにちは！アンパンが大好物のReiReiです！以後お見知りおきを 💁‍♂️

私は現在、内定者アルバイターとしてファッション業界のSRE/バックエンドチームに所属しています。今回はそこでやらせてもらったタスクについて学びが多かったのでアウトプットします。

**目的：** BigQueryで使用されているZetaSQLをLinux上でビルドする

**結論：** 技術とマインドの両面で学びがあった

SQL静的解析ツールであるZetaSQLのビルドの方法、その過程で学んだことをまとめていこうと思います。出だしからこんなことをいうのもアレですがほとんどの人が聞いたことない技術な気がするので「そんな技術もあるんだな〜〜」くらいの軽い気持ちで呼んでいただけると幸いです 🦖

# 2. 登場する技術の紹介
**使用技術ざっくり紹介**🤞

**環境：**
・Java 17.0.5(OpenJDK)
・Maven 3.6.3
・Ubuntu 22.04.1 LTS

**Google Clud：**
・BigQuery
・Compute Engine

**OSS：**
・Zeta SQL

Compute Engineの中に Ubuntuのインスタンスを立てて環境構築しました

**詳しめに紹介**🖖
・Maven とは
>Mavenは、Javaのビルドツールで、プログラムの開発やビルド、テスト、デプロイなどの作業を自動化してくれます。ビルドツールがあるとなにが嬉しいのかというと、ビルドをコマンド一発でやってくれることと、依存関係を解決してくれることです。最初この説明をみて僕はハテナマークが頭に浮かんでいたので同じ状態になった方には下記の記事が参考になると思います！
>
>一言でいうと、Java単体でもビルドはできるが、じつは複数の工程を行っているビルド(コンパイル→テスト→リンク→実行)をコマンド一発でやってくたり、ライブラリ同士の絡まりすぎた知恵の輪みたいな依存関係を勝手に解決してくれる便利屋さん🥰

https://qiita.com/Mura-Mi/items/225825cc3715dc04d923


・BigQuery とは

>Googleが提供するクラウドベースのデータウェアハウスサービスで、大規模なデータセットに高速にクエリを実行できます。具体的には、BigQueryは、Webコンソール上で数千、数百万、あるいは数十億のレコードを含む複雑なデータセットを分析できるようになっていて、SQLを使用して簡単にクエリを作成することができます。
>
>ここでポイントだったのはBigQueryでの一番大きい単位が「プロジェクト」であることです。次に「データセット」、最後に「テーブル」です。僕はRDBMS(MySQLなど)の経験しかなく、RDBMS場合は一番大きい単位が「データベース」次に「テーブル」なので、認識にずれがあり自分の中でつまずいたポイントでした。
>
> 一言でいうと、データセット(データベース)に対して、SQLを打てます😚 

・Compute Engine とは

>こちらも、Googleが提供するクラウドベースの仮想マシンサービスで、アプリケーションやウェブサイトを実行することができます。今回はCompute Engineを使ってLinux(Ubuntu)のインスタンスを立てました。なぜ、ローカルでビルドせずにLinuxのインスタンスを立てたかは後ほど説明します。
>
>一言でいうと、VM(バーチャルマシン)とか仮想サーバーとかって呼ばれるものをたれられます😗

・ZetaSQL とは

>こちらも、安心のGoogle製です。ZetaSQLは、SQLの解析・分析用フレームワークです。BigQueryでも使われてたりします。これだけの説明だとさっぱりだと思われますので、サンプルコードを使って説明します。
>
>下記のようなSQLクエリがあるとき
```SQL:Sample.sql
SELECT
  t1.col1
FROM 
  `MY_PROJECT.MY_DATASET.test_table_1` t1
LEFT JOIN
  `MY_PROJECT.MY_DATASET.test_table_2` t2 ON t1.unique_key=t2.unique_key
WHERE
 t1.col2 is not null
 AND t2.col2 is not null;
```
>こんな感じで解析・分析してくれるのが ZetaSQLです
```SQL:Sample-Output.sql
{tableScans=[{joinColumns=[unique_key], table=MY_PROJECT.MY_DATASET.test_table_1, filterColumns=[status], joinType=}, {joinColumns=[unique_key], table=MY_PROJECT.MY_DATASET.test_table_2, filterColumns=[status], joinType=LEFT}]}
```
>最初に見たときはむしろ見づらくなっていないか...とおもった私ですが、クエリの構造を把握できることがZetaSQLを使って嬉しことです！！
>
>一言でいうと、SQLクエリを解析してくれてクエリの構造を把握することができるのがZetaSQL🤗

# 3. ZetaSQLをビルドするまでの流れ
前置きが長かったですが、いよいよ本題です 😇
改めて、目的を確認します。

**目的：** BigQueryで使われているZetaSQLをLinux上でビルドする

具体的に言うと、下記GithubリポジトリのJavaファイルをビルドします👇

https://github.com/GoogleCloudPlatform/professional-services/blob/main/tools/zetasql-helper/src/main/java/com/pso/bigquery/optimization/BuildCatalogBasedOnQueryAndAnalyzeJoins.java

**最初に全体の流れを確認：** 
1. Compute Engineのインスタンスを建てる
1. インスタンスにSSHでログインする
1. Ubuntu初期設定
1. Open JDKのインストール
1. Mavenのインストール
1. Mavenにmaven-assembly-pluginを追加
1. ZetaSQLのビルドに必要なディレクトリをGithubからclone
1. `BuildCatalogBasedOnQueryAndAnalyzeJoins.java `をビルド

## 3-1.Compute Engineのインスタンスを建てる
インスタンスはLinux系のOSなら基本的に何でもおｋだと思います。僕がたてたインスタンスは以下のような感じ👇
* マシンタイプ = e2-standard-2（2 vCPU、8 GB メモリ）
* ブートディスク = Ubuntu 20.04 LTS サイズ10GB

その他にも設定項目はいくつかあると思いますが、人それぞれだと思うのでZetaSQLをビルドするために必要な設定は上記の2つかとおもいます！

ここの設定では特にブートディスクでLinux系のOSを選ぶことが最重要で、なぜかというとZetaSQLはLinux上でしか動かないとRead.meに表記があるからです👇 (最初、MacOS上でビルドしようとして全然動きませんでした...詳細は '4. こんなビルドは失敗する'で紹介します)

![noname.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1073251/9e82333e-f0b6-0888-b793-02470ed4214d.png)

## 3-2. インスタンスにSSHでログインする
インスタンスがたってさえしまえば、SSHでログインするのは簡単です。AWSのEC2にログインするときはコマンドをしらべて打っていたような気がするのですがCompute Engineさんは優しい子でSSH用のコマンドをコピーすることができます！

VMインスタンス→SSHボタンの横の▼→gcloudコマンドを表示を押すとSSHのコマンドがコピーできます！！(便利！！)
![noname.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1073251/66d7f2e8-23a1-e12e-752b-83162c40df86.png)

下のようなコマンドが表示されるはずです！

```terminal:ssh command
gcloud compute ssh --zone "リージョン" "インスタンス名"  --tunnel-through-iap --project "プロジェクト名"
```

ってなわけで、SSHでログインすることができるようになりました！

## 3-3. Ubuntu初期設定
続けてUbuntuの初期設定をしてきます。最低限必要なものだけ設定してきます。

・パッケージリストの更新・インストール済みのパッケージの更新
```shell:shell
$ sudo apt update 
$ sudo apt upgrade -y
```

・タイムゾーンの設定

現在のタイムゾーンを確認する
```shell:shell
$ timedatectl status

//output
               Local time: Thu 2023-01-26 07:49:06 UTC
           Universal time: Thu 2023-01-26 07:49:06 UTC
                 RTC time: Thu 2023-01-26 07:49:06
                Time zone: Etc/UTC (UTC, +0000)

```
日本時間に変更します

```shell:shell
$ sudo timedatectl set-timezone 'Asia/Tokyo' // 変更
$ timedatectl status //確認

//output
               Local time: Thu 2023-01-26 16:49:06 JST
           Universal time: Thu 2023-01-26 07:49:06 UTC
                 RTC time: Thu 2023-01-26 07:49:06
                Time zone: Asia/Tokyo (JST, +0900)

```
これで日本時間になりました！！

・ロケールの変更

現在のロケールを確認する
```shell:shell
$ localectl list-locales

C.UTF-8
en_US.utf8
```
日本語ロケールである`ja_JP.UTF-8`がインストールされてないのでインストール
```shell:shell
$ sudo apt install -y language-pack-ja //インストール
$ localectl list-locales //確認

C.UTF-8
en_US.utf8
ja_JP.utf8
```
これで日本語ロケールが追加された。けれどまだ追加されただけなので今度はデフォルトのロケールに設定します！

現状設定されているロケールを確認してみる
```shell:shell
$ localectl status

   System Locale: LANG=C.UTF-8
```

`C.UTF-8`がデフォルトのロケールのままなので、`ja_JP.utf8`に変更します！

```shell:shell
$ sudo localectl set-locale LANG=ja_JP.UTF-8 //変更
$ localectl status //確認

   System Locale: LANG=ja_JP.UTF-8
```
これで安心、日本語製のUbuntuちゃんになりました 🤗

・Gitのインストール

`apt-get`コマンドでGitをインストール
```shell:shell
$ sudo apt-get install git

```

・Gitの初期設定
```shell:shell
$ git config --global user.name [任意のユーザ名]
$ git config --global user.email [任意のユーザ名]
```

・ZetaSQLのビルドに必要な必要なリポジトリをclone
```shell:shell
$ git clone https://github.com/GoogleCloudPlatform/professional-services.git 
```

こんな感じでUbuntuの最低限の初期設定は完了🫡

## 3-4. Open JDKのインストール
・パッケージリストの更新、バージョンを指定してOpenJDK(ver 17)をインストール
```shell:shell
$ sudo apt update 
$ sudo apt install -y openjdk-17-jdk
```
少々時間がかかるので、辛抱強く待ちましょう！

・インストールできたか確認
```shell:shell
$ java --version 

//output
openjdk 17.0.5 2022-10-18
OpenJDK Runtime Environment (build 17.0.5+8-Ubuntu-2ubuntu122.04)
OpenJDK 64-Bit Server VM (build 17.0.5+8-Ubuntu-2ubuntu122.04, mixed mode, sharing)
```
バージョンが表示されれば無事インストールが完了しております！

## 3-5. Mavenのインストール
・まずは毎度おなじみパッケージ更新
```shell:shell
$ sudo apt update 
```

・Mavenのインストール
```shell:shell
$ sudo apt install maven
```
こちらも、少々時間がかかるので辛抱強く待ちましょう！

・インストールできたか確認
```shell:shell
$ mvn -version

//Output
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 17.0.5, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.15.0-1027-gcp", arch: "amd64", family: "unix"
```

これでMavenも無事インストールされました！！

## 3-6. Mavenに maven-assembly-pluginを追加
最初に、`maven-assembly-plugin`とはなんぞやって話ですが、一言でいうと「外部ライブラリをすべて含めたjarを作成するため」に使用します。なぜ、外部ライブラリをすべて含める必要があるのかというと、当然の話しではあるのですが必要な外部ライブラリがないとエラーが発生するからです。

これだけだと私は最初何を言っているかさっぱりだったので、理解できるようにもう少し詳細を書いていきます。理解するためにはMavenのビルドについて理解する必要がありました。

まず、Mavenを使ってビルドを行うときは`mvn package`コマンドを使います。このコマンドは以下のことを実行しています。
- clean （一時ファイルの削除）
- validate （プロジェクトの状態確認）
- compile （プロジェクトのコンパイル）
- test-compile （テストコードのコンパイル）
- test （ユニットテストの実行）
- package （アーティファクトの作成）

この工程を経て、1つの.jarファイルが生成されるわけですがMavenのデフォルト設定だと外部ライブラリを含めた.jarを作成してくれません。なぜかというと本来Mavenのビルドの目的がプロジェクト内の依存関係を解決することにあるからです。その依存関係の解決は`pom.xml`ファイルの記述内容をMavenが理解して、よしなに依存関係を解決してくれます。

ちなみに外部ライブラリってのは、intelliJ IDEAでいう赤枠の子たちです。

![noname.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1073251/e4f01e65-d025-c94f-30f8-c07de3468b63.png)

ローカルでintelliJ IDEAとMavenを使ってビルドするときはボタン１発なので意識しないのですが、CLIでやるとこの子達が含まれていないことに気づかず僕の場合はだいぶ詰まりました。

なので、まとめると画像の赤枠の外部ライブラリを含めて１つの.jarを作るために`maven-assembly-plugin`が必要になってくるわけです！

では、実際に`maven-assembly-plugin`をMavenに追加していきます！といっても簡単で`pom.xml`に下記コードを追加するだけです！！

```text:~/professional-services/tools/zetasql-helper/pom.xml
	 <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <version>3.1.0</version>
        <configuration>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>
        <executions>
          <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
```
そうすると、Mavenがよしなに解決してくれます！！

これで、外部ライブラリをすべて含めたjarを作成することができるようになりました！！

## 3-7. `BuildCatalogBasedOnQueryAndAnalyzeJoins.java `をビルド
いよいよビルドしてきます！！

あらためて、ビルドしようとしているファイルのファイルパスの確認、ちょっと長いですがここにあります👇
```shell:shell
professional-services/tools/zetasql-helper/src/main/java/com/pso/bigquery/optimization/BuildCatalogBasedOnQueryAndAnalyzeJoins.java 
```

中身はこれです👇
```java:BuildCatalogBasedOnQueryAndAnalyzeJoins.java 
package com.pso.bigquery.optimization;

import com.google.zetasql.SimpleCatalog;
import com.pso.bigquery.optimization.analysis.QueryAnalyzer;
import com.pso.bigquery.optimization.analysis.QueryAnalyzer.CatalogScope;
import com.pso.bigquery.optimization.analysis.visitors.ExtractScansVisitor;
import com.pso.bigquery.optimization.analysis.visitors.ExtractScansVisitor.QueryScan;
import com.pso.bigquery.optimization.catalog.BigQueryTableService;
import com.pso.bigquery.optimization.catalog.CatalogUtils;
import io.vavr.control.Try;
import java.util.List;

public class BuildCatalogBasedOnQueryAndAnalyzeJoins {
  public static void main(String[] args) {
    String PROJECT_ID = "MY_PROJECT";
    // add a query that references actual tables in your projets
    String QUERY =
        "SELECT \n"
            + "  t1.col1 \n"
            + "FROM \n"
            + "  `MY_PROJECT.MY_DATASET.test_table_1` t1\n"
            + "LEFT JOIN\n"
            + "  `MY_PROJECT.MY_DATASET.test_table_2` t2 ON t1.unique_key=t2.unique_key\n"
            + "WHERE\n"
            + " t1.col2 is not null\n"
            + " AND t2.col2 is not null\n";

    //BigQueryTableServiceクラスのインスタンスを作成。BigQueryのテーブルを扱うためのAPI
    BigQueryTableService bigQueryTableService = BigQueryTableService.buildDefault();

    //QueryAnalyzerクラスのインスタンスを作成
    QueryAnalyzer parser = new QueryAnalyzer(bigQueryTableService);

    //空のカタログを作成
    SimpleCatalog catalog = CatalogUtils.createEmptyCatalog();

    //`parser`を使用して、指定されたクエリに含まれるテーブルのスキャン情報を抽出
    Try<List<QueryScan>> tryScans = parser.getScansInQuery(PROJECT_ID, QUERY, catalog, CatalogScope.QUERY);

    //`tryScans`から抽出されたスキャン情報を、List<ExtractScansVisitor.QueryScan>型のオブジェクトに代入
    List<ExtractScansVisitor.QueryScan> scanResults = tryScans.get();

    //`scanResults`から抽出されたスキャン情報を出力
    scanResults.stream()
        .map(ExtractScansVisitor.QueryScan::toMap)
        .forEach(scanResult -> System.out.println(scanResult));
  }
}
```
ソースコードの細かい説明は割愛させてください。ビルドが目的なのと細かく説明するととんでもない分量になりそうなので...実際に変更するべき場所は`QUERY`(文字列変数)の中身です。ここを実際に存在するテーブルを参照しないとエラーになります。

仮にこんなプロジェクトがあるとすると...
・プロジェクト名 = FOO_PROJECT
・データセット名 = BAR_DATASET
・テーブル名 = fuga_table_1, fuga_table_2

このように変更します👇
```java:BuildCatalogBasedOnQueryAndAnalyzeJoins.java 
// ↑ 省略

public class BuildCatalogBasedOnQueryAndAnalyzeJoins{

  public static void main(String[] args) throws InterruptedException {

    //実際に存在するプロジェクト名に変更(ただ、プロジェクト名は変更しなくてもビルドは成功します)
    String PROJECT_ID = "FOO_PROJECT";

    // プロジェクト内の実際のテーブルを参照するクエリを追加する。(ここだけ変更すれば動く)
    String QUERY =
        "SELECT \n"
            + "  t1.col1 \n"
            + "FROM \n"
            + "  `FOO_PROJECT.BAR_DATASET.fuga_table_1` t1\n"
            + "LEFT JOIN\n"
            + "  `FOO_PROJECT.BAR_DATASET.fuga_table_2` t2 ON t1.unique_key=t2.unique_key\n"
            + "WHERE\n"
            + " t1.col2 is not null\n"
            + " AND t2.col2 is not null\n";

 // ↓ 省略
```
ここでおもいだしてほしいのがBigQueryの紹介のときに説明した下記の情報です。実際のプロジェクトを指定するときは「プロジェクト」、「データセット」、「テーブル」の3つを指定する必要があります！
>BigQueryでの一番大きい単位が「プロジェクト」であることです。次に「データセット」、最後に「テーブル」です。僕はRDBMS(MySQLなど)の場合は一番大きい単位が「データベース」次に「テーブル」なので、自分の中でつまずいたポイントでも合ったので記載しときます！

`QUERY`変数のSQLクエリを実際に存在するBigQueryのデータセットからデータを抽出できるSQLクエリに変更したら準備完了なので外部ライブラリを含めた.jarを作成します。
```shell:shell
$ mvn package

#output
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  24.230 s
[INFO] Finished at: 2023-02-28T16:49:29+09:00
[INFO] ------------------------------------------------------------------------
```
"BUILD SUCCESS"が表示されれば無事に.jarが作成できています。ちなみに作成された.jarは`professional-services/tools/zetasql-helper/target/`の中に`query-pattern-analyzer-1.0-SNAPSHOT-jar-with-dependencies.jar`というファイル名で出来上がっています。

作成した.jar実行すればZetaSQLのビルドできます👇
```shell:shell
$ java -cp target/query-pattern-analyzer-1.0-SNAPSHOT-jar-with-dependencies.jar com.pso.bigquery.optimization.BuildCatalogBasedOnQueryAndAnalyzeJoins
```
ビルドが完了するとSQLクエリにはよるのですが下記のような解析結果が表示されるとビルド成功です！！
```shell:shell

{tableScans=[{joinColumns=[unique_key], table=FOO_PROJECT.BAR_DATASET.fuga_table_1, filterColumns=[status], joinType=}, {joinColumns=[unique_key], table=FOO_PROJECT.BAR_DATASET.fuga_table_2, filterColumns=[status], joinType=LEFT}]}

```

# 4. こんなビルドは失敗する
ここからはこんなビルドは失敗するよ~という失敗例を上げていきます。`3. ZetaSQLをビルドするまでの流れ`だけをみるとさらっと終わっているように見えるのですが、いくつかの失敗を経てビルドすることができました。

ググってもあまりビルドの失敗例がでてこないので書いていこうと思います！！


## 4-1 Mac上(ローカル)でビルド
mac上(ローカル)でビルドしようとすると下記例外が発生します。
```terminal:terminal
Exception in thread "main" java.util.ServiceConfigurationError: com.google.zetasql.ClientChannelProvider: Provider com.google.zetasql.JniChannelProvider could not be instantiated
	at java.base/java.util.ServiceLoader.fail(ServiceLoader.java:586)
	at java.base/java.util.ServiceLoader$ProviderImpl.newInstance(ServiceLoader.java:813)
	at java.base/java.util.ServiceLoader$ProviderImpl.get(ServiceLoader.java:729)
	at java.base/java.util.ServiceLoader$3.next(ServiceLoader.java:1403)
	at com.google.zetasql.ClientChannelProvider.loadChannel(ClientChannelProvider.java:31)
	at com.google.zetasql.Client.getStub(Client.java:29)
	at com.google.zetasql.LanguageOptions.getDefaultFeatures(LanguageOptions.java:57)
	at com.google.zetasql.LanguageOptions.<init>(LanguageOptions.java:65)
	at com.google.zetasql.ZetaSQLBuiltinFunctionOptions.<init>(ZetaSQLBuiltinFunctionOptions.java:46)
	at com.pso.bigquery.optimization.catalog.CatalogUtils.createEmptyCatalog(CatalogUtils.java:53)
	at com.pso.bigquery.optimization.BuildCatalogBasedOnQueryAndAnalyzeJoins.main(BuildCatalogBasedOnQueryAndAnalyzeJoins.java:46)
Caused by: java.lang.ExceptionInInitializerError
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
	at java.base/java.util.ServiceLoader$ProviderImpl.newInstance(ServiceLoader.java:789)
	... 9 more
Caused by: java.lang.RuntimeException: Unsupported os.arch
	at com.google.zetasql.JniChannelProvider.getLibraryPath(JniChannelProvider.java:48)
	at com.google.zetasql.JniChannelProvider.<clinit>(JniChannelProvider.java:66)
	... 15 more
```

ながながスタックトレースが表示されているますが簡単に言うとMacOS（M1）上でビルドしていることが問題で発生している例外でした。

余談ですが、Javaのスタックトレースを見ていると「... 15 more」とかで省略されていることがあります。最初に見たときにはこれ全部表示できないのかなと思ったものですが、これは見る必要がないから省略されているみたいで、それ以前のどこかで呼ばれている場合のみ省略されているので見なくてもいいからJavaがよしなに省略してくれているみたいです。(便利🥰)

下記のスタックオーバーフローの記事がとても参考になりました👇

https://stackoverflow.com/questions/1167888/how-do-i-increase-the-number-of-displayed-lines-of-a-java-stack-trace-dump

MacOS（M1）が原因だとわかるソースコードはスタックトレース内にも表示されている`JniChannelProvider.java:66`でした。下記ソースコードです。

```java:JniChannelProvider.java
    private static String getLibraryPath() {
        String path = System.getProperty("zetasql.local_service.path");
        if (path != null) {
            return path;
        } else {
            String arch = System.getProperty("os.arch");
            if (!"x86_64".equals(arch) && !"amd64".equals(arch)) {
                throw new RuntimeException("Unsupported os.arch");
            } else {
                path = "/zetasql/local_service/";
                String os = System.getProperty("os.name");
                if ("Linux".equals(os)) {
                    return path + "liblocal_service_jni.so";
                } else if ("Mac OS X".equals(os)) {
                    return path + "liblocal_service_jni.dylib";
                } else {
                    throw new RuntimeException("Unsupported os");
                }
            }
        }
    }
```
if文でOSごとに分岐されていて、このソースコードを基に関連するソースコードを読んでいけばなんとなくOSの問題であることがわかります。

スタックトレースに表示されたソースコードを地道に見ていく以外にも方法はあって、README.mdをちゃんと読もうという話なのですが、しっかりとPrerequisites(前提条件)のところに'Must run on Linux' との表記があります...

![noname.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1073251/9e82333e-f0b6-0888-b793-02470ed4214d.png)

僕はMacOSではだめなんだ！ということに気がつくのに4日ほどかかりました...同じような被害を受ける人が少しでもすくなるなることを祈ります🤞

## 4-2 外部ライブラリを含めずにビルド
これはビルド成功パターンで登場した maven-assembly-pluginを使用していなかったため、外部ライブラリが含まれておらずビルドが失敗しました。

スタックトレースはこれです👇
```terminal:
Exception in thread "main" java.lang.NoClassDefFoundError: com/google/api/gax/rpc/HeaderProvider
	at com.pso.bigquery.optimization.BuildCatalogBasedOnQueryAndAnalyzeJoins.main(BuildCatalogBasedOnQueryAndAnalyzeJoins.java:44)
Caused by: java.lang.ClassNotFoundException: com.google.api.gax.rpc.HeaderProvider
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
	... 1 more
```

スタックトレースを見ると 'NoClassDefFoundError: com/google/api/gax/rpc/HeaderProvider'ということで'HeaderProvider'というクラスが存在しないよ言っています。これもわかってしまえば単純なことで外部ライブラリが含まれていないことが原因で必要なクラスが存在しないために発生する例外でした。

ビルド成功パターンのときに初回した'maven-assembly-plugin'を追加して.jarを作成してあげると解決しました🫡


# 5. おわりに
ここまで長々と見ていただいてありがとうございました！！「いいね！」してもらえると次の記事を書く励みになります！清き１いいねお待ちしてます☺️

最後にどんな学びが合ったのかをつらつら書いていこうと思います！
今回やったZetaSQLのビルドでは初めてのことがとにかくたくさんありました。以下のような感じです👇

* Java自体読んだことも書いたこともなかった
* Maven(ビルドツール)を初めて触り、ビルド(コンパイル~実行)の流れを知った
* OSSを初めてガッツリ読んだ
* Google Cloudの BigQuery, Compute Engine初めて触った
* エラーが起きてググってもほとんど情報が出てこない状況が初めてだった

などなどたくさんのはじめましての出会いがありました。特に出会えてよかったと思ったのは'エラーが起きてググってもほとんど情報が出てこない状況が初めてだった'ことです。

普段はエラーが出ればとにかくググってなんとかエラーを解決しようとしていてソースコードからエラーの原因を探るなんてことはやったことがありませんでした。ソースコードからエラーの原因を探らざる負えない状況になりソースコードを読む力はグーンと上がったと思うし、改めてソースコードを読むことの大切さに気づけました。

また、エラーにつまってチームのテックリードの方に相談した時にいただいたアドバイスが僕の中でものすごく響きました。

👨‍💻 「エラーが出た時にググったり、リファレンスをみたりするけど、そもそもソースコードに書いてあること以上のことは起こらないのだからソースコードが１次情報だよ」

とアドバイスをいただきました。あたりまえと言ってしまえばそれで片付いちゃうかもしれないのですが普段ググったり、リファレンスを読むことが全てだった僕からするととても響いたアドバイスで今後もエラーでたときはしっかりソースコードから原因を探ることを大切にしようと思いました。

そんなこんなで、様々なはじめての技術に触れてたくさんの学びがあったし、エラーがでたときはソースコードが１次情報だと考え、原因を探るというエンジニアとしてのマインドの両面ですごく学びがありました！！

今回の記事は以上~~!!また、お会いしましょう！！バイバイ👋