JavaのGA4用クライアントがAWSのECSタスク上で実行したらクラッシュした件

こんにちは、エキサイト株式会社メディア事業部所属のエンジニア岩藤です。

私は現在、JavaでGoogleAnalytics4(以下GA4)へ移行をおこなっています。

今回は、JavaのGA4用クライアントがAWSのECSタスク上で実行した際にクラッシュした件とその対応方法について紹介します。

JavaのGA4クライアントについて

切り替えは下記を元に行いました。 developers.google.com github.com

動作確認環境

Java

openjdk 17.0.2 2022-01-18
OpenJDK Runtime Environment Temurin-17.0.2+8 (build 17.0.2+8)
OpenJDK 64-Bit Server VM Temurin-17.0.2+8 (build 17.0.2+8, mixed mode)

Gradle

------------------------------------------------------------
Gradle 7.4.1
------------------------------------------------------------

Build time:   2022-03-09 15:04:47 UTC
Revision:     36dc52588e09b4b72f2010bc07599e0ee0434e2e

Kotlin:       1.5.31
Groovy:       3.0.9
Ant:          Apache Ant(TM) version 1.10.11 compiled on July 10 2021
JVM:          17.0.2 (Eclipse Adoptium 17.0.2+8)
OS:           Mac OS X 12.6.3 aarch64

SpringBoot

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.6.6)

クラッシュが起きた状況

ローカル環境では問題なくGA4のレポートが取得できた事から、次にaws上のテスト環境にてECSタスクを実行しました。

2023-05-22T12:09:29.761+09:00    #
2023-05-22T12:09:29.761+09:00   # A fatal error has been detected by the Java Runtime Environment:
2023-05-22T12:09:29.761+09:00   #
2023-05-22T12:09:29.761+09:00   # SIGSEGV (0xb) at pc=0x00000000000207d6, pid=1, tid=20
2023-05-22T12:09:29.761+09:00   #
2023-05-22T12:09:29.761+09:00   # JRE version: OpenJDK Runtime Environment Temurin-17.0.2+8 (17.0.2+8) (build 17.0.2+8)
2023-05-22T12:09:29.761+09:00   # Java VM: OpenJDK 64-Bit Server VM Temurin-17.0.2+8 (17.0.2+8, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, serial gc, linux-amd64)
2023-05-22T12:09:29.761+09:00   # Problematic frame:
2023-05-22T12:09:29.761+09:00   # C 0x00000000000207d6
2023-05-22T12:09:29.761+09:00   #
2023-05-22T12:09:29.761+09:00   # Core dump will be written. Default location: //core.1
2023-05-22T12:09:29.761+09:00   #
2023-05-22T12:09:29.762+09:00   # An error report file with more information is saved as:
2023-05-22T12:09:29.762+09:00   # //hs_err_pid1.log
2023-05-22T12:09:29.796+09:00   #
2023-05-22T12:09:29.796+09:00   # If you would like to submit a bug report, please visit:
2023-05-22T12:09:29.796+09:00   # https://github.com/adoptium/adoptium-support/issues
2023-05-22T12:09:29.796+09:00   # The crash happened outside the Java Virtual Machine in native code.
2023-05-22T12:09:29.796+09:00   # See problematic frame for where to report the bug.

実行した時のログです。

ローカル環境ではGA4のPVレポートが問題なく取得できたのに、テスト環境ではJavaがクラッシュしていました。

クラッシュ発生場所

デバッグ調査したところクラッシュしたのは下記の処理です。

String credentialsJson = "{…}";
InputStream credentialsJson = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
GoogleCredentials googleCredentials = GoogleCredentials.fromStream(credentialsJson);

BetaAnalyticsDataSettings settings = BetaAnalyticsDataSettings.newBuilder()
    .setCredentialsProvider(FixedCredentialsProvider.create(googleCredentials))
    .build();

//この先でクラッシュが発生
return BetaAnalyticsDataClient.create(settings);

※なお私は今回、認証用JSON環境変数でパスを指定する方式ではなく、文字列で渡す方法を採用しています。

原因調査

BetaAnalyticsDataClient.create先をデバッグしたところ、処理の先でBetaAnalyticsDataStubSettingsというクラスのcreateStubメソッドが呼ばれていました。

BetaAnalyticsDataStubSettingsクラス

public BetaAnalyticsDataStub createStub() throws IOException {
    if (getTransportChannelProvider()
        .getTransportName()
        .equals(GrpcTransportChannel.getGrpcTransportName())) {
      return GrpcBetaAnalyticsDataStub.create(this);
    }
    if (getTransportChannelProvider()
        .getTransportName()
        .equals(HttpJsonTransportChannel.getHttpJsonTransportName())) {
      return HttpJsonBetaAnalyticsDataStub.create(this);
    }
    throw new UnsupportedOperationException(
        String.format(
            "Transport not supported: %s", getTransportChannelProvider().getTransportName()))
}

このcreateStubは通信方法を選択している処理のようです。

1つ目のif:gRPCを採用 ← 現状ではこちらが選択されていました。

2つ目のif:HTTP/JSONを採用

つまりgRPCが使えなくてクラッシュが発生しているようです。

対応方法

呼び出し元を下記に変更しました。

String credentialsJson = "{…}";
InputStream credentialsJson = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
GoogleCredentials googleCredentials = GoogleCredentials.fromStream(credentialsJson);

BetaAnalyticsDataSettings settings = BetaAnalyticsDataSettings.newBuilder()
    .setTransportChannelProvider(
        BetaAnalyticsDataSettings.defaultHttpJsonTransportProviderBuilder().build()
    )
    .setCredentialsProvider(FixedCredentialsProvider.create(googleCredentials))
    .build();

return BetaAnalyticsDataClient.create(settings);

変更箇所はBetaAnalyticsDataSettings生成時に下記が追加されています。

.setTransportChannelProvider(
    BetaAnalyticsDataSettings.defaultHttpJsonTransportProviderBuilder().build()
)

これを追加するとどうなるかと言うと、createStubメソッドの2つ目のifに入るようになります。

2つ目のif

if (getTransportChannelProvider()
    .getTransportName()
    .equals(HttpJsonTransportChannel.getHttpJsonTransportName())) {
    return HttpJsonBetaAnalyticsDataStub.create(this);
}

つまり、gRPCではなくHTTP/JSONを使って通信されるようになりました。

そして、再度テスト環境で実行したところ。無事成功!

gRPCを使えるようにする等、他の回避方法あるかもしれませんが今回は時間がないので上記を採用しました。

もし、同様のエラーの方がいたら参考頂けますと幸いです。

GA4では下記のブログも公開しています。 tech.excite.co.jp