本指南记录了向 Apache Spark 做出各种类型贡献的最佳方式,包括提交代码更改之前所需的条件。

贡献 Spark 不仅仅意味着编写代码。在邮件列表上帮助新用户、测试发布版本以及改进文档也同样受欢迎。事实上,提出重要的代码更改通常需要首先通过其他方式在社区中积累经验和信誉。本指南也旨在帮助您成为一名有效的贡献者。

因此,本指南按照新贡献者如果打算长期参与可能需要考虑的顺序来组织贡献。建立帮助他人的良好记录,而不仅仅是打开拉取请求。

通过帮助其他用户来贡献

为 Spark 做出贡献的一个好方法是在 user@spark.apache.org 邮件列表或 StackOverflow 上帮助回答用户问题。总是有许多新的 Spark 用户;花几分钟时间帮助回答一个问题是一项非常有价值的社区服务。

贡献者应订阅此邮件列表并关注它,以便了解 Spark 的最新动态。回答问题是帮助社区的一种极佳且显眼的方式,也能展示您的专业知识。

有关如何在邮件列表以及 StackOverflow 等论坛上有效参与讨论的指南,请参阅邮件列表指南

通过测试发布版本来贡献

Spark 的发布流程以社区为导向,社区成员可以在 dev@spark.apache.org 邮件列表上投票决定新版本。Spark 用户受邀订阅此列表以接收公告,并在新版本上测试他们的工作负载,以及提供在新版本中发现的任何性能或正确性问题的反馈。

通过审查更改来贡献

Spark 源代码的更改通过GitHub 拉取请求(稍后描述)提出、审查和提交。任何人都可以查看并评论此处的活动更改。审查他人的更改是了解更改流程如何工作以及接触代码各部分活动的好方法。您可以通过审查更改并提出问题或指出问题来提供帮助——无论是错别字还是小的风格问题。另请参阅 https://spark-prs.appspot.com/,以方便查看和筛选开放的 PR。

贡献文档更改

