gradle - 同时运行的 gradle/geb 测试挂起导致堆积和内存故障“无法获得文件锁定”。
问题描述
我提前为这篇文章的篇幅道歉。
我在增加我的 geb 测试套件中的任务数量时遇到了问题,其中 gradle 似乎无法同时处理多个并发构建。到目前为止,它已经成功运行了 4 年,似乎在我的系统中添加新任务已经达到了某种阈值。最初这开始看起来像是来自 selenoid 网格的连接错误,但在升级版本后,开始出现新的错误消息,使问题更加清晰。如果有人可以帮助解决这个问题,我将不胜感激,我已经做了一个月,似乎无法深入了解它。理想情况下,该解决方案将为进一步扩展留出空间,因为这似乎确实是并发任务的问题。
我会注意到减少计划任务的数量开始解决这个问题,但即使在这一点上,我也没有运行我想要的所有任务。
测试可以正常运行几个小时,然后出现问题的第一个迹象是通过此消息,显示在测试结束时:
Couldn't flush user prefs: java.util.prefs.BackingStoreException: Couldn't get file lock.
虽然我的测试设置为 7 分钟后超时(4 分钟是预期的运行时间),但构建开始需要越来越长的时间才能完成,从 25 分钟开始持续长达 4 小时(随着容器接近内存而增加限制)。
在其中一些消息之后,这些消息开始出现(敏感信息已编辑):
[redacted]TestReport: Timeout waiting to lock daemon addresses registry. It is currently in use by another Gradle instance.
Feb 18 15:10:35 ip-172-30-3-121 [redacted]TestReport: Owner PID: 21092
Feb 18 15:10:35 ip-172-30-3-121 [redacted]TestReport: Our PID: 21030
Feb 18 15:10:35 ip-172-30-3-121 [redacted]TestReport: Owner Operation:
Feb 18 15:10:35 ip-172-30-3-121 [redacted]TestReport: Our operation:
Feb 18 15:10:35 ip-172-30-3-121 [redacted]TestReport: Lock file: /root/.gradle/daemon/6.8.2/registry.bin.lock
我相信这与竞争锁定文件的任务有关,这些任务完成的时间越来越长,在前一个任务运行时启动了更多任务,最终导致 docker 容器完全耗尽内存。
每个 gradle 任务最多占用 400mb,但即便如此,也没有足够的任务为此安排,仅由于一次运行的任务太多而导致内存故障
除了 cron 任务的安排和包括我有哪些 gradle 设置之外,我认为没有太多代码可以帮助解决这个问题。
我尝试解决这个问题是从升级到最新的兼容版本开始的,这导致了 2 周的依赖地狱,所以版本号反映了这一点。
我试图解决这个问题的方法:
- 升级依赖版本
- 增加jvm堆大小,perm大小
- 将任务拆分为两个 docker 容器,每个容器运行计划的 17 个任务的一半
- 禁用 gradle 构建缓存 (--no-build-cache)
- 启用、禁用并行执行(build.gradle 中的 maxParallelForks 和 gradle.properties 中的 org.gradle.parallel)
- org.gradle.unsafe.configuration-cache=true 在 gradle.properties
- 将 selenoid 中的浏览器容器的最大数量增加到 18(减少连接池限制/等待连接的超时)
- 每个构建添加超时
- 改进 docker 清理以确保 ecs 容器不会被死容器淹没(docker system prune on a cron schedule)
- 每个任务单独的构建目录(以任务名称命名)
我认为会有所帮助的事情
- 一种为每个任务指定单独的缓存以避免争夺锁的方法,找不到实现这一点的方法
- java/maven 用户建议我尝试在同一个 JVM 中运行多个任务,但这在 gradle 中似乎是不可能的(据我所知,每个守护进程都有自己的 jvm),而且这些任务不能很好地共享构建空间.
代码和基础设施
堆:
- geb(3.0.1)
- 时髦的
- 分级(4.3.1 -> 6.8.2)
- 码头工人(运行 ubuntu)
- java (jre 1.8 -> jre 11/java 8 -> java 11)
- 硒(3.141.59 -> 4.0.0-alpha-7)
- 君特 (4.12)
- spock (1.1-groovy-2.4)
- 硒化网格(远程)
- docker 分发到 ECS 容器 (16gb)
系统工作原理的总体思路
- test (type: Test) 运行测试,生成并存储测试报告和相关信息
- 报告(JavaExec 任务)立即运行,将存储的信息发送到适当的位置(firestore 和 slack)
cron 计划
*/5 * * * * root cd /tests ; ./gradlew clean test --tests [test1] -Dtest.single=[test1] -DenableVideo=true -Denvironment=http://[hub address]/wd/hub -Dbrowser=chrome --no-build-cache --stacktrace --info 2>&1 | logger -t [test1] ; ./gradlew report --no-build-cache --stacktrace --info 2>&1 | logger -t [test1]Report
*/11 * * * * root cd /tests ; ./gradlew clean test --tests [test2] -Dtest.single=[test2] -DenableVideo=true -Denvironment=http://[hub address]/wd/hub -Dbrowser=chrome --no-build-cache --stacktrace --info 2>&1 | logger -t [test2] ; ./gradlew report -DclassName=[test2] --no-build-cache --stacktrace --info 2>&1 | logger -t [test2]Report
*/8 * * * * root cd /tests ; ./gradlew clean test --tests [test3] -Dtest.single=[test3] -Denvironment=http://[hub address]/wd/hub -DenableVideo=true -Dbrowser=chrome --no-build-cache --stacktrace --info 2>&1 | logger -t [test3] ; ./gradlew report -DclassName=[test3] --no-build-cache --stacktrace --info 2>&1 | logger -t [test3]Report
3 * * * * root cd /tests ; ./gradlew clean test --tests [test4] -Dtest.single=[test4] -DenableVideo=true -Denvironment=http://[redacted]/wd/hub -Dbrowser=chrome --no-build-cache --stacktrace --info 2>&1 | logger -t [test4] ; ./gradlew report -DclassName=[test4] --no-build-cache --stacktrace --info 2>&1 | logger -t [test4]Report
8 * * * * root cd /tests ; ./gradlew clean test --tests [test5] -Dtest.single=[test5] -DenableVideo=true -Denvironment=http://[redacted]/wd/hub -Dbrowser=chrome --stacktrace --info 2>&1 | logger -t [test5] ; ./gradlew report -DclassName=[test5] --no-build-cache --stacktrace --info 2>&1 | logger -t [test5]Report
gradle 基础设施(来自构建扫描)
Background build scan publication On
Build Cache On
Daemon On
Configuration Cache Off
Configure on demand Off
Continue on failure Off
Continuous Off
Dry run Off
File system watching Off
Offline Off
Parallel Off
Re-run tasks Off
Refresh dependencies Off
Task inputs file capturing Off
ecs/docker 基础设施
Operating system Linux 4.14.33-51.37.amzn1.x86_64
CPU cores 1 core
Max Gradle workers 1 worker
Java runtime Ubuntu OpenJDK Runtime Environment 11.0.10+9-Ubuntu-0ubuntu1.20.04
Java VM Ubuntu OpenJDK 64-Bit Server VM 11.0.10+9-Ubuntu-0ubuntu1.20.04 (mixed mode, sharing)
Max JVM memory heap size 1.9 GiB
Locale English (United States)
Default charset US-ASCII
Username root
这是我的项目基于的示例项目:
https://github.com/geb/geb-example-gradle
如果您能提供帮助,非常感谢您抽出宝贵的时间。
解决方案
推荐阅读
- java - 在 java 中播放 .WAV 文件时出现问题
- php - 这个怎么不解密?
- ruby-on-rails - 在 Rails 中使用 JSONAPI::Resource 从 JSON 响应正文中解析数组
- java - 使用 JpaRepository 创建类似于 findById 的 findByEmail 方法
- c# - 在 Powershell 中没有得到任何结果,怀疑是异步问题
- javascript - 为什么即使我在访问时包含不必要的反斜杠,访问属性也会起作用?
- postgresql - 计算收盘百分比的窗函数
- python - 一旦在 os.walk() 中找到我想要的文件,我该如何打破这个循环?
- r - 创建一个循环来统计每个数字的实例数,在新的数据框中记录每个实例的唯一数和计数
- python - _add_ vs _radd_:如何评估表达式?