测试覆盖率

Apache Spark 社区使用各种资源来维护社区测试覆盖率。

GitHub Actions

GitHub Actions 在 Ubuntu 22.04 上提供以下功能。

Apache Spark 4

  • 使用 Java 17 的 Scala 2.13 SBT 构建
  • 使用 Java 17/21 的 Scala 2.13 Maven 构建
  • 使用 Java 17/Scala 2.13/SBT 的 Java/Scala/Python/R 单元测试
  • TPC-DS 基准测试,比例因子为 1
  • JDBC Docker 集成测试
  • Kubernetes 集成测试
  • 使用 Java 21 和 Scala 2.13/SBT 的每日 Java/Scala/Python/R 单元测试

Apache Spark 3

  • 使用 Java 8 的 Scala 2.12 SBT 构建
  • 使用 Java 11/17 的 Scala 2.12 Maven 构建
  • 使用 Java 8/Scala 2.12/SBT 的 Java/Scala/Python/R 单元测试
  • Kubernetes 集成测试
  • 使用 Java 8 和 Scala 2.13/SBT 的每日 Java/Scala/Python/R 单元测试
  • 使用 Java 8 和 Scala 2.13/SBT 的每日 JDBC Docker 集成测试
  • 使用 Java 8 和 Scala 2.12/SBT 的每日 TPC-DS 基准测试,比例因子为 1

AppVeyor

AppVeyor 在 Windows 上提供以下功能。

  • 使用 Java 17/Scala 2.13/SBT 的 R 单元测试

Scaleway

Scaleway 在 MacOS 和 Apple Silicon 上提供以下功能。

有用的开发者工具

减少构建时间

SBT:避免重新创建汇编 JAR

Spark 的默认构建策略是汇编一个包含所有依赖项的 JAR 文件。这在进行迭代开发时可能很麻烦。在本地开发时,可以创建一个包含所有 Spark 依赖项的汇编 JAR 文件,然后在进行更改时仅重新打包 Spark 本身。

$ build/sbt clean package
$ ./bin/spark-shell
$ export SPARK_PREPEND_CLASSES=true
$ ./bin/spark-shell # Now it's using compiled classes
# ... do some local development ... #
$ build/sbt compile
$ unset SPARK_PREPEND_CLASSES
$ ./bin/spark-shell
 
# You can also use ~ to let sbt do incremental builds on file changes without running a new sbt session every time
$ build/sbt ~compile

单独构建子模块

例如,可以使用以下命令构建 Spark Core 模块

$ # sbt
$ build/sbt
> project core
> package

$ # or you can build the spark-core module with sbt directly using:
$ build/sbt core/package

$ # Maven
$ build/mvn package -DskipTests -pl core

运行单个测试

在本地开发时,运行单个测试或几个测试通常比运行整个测试套件更方便。

使用 SBT 进行测试

运行单个测试的最快方法是使用 sbt 控制台。保持 sbt 控制台打开并使用它根据需要重新运行测试是最快的。例如,要运行特定项目(例如 core)中的所有测试,可以使用以下命令

$ build/sbt
> project core
> test

可以使用 testOnly 命令运行单个测试套件。例如,要运行 DAGSchedulerSuite,可以使用以下命令

> testOnly org.apache.spark.scheduler.DAGSchedulerSuite

testOnly 命令接受通配符;例如,也可以使用以下命令运行 DAGSchedulerSuite

> testOnly *DAGSchedulerSuite

或者可以运行调度程序包中的所有测试

> testOnly org.apache.spark.scheduler.*

如果要运行 DAGSchedulerSuite 中的单个测试(例如,名称中包含“SPARK-12345”的测试),可以在 sbt 控制台中运行以下命令

> testOnly *DAGSchedulerSuite -- -z "SPARK-12345"

如果愿意,可以在命令行中运行所有这些命令(但这比使用打开的控制台运行测试要慢)。为此,需要将 testOnly 和以下参数用引号括起来

