SpringBootのテストをJenkinsで行っていたんですが、
ある日OutOfMemoryErrorで落ちて如何ともし難くなりました。かなしみ。
一応、何度かトライして復旧できたので、ログを残しておきます。
環境
- Java8
- CentOS7
- Jenkins
- JUnit5
①ヒープを増やす
java.lang.OutOfMemoryError: GC overhead limit exceeded
とあり、Jenkins-SlaveでUnitTestがOutOfMemory (@fake-deli-ca) の記事によるとtestタスクはプロセスが別になって実行される(へー知らなかった)ということなので、そのプロセスのヒープサイズの割当を増やせば良さそうと。
23.13.1. テストの実行
テストの実行は別のJVMで行われ、メインのビルドプロセスからは分離されます。TestタスクのAPIを使って、テストの実行を制御できます。
なので、testタスクに以下の記述を追記して、再実行。
test {
useJUnitPlatform()
maxHeapSize = '2G'
}
OOMは発生しなくなったけど、テスト結果の保存に失敗。
設定は変えてないんですけどー
BUILD SUCCESSFUL in 32m 45s
7 actionable tasks: 7 executed
Build step 'Invoke Gradle script' changed build result to SUCCESS
成果物を保存中
テスト結果を保存中
ERROR: Step ‘JUnitテスト結果の集計’ failed: テストのレポートファイルが見つかりませんでした。設定ミス?
次に進む。
②テストプロセスの設定の追記
testタスクに『-i』つけて実行すると、以下のログが出てました。
Successfully started process 'Gradle Test Executor 1'
Gradle Test Executor 1 STANDARD_ERROR
11 08, 2021 4:25:30 午後 org.junit.platform.launcher.core.DefaultLauncher handleThrowable
警告: TestEngine with ID 'junit-jupiter' failed to discover tests
java.lang.StackOverflowError
at org.junit.jupiter.engine.discovery.MethodOrderingVisitor.visit(MethodOrderingVisitor.java:55)
at org.junit.platform.engine.TestDescriptor.accept(TestDescriptor.java:249)
at org.junit.platform.engine.TestDescriptor.lambda$accept$0(TestDescriptor.java:251)
at java.lang.Iterable.forEach(Iterable.java:75)
at org.junit.platform.engine.TestDescriptor.accept(TestDescriptor.java:251)
at org.junit.platform.engine.TestDescriptor.lambda$accept$0(TestDescriptor.java:251)
at java.lang.Iterable.forEach(Iterable.java:75)
at org.junit.platform.engine.TestDescriptor.accept(TestDescriptor.java:251)
at org.junit.platform.engine.TestDescriptor.lambda$accept$0(TestDescriptor.java:251)
今度はStackOverflowですか。。でもヒープを上げて解消できる感じでもなさそうだしなぁと思い、プロセス周りで調べたところ
ここのサイトがわかりやすかったです。
Gradleのユニットテストを早くするためにやったこと
設定値としては以下がある模様。
maxParallelForks — default: 1
You can run your tests in parallel by setting this property to a value greater than 1. This may make your test suites complete faster, particularly if you run them on a multi-core CPU. When using parallel test execution, make sure your tests are properly isolated from one another. Tests that interact with the filesystem are particularly prone to conflict, causing intermittent test failures.Your tests can distinguish between parallel test processes by using the value of the org.gradle.test.worker property, which is unique for each process. You can use this for anything you want, but it’s particularly useful for filenames and other resource identifiers to prevent the kind of conflict we just mentioned.
forkEvery — default: 0 (no maximum)
This property specifies the maximum number of test classes that Gradle should run on a test process before its disposed of and a fresh one created. This is mainly used as a way to manage leaky tests or frameworks that have static state that can’t be cleared or reset between tests.Warning: a low value (other than 0) can severely hurt the performance of the tests
現在の設定値がなんのか、調べる術がわからなかったんですが、そんなにJenkinsやらGradleやらの設定を追加してるわけではないので、多分デフォルト値のはず。
最終的な記述
上記を踏まえて、以下の記述で通りました。
forkEveryもmaxHeapSizeもいらなかったような気もしなくはないですが、まああっても問題ないかと。
test {
useJUnitPlatform()
maxParallelForks = Runtime.runtime.availableProcessors()
forkEvery = 0L
maxHeapSize = '2G'
}
あー解決してよかった。当分これで様子を見ます。