要提出对发布文档(即出现在 https://spark.apache.org/docs/ 下的文档)的更改,请编辑 Spark docs/ 目录中的 Markdown 源文件,其 README 文件展示了如何在本地构建文档以测试您的更改。提出文档更改的过程与下面提出代码更改的过程相同。

要提出对其他文档(即出现在 https://spark.apache.org/docs/ 下的文档)的更改,同样,请编辑 spark-website 仓库中的 Markdown 并打开一个拉取请求。

向 Spark 贡献用户库

就像 Java 和 Scala 应用程序可以访问大量库和实用程序,但这些都不属于 Java 或 Scala 本身一样,Spark 旨在支持丰富的库生态系统。许多新的有用实用程序或功能应该存在于 Spark 之外,而不是在核心中。例如:语言支持可能必须是 Spark 核心的一部分,但有用的机器学习算法可以很好地存在于 MLlib 之外。

为此,大型和独立的全新功能通常会被拒绝包含在 Spark 本身中,但可以且应该作为单独的项目和仓库托管,并包含在 spark-packages.org 集合中。

贡献错误报告

理想情况下,错误报告应附带修复该错误的拟议代码更改。但这并非总是可行,因为发现错误的人可能没有修复错误的经验。可以通过创建 JIRA 报告错误,但不创建拉取请求(见下文)。

然而,错误报告只有包含足够的信息以理解、隔离并理想情况下重现错误时才有用。仅仅遇到错误并不意味着应该报告错误;如下所述,请先搜索 JIRA,并在 Spark 用户/开发者邮件列表上搜索和询问。无法重现的错误或简单的错误报告可能会被关闭。

如果错误报告包含有关错误是如何引入的、由哪个提交引入的描述,那将非常有帮助,这样审查者可以轻松理解该错误。它还有助于提交者决定合并拉取请求时,错误修复应该回溯到多远的版本。修复错误的拉取请求应将问题缩小到根本原因。

性能退化也是一种错误。修复性能退化的拉取请求必须提供基准测试以证明问题确实已修复。

请注意,数据正确性/数据丢失错误非常严重。请确保相应的错误报告 JIRA 票据被标记为 correctnessdata-loss。如果错误报告没有得到足够的关注,请发送电子邮件至 dev@spark.apache.org,以引起更多关注。

也可以提出新功能。除非附有详细信息(例如设计文档和/或代码更改),否则这些通常没有帮助。大型的新贡献应首先考虑 spark-packages.org(见上文),或者首先在邮件列表上进行讨论。功能请求可能会被拒绝,或者在长时间不活动后关闭。

贡献 JIRA 维护

鉴于 Apache Spark JIRA 中提出的问题数量庞大,不可避免地,有些问题是重复的,或者变得过时并最终通过其他方式修复,或者无法重现,或者可以从更多细节中受益,等等。帮助识别这些问题并解决它们很有用,可以通过推进讨论甚至解决 JIRA。大多数贡献者都能够直接解决 JIRA。在确定您是否非常确信问题应该解决时,请运用判断力,尽管更改可以很容易地撤销。如果有疑问,只需在 JIRA 上留下评论即可。

解决 JIRA 时,请遵循一些有用的约定

  • 如果存在可以指向的已解决问题的更改,则将其解决状态设置为已修复 (Fixed)
    • 仅当解决状态为“已修复”时,才设置“修复版本 (Fix Version(s))”。
    • 将“经办人 (Assignee)”设置为对解决问题贡献最大的人,通常是打开解决问题 PR 的人。
    • 如果有多人贡献,优先分配给更“初级”的非提交者贡献者
  • 对于无法在 master 上复现的问题,将其解决状态设置为无法复现 (Cannot Reproduce)
    • 如果清楚是哪个以前的拉取请求解决了它,那么“已修复”也是合理的。链接到它。
  • 如果问题与另一个问题相同或为其子集,则将其解决状态设置为重复 (Duplicate)
    • 确保链接到它所重复的 JIRA
    • 优先将活动或讨论较少的问题解决为重复问题
  • 如果问题看起来明显过时,并且适用于自其创建以来已发生根本性变化的问题或组件,则将其解决状态设置为不是问题 (Not a Problem)
  • 如果问题没有意义——例如,不可操作,非 Spark 问题,则将其解决状态设置为无效 (Invalid)
  • 如果它是一个连贯的问题,但有明确迹象表明没有支持或兴趣对其采取行动,则将其解决状态设置为不修复 (Won’t Fix)
  • 伞形任务如果仅仅是容器任务,不对应于自身的可操作更改,则通常被标记为完成 (Done)

准备贡献代码更改

选择贡献什么

Spark 是一个异常繁忙的项目,平均每隔几小时就会有一个新的 JIRA 或拉取请求。审查可能需要提交者数小时或数天的时间。如果贡献者专注于有用、清晰、易于评估且已通过基本检查的更改,那么每个人都会受益。

有时,贡献者会已经有特定的新更改或错误。如果正在寻找想法,请查阅 JIRA 中入门任务列表,或询问 user@spark.apache.org 邮件列表。

在继续之前,贡献者应评估拟议的更改是否可能相关、新颖且可操作

  • 是否明确必须更改代码?仅当已发现明确问题或更改时,才适合提出 JIRA 和拉取请求。如果只是在使用 Spark 时遇到问题,请首先使用邮件列表,而不是考虑提交 JIRA 或提出更改。如有疑问,请首先发送电子邮件至 user@spark.apache.org 询问可能的更改
  • user@spark.apache.orgdev@spark.apache.org 邮件列表存档中搜索相关讨论。通常,问题以前已经讨论过,并且解决方案不需要代码更改,或者记录了哪些类型的更改将不被接受为解决方案。
  • 在 JIRA 中搜索现有问题:https://issues.apache.org/jira/browse/SPARK
  • 在右上角的搜索框中输入 spark [搜索词]。如果已经存在逻辑上类似的问题,则首先对现有 JIRA 和拉取请求的讨论做出贡献,而不是创建新的。
  • 更改的范围是否与贡献者的经验水平相符?任何人都有资格建议修复错别字,但重构核心调度逻辑需要对 Spark 有更多的了解。一些更改需要首先积累经验(见上文)。

值得再次强调的是,对 Spark 核心或对 SQL 和 Catalyst 等高度复杂且重要的模块进行更改,更难以正确完成。它们将受到更严格的审查,并按照比对不那么关键的代码更高的审查标准进行评估。

MLlib 特定贡献指南

虽然丰富的算法集是 MLlib 的重要目标,但扩展项目需要将可维护性、一致性和代码质量放在首位。新算法应

  • 广为人知
  • 已被使用和接受(学术引用和具体用例有助于证明这一点)
  • 具有高度可扩展性
  • 有完善的文档
  • 具有与 MLlib 中其他实现相同功能的算法一致的 API
  • 对开发者支持有合理的期望。
  • 公共类、方法和变量上带有 @Since 注解。

错误消息指南

Spark 中抛出的异常应与标准化且可操作的错误消息相关联。

错误消息应回答以下问题

  • 问题是什么?
  • 问题为什么发生?
  • 如何解决问题?

编写错误消息时,您应该

  • 使用主动语态
  • 避免基于时间的声明,例如未来支持的承诺
  • 使用现在时态描述错误并提供建议
  • 如果解决方案不明确,请提供具体示例
  • 避免听起来指责、评判或侮辱
  • 直截了当
  • 在面向用户的错误中不要使用编程术语

有关更多详细信息,请参阅错误消息指南

行为变更

行为变更是指通过公共 API 在新版本中用户可见的功能变更。“用户”在这里不仅指编写查询和/或开发 Spark 插件的人,还指部署和/或管理 Spark 集群的人。新功能和错误修复,例如纠正查询结果或模式以及使以前返回不正确结果的不受支持查询失败,都被视为行为变更。但是,性能改进、代码重构以及对未发布 API/功能的更改则不是。

每个人都会犯错误,包括 Spark 开发者。我们将继续修复 Spark 中出现的缺陷。然而,重要的是要传达这些行为变更,以便 Spark 用户为版本升级做好准备。如果一个 PR 引入了行为变更,应在 PR 描述中明确提及。如果行为变更可能需要额外的用户操作,应在迁移指南中突出显示(SQL 组件为 docs/sql-migration-guide.md,其他组件为类似文件)。在可能的情况下,提供选项以恢复之前的行为,并在错误消息中提及这些选项。一些示例包括

  • 更改查询结果的错误修复。用户可能需要回填以纠正现有数据,并且必须被告知这些正确性修复。
  • 更改查询模式的错误修复。用户可能需要更新其数据管道中表的模式,并且必须被告知这些更改。
  • 删除或重命名 Spark 配置。
  • 重命名错误类或条件。
  • 对公共 Python/SQL/Scala/Java/R API(包括开发者 API)的任何非附加性更改,例如重命名函数、删除参数、添加参数、重命名参数或更改参数默认值。通常应避免这些更改,如果必要,则应通过弃用旧函数并引入新函数的方式以二进制兼容的方式进行。
  • 对 Spark 部署和管理方式的任何非附加性更改:部署脚本中参数名称的重命名、REST API 的更新、加载配置文件方法的更改等。

此列表并非旨在详尽。任何审查 PR 的人都可以要求 PR 作者将其添加到迁移指南中,如果他们认为该更改有风险并且可能在升级期间扰乱用户。

代码审查标准

在考虑如何贡献代码之前,了解代码是如何被审查以及为什么更改可能被拒绝是很有用的。请参阅 Google 工程实践文档中代码审查员详细指南。简而言之,具有许多或巨大优点、且负面影响或风险较小的更改,更有可能被合并,并且会很快合并。有风险且价值较低的更改极不可能被合并,并且可能被直接拒绝,而不是进行多次审查。

积极因素

  • 修复现有功能中错误的根本原因
  • 添加了大量用户需要的功能或解决了问题
  • 简单、有针对性
  • 维护或改进了 Python、Java、Scala 之间的一致性
  • 易于测试;有测试
  • 降低了复杂性和代码行数
  • 更改已讨论过并为提交者所知

消极因素、风险

  • 仅修补了错误的症状
  • 引入了复杂的新功能,特别是需要支持的 API
  • 增加了只对小众用例有帮助的复杂性
  • 添加了用户空间功能,无需在 Spark 中维护,但可以在外部托管并由 spark-packages.org 索引
  • 更改了公共 API 或语义(极少允许)
  • 添加了大型依赖项
  • 更改了现有依赖项的版本
  • 添加了大量代码
  • 在一次“大爆炸”式更改中进行了大量修改

贡献代码更改

在提出代码更改之前,请先阅读前一节。本节将介绍如何操作。

当您贡献代码时,您确认该贡献是您的原创作品,并且您根据项目的开源许可将作品许可给项目。无论您是否明确声明,通过拉取请求、电子邮件或其他方式提交任何受版权保护的材料,即表示您同意根据项目的开源许可许可该材料,并保证您拥有这样做的合法权限。

克隆 Apache Spark 源代码

如果您有兴趣使用最新的开发中代码或为 Apache Spark 开发做出贡献,您可以从 Git 中检出 master 分支

# Master development branch
git clone git://github.com/apache/spark.git

下载 Spark 后,您可以在文档页面上找到安装和构建它的说明。

JIRA

通常,Spark 使用 JIRA 来跟踪逻辑问题,包括错误和改进,并使用 GitHub 拉取请求来管理特定代码更改的审查和合并。也就是说,JIRA 用于描述应该修复或更改什么以及高级方法,而拉取请求描述如何在项目源代码中实现该更改。例如,重要的设计决策在 JIRA 中讨论。

  1. 找到与更改相关的现有 Spark JIRA。
    1. 如果创建更改是为了解决 JIRA 中现有问题,请不要创建新的 JIRA;而是添加到现有讨论和工作中
    2. 查找从 JIRA 链接的现有拉取请求,以了解是否有人已在处理该 JIRA
  2. 如果更改是新的,那么通常需要一个新的 JIRA。但是,对于琐碎的更改,如果“应该更改什么”实际上与“如何更改”相同,则不需要 JIRA。示例:修复 Foo scaladoc 中的错别字
  3. 如果需要,创建新的 JIRA
    1. 提供描述性标题。“更新 Web UI”或“调度器中的问题”是不够的。“Kafka Streaming 支持在 YARN 集群模式下无法处理空队列”是好的。
    2. 撰写详细的描述。对于错误报告,理想情况下应包含问题的简短复现。对于新功能,可能包含设计文档。
    3. 设置必填字段
      1. 问题类型 (Issue Type)。通常,Bug、Improvement 和 New Feature 是 Spark 中唯一使用的类型。
      2. 优先级 (Priority)。设置为 Major 或以下;更高的优先级通常保留给提交者设置。主要例外是正确性或数据丢失问题,这些问题可以标记为 Blocker。JIRA 不幸地在其优先级字段值中混淆了“大小”和“重要性”。它们的含义大致是
        1. Blocker:没有此更改发布毫无意义,因为发布对大部分用户将不可用。正确性和数据丢失问题应被视为其目标版本的 Blockers。
        2. Critical:大量用户缺少重要功能,并且/或解决方法很困难
        3. Major:一小部分用户缺少重要功能,并且有解决方法
        4. Minor:小众用例缺少一些支持,但不影响使用或容易解决
        5. Trivial:一个锦上添花的更改,但在实践中不太可能成为问题
      3. 组件 (Component)
      4. 受影响版本 (Affects Version)。对于错误,至少分配一个已知存在问题或需要更改的版本
      5. 标签 (Label)。不广泛使用,以下情况除外
        • correctness: 正确性问题
        • data-loss: 数据丢失问题
        • release-notes: 更改的效果需要在发布说明中提及。JIRA 或拉取请求应包含适合发布说明的详细信息——请参阅下面的“文档文本”。
        • starter: 适合新贡献者的简单小更改
      6. 文档文本 (Docs Text):对于需要在发布说明中列出的问题,此字段应包含发布经理应包含在发布说明中的信息。这应包括受影响行为的简短摘要以及行为更改的详细信息。可以在打开 JIRA 时临时填写,但可能需要在问题解决时更新最终详细信息。
    4. 不要设置以下字段
      1. 修复版本 (Fix Version)。这仅由提交者在解决时分配。
      2. 目标版本 (Target Version)。这由提交者分配,以指示 PR 已被接受,可能在目标版本中修复。
    5. 不要包含补丁文件;拉取请求用于提议实际的更改。
  4. 如果更改是大型更改,请考虑在实施更改之前,先在 dev@spark.apache.org 讨论该问题。

拉取请求

在 Apache Spark 中创建拉取请求之前,检查您的分支上的测试是否可以通过非常重要,因为我们的 GitHub Actions 工作流会自动为您的拉取请求/后续提交运行测试,并且每次运行都会增加 Apache Spark 仓库中 GitHub Actions 有限资源的负担。以下步骤将引导您完成该过程。

  1. 如果尚未fork GitHub 仓库 https://github.com/apache/spark
  2. 进入您 fork 的仓库的“Actions”选项卡,启用“Build and test”和“Report test results”工作流
  3. 克隆您的 fork 并创建一个新分支
  4. 考虑是否需要添加或更新文档或测试作为更改的一部分,并根据需要添加它们。
    1. 添加测试时,请确保测试具有自描述性。
    2. 此外,当您的拉取请求旨在修复特定问题时,您应该考虑在测试中写入 JIRA ID。实际上,这通常在 JIRA 类型为 bug 或 PR 向现有测试类添加少量测试时添加。请参阅以下示例
      • Scala
        test("SPARK-12345: a short description of the test") {
          ...
        
      • Java
        @Test
        public void testCase() {
          // SPARK-12345: a short description of the test
          ...
        
      • Python
        def test_case(self):
            # SPARK-12345: a short description of the test
            ...
        
      • R
        test_that("SPARK-12345: a short description of the test", {
          ...
        
  5. 考虑是否需要添加或更新基准测试结果作为更改的一部分,并根据需要通过在您fork的仓库中运行基准测试来生成基准测试结果。
  6. 运行所有测试(使用 ./dev/run-tests)以验证代码是否仍然编译、通过测试并符合风格检查。如果风格检查失败,请查阅下面的代码风格指南。
  7. 将提交推送到您的分支。这将触发您 fork 的仓库上的“Build and test”和“Report test results”工作流,并开始测试和验证您的更改。
  8. apache/sparkmaster 分支打开拉取请求。(仅在特殊情况下 PR 才会针对其他分支打开)。这将触发“On pull request*”工作流(在 Spark 仓库上),该工作流将查找/监视您 fork 的仓库上的成功工作流运行(如果正在运行,它将等待)。
    1. PR 标题应为 [SPARK-xxxx][COMPONENT] Title 的形式,其中 SPARK-xxxx 是相关的 JIRA 编号,COMPONENT spark-prs.appspot.com 中所示的 PR 类别之一,Title 可以是 JIRA 的标题或更具体地描述 PR 本身的标题。
    2. 如果拉取请求仍在进行中,尚未准备好合并,但需要推送到 GitHub 以方便审查,则在组件后添加 [WIP]
    3. 考虑找出处理过所更改代码的提交者或其他贡献者。在 GitHub 中找到文件并点击“Blame”以查看谁最后更改了代码的逐行注释。您可以在 PR 描述中添加 @username 以立即提醒他们。
    4. 请声明该贡献是您的原创作品,并且您根据项目的开源许可将作品许可给项目。
  9. 相关 JIRA(如果有)将被标记为“进行中”,您的拉取请求将自动链接到它。不需要成为 JIRA 的“经办人”才能处理它,但欢迎您评论您已开始工作。
  10. 如果您的拉取请求中包含与 SparkR 相关的更改,AppVeyor 将自动触发以在 Windows 上测试 SparkR,这大约需要一个小时。与上述步骤类似,修复失败并推送新的提交,这将请求在 AppVeyor 中重新测试。

审查过程

  • 其他审阅者(包括提交者)可能会对更改发表评论并建议修改。通过简单地向同一分支推送更多提交即可添加更改。
  • 社区中的每个人都鼓励进行活跃、礼貌、快速的技术辩论。结果可能是拒绝整个更改。
  • 请记住,对 Spark 更关键部分(如其核心和 SQL 组件)的更改将受到更多审查,并且可能需要比其他更改更多的测试和正确性证明。
  • 审阅者可以通过诸如“我认为这个补丁看起来不错”之类的评论来表明更改适合合并。Spark 使用 LGTM 约定来表示对补丁的最高技术认可级别:只需评论单词“LGTM”即可。它特别意味着:“我已彻底审查了此内容,并像我自己编写补丁一样承担所有权”。如果您评论 LGTM,您将被期望帮助解决补丁上的错误或后续问题。持续、明智地使用 LGTM 是在更广泛的社区中获得审阅者信誉的好方法。
  • 有时,其他已合并的更改会与您的拉取请求的更改发生冲突。PR 无法合并,直到冲突解决。这可以通过例如,通过 git remote add upstream https://github.com/apache/spark.git 添加远程以跟上上游更改,运行 git fetch upstream,然后运行 git rebase upstream/master 并手动解决冲突,然后将结果推送到您的分支来解决。
  • 尽量及时响应讨论,而不是让回复之间间隔数天

关闭您的拉取请求/JIRA

  • 如果更改被接受,它将被合并,拉取请求将自动关闭,相关 JIRA(如果有)也将关闭
    • 请注意,在极少数情况下,如果您被要求针对 master 以外的分支打开拉取请求,您实际上必须手动关闭拉取请求
    • JIRA 将分配给更改的主要贡献者,以示感谢。如果 JIRA 未及时关闭和/或分配,请在 JIRA 上评论。
  • 如果您的拉取请求最终被拒绝,请及时关闭它
    • ……因为提交者无法直接关闭 PR
    • 如果提交者发表了“介意关闭这个 PR 吗?”之类的评论,拉取请求将由 Apache 的自动化流程在大约一周后自动关闭。这意味着提交者明确要求关闭它。
  • 如果拉取请求很少或没有受到关注,请考虑改进描述或更改本身,并在几天后再次提醒可能的审阅者。考虑提出更容易包含的更改,例如更小和/或侵入性更小的更改。
  • 如果经过审查但几周后仍未被采纳,在征求最相关审阅者的意见后,或者,遇到了中立的反应,结果可以被视为“软拒绝”。在这种情况下,撤回并关闭 PR 会很有帮助。
  • 如果拉取请求因被认为不是解决 JIRA 的正确方法而被关闭,则将 JIRA 保持开放。但是,如果审查明确表明 JIRA 中识别出的问题不会通过任何拉取请求解决(不是问题,不修复),则也解决 JIRA。

代码风格指南

请遵循现有代码库的风格。

  • 对于 Python 代码,Apache Spark 遵循 PEP 8,但有一个例外:行长可达 100 个字符,而非 79 个。
  • 对于 R 代码,Apache Spark 遵循Google 的 R 风格指南,但有三个例外:行长可达 100 个字符,而非 80 个;函数名没有长度限制,但首字母小写;允许使用 S4 对象/方法。
  • 对于 Java 代码,Apache Spark 遵循Oracle 的 Java 代码约定以及下面的 Scala 指南。后者更受偏好。
  • 对于 Scala 代码,Apache Spark 遵循官方的Scala 风格指南Databricks Scala 指南。后者更受偏好。要格式化 Scala 代码,请在提交 PR 之前运行 ./dev/scalafmt。

如有疑问

如果您不确定某种风格是否正确,请尝试遵循现有代码库的风格。查看代码中是否有其他使用您功能的示例。也欢迎在 dev@spark.apache.org 邮件列表上提问和/或咨询提交者。

行为准则

Apache Spark 项目遵循Apache 软件基金会行为准则行为准则适用于 Apache 软件基金会管理的所有空间,包括 IRC、所有公共和私人邮件列表、问题追踪器、维基、博客、Twitter 以及我们社区使用的任何其他通信渠道。针对面对面活动(即会议)的行为准则已在发布的 ASF 反骚扰政策中明确规定。

我们期望所有正式或非正式参与 Apache 社区、或声称与基金会有任何关联的人,在所有与基金会相关的活动中,特别是在以任何身份代表 ASF 时,都应遵守本行为准则。

本准则并非详尽或完整。它旨在提炼我们对协作、共享环境和目标的共同理解。我们期望其精神和文字同样得到遵循,以便它能够丰富我们所有人以及我们所参与的技术社区。

欲了解更多信息和具体指南,请参阅Apache 软件基金会行为准则