$ build/sbt "core/testOnly *DAGSchedulerSuite -- -z SPARK-12345"

有关如何使用 sbt 运行单个测试的更多信息,请参阅 sbt 文档

使用 Maven 进行测试

使用 Maven,可以使用 -DwildcardSuites 标志运行单个 Scala 测试

build/mvn -Dtest=none -DwildcardSuites=org.apache.spark.scheduler.DAGSchedulerSuite test

需要使用 -Dtest=none 来避免运行 Java 测试。有关 ScalaTest Maven 插件的更多信息,请参阅 ScalaTest 文档

要运行单个 Java 测试,可以使用 -Dtest 标志

build/mvn test -DwildcardSuites=none -Dtest=org.apache.spark.streaming.JavaAPISuite test

测试 PySpark

要运行单个 PySpark 测试,可以使用 python 目录下的 run-tests 脚本。测试用例位于每个 PySpark 包下的 tests 包中。请注意,如果在 Apache Spark 的 Scala 或 Python 端添加了一些更改,则需要在运行 PySpark 测试之前手动重新构建 Apache Spark,以便应用这些更改。运行 PySpark 测试脚本不会自动构建它。

另外,请注意,在 macOS High Serria+ 上使用 PySpark 时存在一个正在进行的问题。需要将 OBJC_DISABLE_INITIALIZE_FORK_SAFETY 设置为 YES 才能运行某些测试。有关更多详细信息,请参阅 PySpark 问题Python 问题

要运行特定模块中的测试用例,可以使用以下命令

$ python/run-tests --testnames pyspark.sql.tests.test_arrow

要运行特定类中的测试用例,可以使用以下命令

$ python/run-tests --testnames 'pyspark.sql.tests.test_arrow ArrowTests'

要运行特定类中的单个测试用例,可以使用以下命令

$ python/run-tests --testnames 'pyspark.sql.tests.test_arrow ArrowTests.test_null_conversion'

还可以运行特定模块中的 doctest

$ python/run-tests --testnames pyspark.sql.dataframe

最后,在同一位置还有一个名为 run-tests-with-coverage 的脚本,它会为 PySpark 测试生成覆盖率报告。它接受与 run-tests 相同的参数。

$ python/run-tests-with-coverage --testnames pyspark.sql.tests.test_arrow --python-executables=python
...
Name                              Stmts   Miss Branch BrPart  Cover
-------------------------------------------------------------------
pyspark/__init__.py                  42      4      8      2    84%
pyspark/_globals.py                  16      3      4      2    75%
...
Generating HTML files for PySpark coverage under /.../spark/python/test_coverage/htmlcov

可以通过 /.../spark/python/test_coverage/htmlcov 下的 HTML 文件以可视方式查看覆盖率报告。

请通过 python/run-tests[-with-coverage] --help 检查其他可用选项。

测试 K8S

虽然 GitHub Actions 提供了 K8s 单元测试和集成测试覆盖率,但可以在本地运行它。例如,Volcano 批处理调度程序集成测试需要手动完成。有关详细信息,请参阅集成测试文档。

https://github.com/apache/spark/blob/master/resource-managers/kubernetes/integration-tests/README.md

运行 Docker 集成测试

Docker 集成测试由 GitHub Actions 涵盖。但是,可以在本地运行它以加快开发和测试速度。有关详细信息,请参阅 Docker 集成测试文档

使用 GitHub Actions 工作流程进行测试

Apache Spark 利用 GitHub Actions,它支持持续集成和广泛的自动化。Apache Spark 存储库为开发者提供了一些 GitHub Actions 工作流程,以便在创建拉取请求之前运行它们。

在分叉的存储库中运行基准测试

