HIRO Tracks

ソフトウェアエンジニアが日々学んだ知識を発信します。

【反省】AWS Batch Clientのメモリ解放を忘れて処理が遅い【AWS SDK for Java v2】

これは何?

Kotlinで書いたAPI内からAWS Batchを起動するために、sdk-for-javaを使って処理を書いていました。

sdk-for-javaはお手軽にバッチ起動ができて大変便利なのですが、リソースをちゃんと解放できるように書いてあげないと性能劣化の原因になります。本記事はその時の反省話です。

伝えたいこと「BatchClient、ちゃんとcloseしよう」

  • AWS SDK for Java v2のBatchClientクラスはFileInputStream等と同じくClose可能なクラス。
  • AutoClosableインターフェースを実装しているため、try-with-resources(java)やuse(kotlin)を使うことで明示的にcloseしなくてもclose可能。
  • ちゃんとCloseしないと性能劣化の原因なるよ!!!!

SDK・Maven Repository

sdk.amazonaws.com

Maven・Repository

https://mvnrepository.com/artifact/software.amazon.awssdk/batch

ソースコード例

NG(Closeできてないやつ)

// 認証情報作成
val credentials: AwsBasicCredentials  = AwsBasicCredentials.create("dummy","dummy")
val provider: StaticCredentialsProvider = StaticCredentialsProvider.create(credentials)

// クライアント作成
val client: BatchClient = BatchClient.builder().credentialsProvider(provider).build()

val request: SubmitJobRequest = SubmitJobRequest.builder()
        .jobName("jobName")
        .jobDefinition("jobDefinitionName")
        .jobQueue("jobQueueName")
        .build()

val response: SubmitJobResponse = client.submitJob(request)

OK(Closeできているやつ)

kotlinのuse句を使う!

val credentials: AwsBasicCredentials  = AwsBasicCredentials.create("dummy","dummy")
val provider: StaticCredentialsProvider = StaticCredentialsProvider.create(credentials)
        
// クライアント作成
val client: BatchClient = BatchClient.builder().credentialsProvider(provider).build()

client.use { //use句使って自動でClose
     val request: SubmitJobReuqest = SubmitJobRequest.builder()
     .jobName("jobName")
     .jobDefinition("jobDefinitionName")
     .jobQueue("jobQueueName")
     .build()

     val response: SubmitJobResponse = client.submitJob(request)
}

OK(Closeできているやつ、Java版)

try-with-resourcesを使う!

// 認証情報作成
AwsBasicCredentials credentials = AwsBasicCredentials.create("dummy","dummy");
StaticCredentialsProvider provider = StaticCredentialsProvider.create(credentials);

// クライアント作成
try(BatchClient client = BatchClient.builder().credentialsProvider(provider).build();) {

    SubmitJobRequest request = SubmitJobRequest.builder()
            .jobName("jobName")
            .jobDefinition("jobDefinitionName")
            .jobQueue("jobQueueName")
            .build();

    SubmitJobResponse response = client.submitJob(request);
}

やってないとどうなるの?

全体の処理の規模感にもよりますが...山根の場合は、

メモリ解放されない => 解放するためにガベージコレクションがいっぱい走る => ガベージコレクションにCPU使っちゃって本来の処理が遅延。

みたいなことが発生しました。

その他、「そもそもなんでCloseしないとダメなん?」話は以下の記事に譲ります。

www.kab-studio.biz

mtaketani113.github.io

まとめ

JVMerとしてめちゃくちゃ当たり前の「CloseできるオブジェクトはCloseしよう」原則を、AWSのSDK使ってる時に忘却しちゃいましたというお話でした。

S3のClientとかだとよく使うので閉じ忘れはないのですが、Batchだとあまり使わないために意識の外にいっちゃってましたね。反省。