Apache Spark 存储库提供了一种在 GitHub Actions 中运行基准测试的简便方法。在拉取请求中更新基准测试结果时,建议使用 GitHub Actions 运行并生成基准测试结果,以便在尽可能相同的环境中运行它们。

  • 单击分叉存储库中的“Actions”选项卡。
  • 在“All workflows”列表中选择“Run benchmarks”工作流程。
  • 单击“Run workflow”按钮,并按如下所示适当地填写字段
    • Benchmark class:要运行的基准测试类。它允许使用通配符模式。例如,org.apache.spark.sql.*
    • JDK version:要使用其运行基准测试的 Java 版本。例如,11
    • Failfast:指示基准测试失败时是否要停止基准测试和工作流程。如果为 true,则会立即失败。如果为 false,则无论成功与否都会运行所有测试。
    • Number of job splits:它将基准测试作业分成指定数量的作业,并并行运行它们。这对于解决 GitHub Actions 中工作流程和作业的时间限制特别有用。
  • 完成“Run benchmarks”工作流程后,单击工作流程并在“Artifacts”中下载基准测试结果。
  • 转到 Apache Spark 存储库的根目录,解压缩/解压缩下载的文件,这些文件将更新基准测试结果,并将文件适当地定位到要更新的位置。

ScalaTest 问题

如果在运行 ScalaTest 时出现以下错误

An internal error occurred during: "Launching XYZSuite.scala".
java.lang.NullPointerException

这是由于类路径中的 Scala 库不正确造成的。要解决此问题,请执行以下操作

  • 右键单击项目
  • 选择 Build Path | Configure Build Path
  • 添加库 | Scala 库
  • 删除 scala-library-2.10.4.jar - lib_managed\jars

如果出现“Could not find resource path for Web UI: org/apache/spark/ui/static”,则这是由于类路径问题造成的(一些类可能未编译)。要解决此问题,只需从命令行运行测试即可

build/sbt "testOnly org.apache.spark.rdd.SortingSuite"

二进制兼容性

为了确保二进制兼容性,Spark 使用 MiMa

确保二进制兼容性

在处理问题时,最好在打开拉取请求之前检查更改是否不会引入二进制不兼容性。

可以通过运行以下命令来做到这一点

$ dev/mima

MiMa 报告的二进制不兼容性可能如下所示

[error] method this(org.apache.spark.sql.Dataset)Unit in class org.apache.spark.SomeClass does not have a correspondent in current version
[error] filter with: ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.spark.SomeClass.this")

解决二进制不兼容性

如果认为二进制不兼容性是合理的,或者 MiMa 报告了误报(例如,报告的二进制不兼容性与非用户界面 API 有关),则可以通过在 project/MimaExcludes.scala 中添加一个排除项来过滤掉它们,该排除项包含 MiMa 报告中建议的内容以及一个包含正在处理的问题的 JIRA 编号及其标题的注释。

对于上述问题,可以添加以下内容

// [SPARK-zz][CORE] Fix an issue
ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.spark.SomeClass.this")

否则,必须在打开或更新拉取请求之前解决这些不兼容性。通常,MiMa 报告的问题是不言自明的,并且与缺少的成员(方法或字段)有关,必须添加这些成员才能保持二进制兼容性。

签出拉取请求

Git 提供了一种机制,可以将远程拉取请求提取到自己的本地存储库中。这在审查代码或在本地测试补丁时很有用。如果尚未克隆 Spark Git 存储库,请使用以下命令

$ git clone https://github.com/apache/spark.git
$ cd spark

要启用此功能,需要配置 git 远程存储库以提取拉取请求数据。为此,请修改 Spark 目录内的 .git/config 文件。如果已将远程存储库命名为其他名称,则它可能不是“origin”。

[remote "origin"]
  url = [email protected]:apache/spark.git
  fetch = +refs/heads/*:refs/remotes/origin/*
  fetch = +refs/pull/*/head:refs/remotes/origin/pr/*   # Add this line

完成此操作后,可以提取远程拉取请求

# Fetch remote pull requests
$ git fetch origin
# Checkout a remote pull request
$ git checkout origin/pr/112
# Create a local branch from a remote pull request
$ git checkout origin/pr/112 -b new-branch

生成依赖关系图

$ # sbt
$ build/sbt dependencyTree
 
$ # Maven
$ build/mvn -DskipTests install
$ build/mvn dependency:tree

组织导入

可以使用 Aaron Davidson 的 IntelliJ Imports Organizer 来帮助组织代码中的导入。可以将其配置为与样式指南中的导入顺序匹配。

格式化代码

要格式化 Scala 代码,请在提交 PR 之前运行以下命令

$ ./dev/scalafmt

默认情况下,此脚本将格式化与 git master 不同的文件。有关更多信息,请参阅 scalafmt 文档,但请使用现有的脚本,而不是本地安装的 scalafmt 版本。

IDE 设置

IntelliJ

虽然许多 Spark 开发人员在命令行中使用 SBT 或 Maven,但我们最常用的 IDE 是 IntelliJ IDEA。可以免费获得社区版(Apache 提交者可以获得免费的 IntelliJ Ultimate Edition 许可证),并从 Preferences > Plugins 安装 JetBrains Scala 插件。

要为 IntelliJ 创建 Spark 项目,请执行以下操作

  • 下载 IntelliJ 并安装 IntelliJ 的 Scala 插件
  • 转到 文件 -> 导入项目,找到 Spark 源代码目录,并选择“Maven 项目”。
  • 在导入向导中,可以将设置保留为默认值。但是,通常启用“自动导入 Maven 项目”很有用,因为项目结构的更改将自动更新 IntelliJ 项目。
  • 构建 Spark 文档中所述,某些构建配置需要启用特定配置文件。在导入向导的“配置文件”屏幕中,可以启用与上面使用 -P[profile name] 启用的相同配置文件。例如,如果要为支持 YARN 的 Hadoop 2.7 开发,请启用配置文件 yarnhadoop-2.7。这些选择可以通过从“视图”菜单访问“Maven 项目”工具窗口,并展开“配置文件”部分来更改。

其他提示

  • 第一次编译项目时,“重建项目”可能会失败,因为生成的源文件不会自动生成。尝试单击“Maven 项目”工具窗口中的“为所有项目生成源代码并更新文件夹”按钮以手动生成这些源代码。
  • 与 IntelliJ 捆绑在一起的 Maven 版本可能不够新,无法用于 Spark。如果发生这种情况,操作“为所有项目生成源代码并更新文件夹”可能会静默失败。请记住将项目的 Maven 主目录 (首选项 -> 构建、执行、部署 -> Maven -> Maven 主目录) 重置为指向更新的 Maven 安装。您也可以使用脚本 build/mvn 首先构建 Spark。如果脚本无法找到足够新的 Maven 安装,它将下载并将最近版本的 Maven 安装到文件夹 build/apache-maven-<version>/ 中。
  • 某些模块具有基于 Maven 配置文件的可插拔源代码目录(即,支持 Scala 2.11 和 2.10,或允许针对不同版本的 Hive 进行交叉构建)。在某些情况下,IntelliJ 无法正确检测到 maven-build-plugin 的使用来添加源代码目录。在这些情况下,您可能需要显式添加源代码位置才能编译整个项目。如果是这样,请打开“项目设置”并选择“模块”。根据您选择的 Maven 配置文件,您可能需要将源代码文件夹添加到以下模块
    • spark-hive:添加 v0.13.1/src/main/scala
    • spark-streaming-flume-sink:添加 target\scala-2.11\src_managed\main\compiled_avro
    • spark-catalyst:添加 target\scala-2.11\src_managed\main
  • 编译可能会失败,并出现类似“scalac:错误选项:-P:/home/jakub/.m2/repository/org/scalamacros/paradise_2.10.4/2.0.1/paradise_2.10.4-2.0.1.jar”的错误。如果是这样,请转到“首选项”>“构建、执行、部署”>“Scala 编译器”,并清除“其他编译器选项”字段。它将起作用,尽管该选项将在项目重新导入时返回。如果您尝试使用 quasiquotes(例如,sql)构建任何项目,那么您需要将该 jar 作为编译器插件(位于“其他编译器选项”下方)。否则,您将看到类似的错误
    /Users/irashid/github/spark/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/codegen/CodeGenerator.scala
    Error:(147, 9) value q is not a member of StringContext
     Note: implicit class Evaluate2 is not applicable here because it comes after the application point and it lacks an explicit result type
          q"""
          ^ 
    

远程调试 Spark

本部分将向您展示如何使用 IntelliJ 远程调试 Spark。

设置远程调试配置

按照运行 > 编辑配置 > + > 远程 打开默认的远程配置模板:

通常,默认值足以使用。确保您选择侦听远程 JVM 作为调试器模式,并选择正确的 JDK 版本以生成正确的远程 JVM 的命令行参数

完成配置并保存后。您可以按照运行 > 运行 > Your_Remote_Debug_Name > 调试 启动远程调试过程,并等待 SBT 控制台连接

触发远程调试

一般来说,有两个步骤

  1. 使用上一步中生成的远程 JVM 的命令行参数 设置 JVM 选项。
  2. 启动 Spark 执行(SBT 测试、pyspark 测试、spark-shell 等)。

以下是如何使用 SBT 单元测试触发远程调试的示例。

进入 SBT 控制台

./build/sbt

切换到目标测试所在的项目,例如

sbt > project core

复制粘贴远程 JVM 的命令行参数

sbt > set javaOptions in Test += "-agentlib:jdwp=transport=dt_socket,server=n,suspend=n,address=localhost:5005"

使用 IntelliJ 设置断点,并使用 SBT 运行测试,例如

sbt > testOnly *SparkContextSuite -- -t "Only one SparkContext may be active at a time"

当您在 IntelliJ 控制台中看到“已连接到目标 VM,地址:‘localhost:5005’,传输:‘socket’”时,它应该已成功连接到 IntelliJ。然后,您可以像往常一样在 IntelliJ 中开始调试。

要退出远程调试模式(这样您就不必一直启动远程调试器),请在项目中时在 SBT 控制台中键入“session clear”。

Eclipse

Eclipse 可用于开发和测试 Spark。以下配置已知有效

最简单的方法是从 Scala IDE 下载页面下载 Scala IDE 捆绑包。它预装了 ScalaTest。或者,使用 Scala IDE 更新站点或 Eclipse 市场。

SBT 可以创建 Eclipse .project.classpath 文件。要为每个 Spark 子项目创建这些文件,请使用以下命令

sbt/sbt eclipse

要导入特定项目,例如 spark-core,请选择 文件 | 导入 | 工作区中的现有项目。不要选择“将项目复制到工作区”。

如果您想在 Scala 2.10 上进行开发,您需要为用于编译 Spark 的确切 Scala 版本配置 Scala 安装。由于 Scala IDE 捆绑了最新版本(目前为 2.10.5 和 2.11.8),您需要在 Eclipse 首选项 -> Scala -> 安装 中添加一个,方法是指向 Scala 2.10.5 发行版的 lib/ 目录。完成此操作后,选择所有 Spark 项目并右键单击,选择 Scala -> 设置 Scala 安装,并指向 2.10.5 安装。这应该清除所有关于无效交叉编译库的错误。现在,干净的构建应该成功。

ScalaTest 可以通过右键单击源文件并选择 以...方式运行 | Scala Test 来执行单元测试。

如果出现 Java 内存错误,可能需要增加 Eclipse 安装目录中 eclipse.ini 中的设置。根据需要增加以下设置

--launcher.XXMaxPermSize
256M

每日构建

Spark 定期发布其 Maven 工件的 SNAPSHOT 版本,用于主分支和维护分支。要链接到 SNAPSHOT,您需要将 ASF 快照存储库添加到您的构建中。请注意,SNAPSHOT 工件是短暂的,可能会更改或删除。要使用这些,您必须在 https://repository.apache.org/snapshots/ 添加 ASF 快照存储库。

groupId: org.apache.spark
artifactId: spark-core_2.12
version: 3.0.0-SNAPSHOT

使用 YourKit 分析 Spark 应用程序

以下是使用 YourKit Java Profiler 分析 Spark 应用程序的说明。

在 Spark EC2 映像上

  • 登录主节点后,从 YourKit 下载页面 下载适用于 Linux 的 YourKit Java Profiler。此文件非常大(约 100 MB),YourKit 下载站点速度有点慢,因此您可能考虑镜像此文件或将其包含在自定义 AMI 中。
  • 将此文件解压缩到某个位置(在本例中为 /root):unzip YourKit-JavaProfiler-2017.02-b66.zip
  • 使用 copy-dir 将扩展的 YourKit 文件复制到每个节点:~/spark-ec2/copy-dir /root/YourKit-JavaProfiler-2017.02
  • 通过编辑 ~/spark/conf/spark-env.sh 并添加以下行来配置 Spark JVM 以使用 YourKit 分析代理
    SPARK_DAEMON_JAVA_OPTS+=" -agentpath:/root/YourKit-JavaProfiler-2017.02/bin/linux-x86-64/libyjpagent.so=sampling"
    export SPARK_DAEMON_JAVA_OPTS
    SPARK_EXECUTOR_OPTS+=" -agentpath:/root/YourKit-JavaProfiler-2017.02/bin/linux-x86-64/libyjpagent.so=sampling"
    export SPARK_EXECUTOR_OPTS
    
  • 将更新的配置复制到每个节点:~/spark-ec2/copy-dir ~/spark/conf/spark-env.sh
  • 重新启动您的 Spark 集群:~/spark/bin/stop-all.sh~/spark/bin/start-all.sh
  • 默认情况下,YourKit 分析代理使用端口 10001-10010。要将 YourKit 桌面应用程序连接到远程分析代理,您必须在集群的 EC2 安全组中打开这些端口。为此,请登录 AWS 管理控制台。转到 EC2 部分,然后从页面左侧的“网络和安全”部分选择“安全组”。找到与您的集群相对应的安全组;如果您启动了一个名为 test_cluster 的集群,那么您将需要修改 test_cluster-slavestest_cluster-master 安全组的设置。对于每个组,从列表中选择它,单击“入站”选项卡,并创建一个新的“自定义 TCP 规则”,打开端口范围 10001-10010。最后,单击“应用规则更改”。确保对两个安全组都执行此操作。注意:默认情况下,spark-ec2 会重复使用安全组:如果您停止此集群并使用相同名称启动另一个集群,您的安全组设置将被重复使用。
  • 在您的桌面上启动 YourKit 分析器。
  • 从欢迎屏幕中选择“连接到远程应用程序...”,然后输入您的 Spark 主节点或工作节点的地址,例如 ec2--.compute-1.amazonaws.com
  • YourKit 现在应该已连接到远程分析代理。分析信息可能需要几分钟才能显示。

有关分析代理的完整列表,请参阅完整的 YourKit 文档 启动选项

在 Spark 单元测试中

通过 SBT 运行 Spark 测试时,将 javaOptions in Test += "-agentpath:/path/to/yjp" 添加到 SparkBuild.scala 以启动启用了 YourKit 分析代理的测试。
分析代理的平台特定路径列在 YourKit 文档 中。

生成式工具的使用

一般来说,ASF 允许使用生成式 AI 工具共同创作的贡献。但是,在提交包含生成内容的补丁时,有一些注意事项。

最重要的是,您需要披露此类工具的使用情况。此外,您有责任确保所用工具的条款和条件与在开源项目中的使用兼容,并且包含生成内容不会构成版权侵犯的风险。

有关详细信息和发展,请参阅 ASF 生成式工具指南

最新消息

存档