# 贡献于 Kudu

原文链接 : [http://kudu.apache.org/docs/contributing.html](http://kudu.apache.org/docs/contributing.html)

译文链接 : [http://cwiki.apachecn.org/pages/viewpage.action?pageId=10813653](http://cwiki.apachecn.org/pages/viewpage.action?pageId=10813653)

贡献者 : [小瑶](/display/~chenyao) [ApacheCN](/display/~apachecn) [Apache中文网](/display/~apachechina)

## Contributing Patches Using Gerrit ( 使用 Gerrit 贡献补丁 )

**Kudu** 团队使用 **Gerrit** 进行代码审查，而不是 **Github pull requests**。通常，您从 **Github pull** ，但是 **push** 到 **Gerrit** ， **Gerrit** 用于查看代码并将其合并到 **Github** 中。

有关使用 **Gerrit** 进行代码审查的概述，请参阅 [**Gerrit** 教程](https://www.mediawiki.org/wiki/Gerrit/Tutorial)。

### Gerrit 的初始设置

1.  使用您的 **Github** 用户名登录 [**Gerrit**](http://gerrit.cloudera.org:8080/) 。
2.  前往 **[Setting](http://gerrit.cloudera.org:8080/#/settings/) **。在 **Contact Information** 页面上更新您的姓名和电子邮件地址，并上传 **SSH** 公钥。如果您不更新您的姓名，它将在 **Gerrit** 评论中显示为 **“Anonymous Coward”** 。
3.  如果还没有这样做，请 **clone the main Kudu repository** 。默认情况下，**main remote** 称为 **origin** 。当你 **fetch **或 **pull** ，你会从 **origin** 这样做。

    ```
    git clone https://github.com/apache/kudu
    ```

4.  切换到新的 **kudu** 目录。
5.  添加 **gerrit remote** 。在以下命令中，用您的 **Github** 用户名替换 **&lt;username&gt;** 。

    ```
    git remote add gerrit ssh://&lt;username&gt;@gerrit.cloudera.org:29418/kudu
    ```

6.  运行以下命令来安装 **Gerrit commit-msg hook** 。使用以下命令，用您的 **Github** 用户名替换 **&lt;username&gt;** 。

    ```
    gitdir=$(git rev-parse --git-dir); scp -p -P 29418 &lt;username&gt;@gerrit.cloudera.org:hooks/commit-msg ${gitdir}/hooks/
    ```

7.  默认情况下，您已经设置了 **Kudu** 存储库使用 **pull -rebase** 。您可以使用以下两个命令，假设您至今已经检查过主机：

    ```
    git config branch.autosetuprebase always
    git config branch.master.rebase true
    ```

如果由于某种原因，您已经 **checked out branches** 而不是 **master** ，请在上面的第二个命令中替换 **master  **以获取 **other branch names**。

### Submitting Patches ( 提交补丁 )

要提交修补程序，首先提交您的更改（如果可能，使用描述性多行提交消息），然后将请求 **push** 到 **gerrit remote**。例如，要将更改推送到 **master** 分支：

```
git push gerrit HEAD:refs/for/master --no-thin
```

或者将更改 **push** 到 **gh-pages** 分支 ( 更新网页 )：

```
git push gerrit HEAD:refs/for/gh-pages --no-thin
```

注意

在准备一个修补程序进行审查时，最好[遵循通用 **git** 提交准则和良好做法](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines)。

注意

**--no-thin** 参数是防止 **Gerrit** 中的错误的解决方法。请参阅 

注意

考虑为上述命令创建 **Git** 别名。 **Gerrit** 还包括一个名为 **git-review** 的命令行工具，您可能会发现有用。

**Gerrit** 会在您的提交消息中添加更改 **ID** ，并创建一个 **Gerrit review** ，其 **URL** 将作为推送回复的一部分发布。如果需要，您可以向 **kudu-dev** 邮件列表发送消息，解释补丁并请求 **review** 。

获得反馈后，您可以更改或修改您的提交（例如，使用像 **git commit --amend** 这样的命令），同时保留更改 **ID** 。将您的更改再次 **push** 给 **Gerrit** ，这将在 **Gerrit** 中创建一个新的修补程序，并通知所有审阅者有关更改。

当您的代码经过审查并准备合并到 **Kudu** 代码库后， **Kudu** 提交者将使用 **Gerrit** 进行合并。你可以丢弃你的 **local branch** 。

### Abandoning a Review ( 放弃 review )

如果您的补丁不被接受或您决定从考虑中提取补丁，则可以使用 **Gerrit UI** 放弃补丁。它仍将在 **Gerrit** 的历史上展示，但不会被列为待审查。

### Reviewing Patches In Gerrit ( 审查 Gerrit 中的补丁 )

您可以使用 **Web UI** 查看 **Gerrit** 中的统一或并行差异更改。要发表评论，请单击相关行号或突出显示该行的相关部分，然后键入 **“c”** 以显示注释框。要提交您的 **review** 和/或 您的 **review status** ，请转到评论的顶层，然后单击 **Reply**。您可以在此处添加其他顶级注释，然后提交。

要查看 **Gerrit review** 中的代码，请单击下载并将相关的 **Git** 命令粘贴到 **Git** 客户端。然后，您可以更新提交并推送 **Gerrit** 向审阅提交补丁，即使您不是原始审阅者。

**Gerrit** 允许您对 **review** 进行投票。在修补程序可以合并之前，需要至少提交一个提交者（除了提交者）之外的一个 **+2** 的投票。

## Code Style ( 代码风格 )

熟悉这些准则，以便您的贡献能够快速轻松地进行审查和整合。

一般来说，**Kudu** 遵循 **[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)** ，但有以下例外：

### Notes on C++ 11 ( 关于 C++ 11 的注释 )

**Kudu** 使用 **C ++ 11** 。查看 **C ++ 11** 移动语义和 **rvalue** 引用的方便指南：[https://www.chromium.org/rvalue-references](https://www.chromium.org/rvalue-references) 。

我们的目标是遵循大多数相同的指导原则，例如在可能的情况下迁移远离 **foo.Pass()** ，有利于 **std :: move(foo)**。

### Limitations on boost Use ( boost 使用限制 )

在 **kudu** 代码库中不存在合适的替换的情况下，可以使用仅来自标头库的 **boost** 类。然而：

*   不要对标准 **C ++** 库或 **src/kudu/gutil/** 中存在等效功能的 **boost** 类引入依赖关系。例如，喜欢来自 **gutil** 的 **strings :: Split()** ，而不是 **boost :: split** 。
*   喜欢使用 **boost** 的功能而不是重新实现相同的功能，除非使用 **boost** 功能需要过度使用我们的风格指南不允许的 **C ++** 功能。例如，**boost :: spirit** 非常基于模板元编程，不应该使用。
*   不要在 **Kudu C ++** 客户端的任何公用头文件中使用 **boost** ，因为 **boost** 通常会破坏向后兼容性，并且在两个升级版本之间传递数据（一个由用户由 **Kudu** 导出）会导致严重的问题。

如果有任何提升功能引入新的依赖关系，最好发送电子邮件至 dev@kudu.apache.org 开始讨论。

### Line length ( 线长 )

**Kudu** 团队允许每行 **100** 个字符的行长度，而不是 **Google** 的 **80** 标准。尽可能保持在 **80** 以下，但如果需要，您可以溢出到 **100** 个左右。

### Pointers ( 指针 )

**Smart Pointers and Singly-Owned Pointers ( 智能指针和单独指针 )**

通常，大多数对象应该有明确的 **“single-owner”** 语义。大多数时候， **singly-owned** 的对象可以包装在 **unique_ptr &lt;&gt;** 中，确保在范围退出时删除，并防止意外复制。

如果对象是 **singly owned** 的，但是从多个位置引用，例如当已知指向对象至少与指针本身一样长时，将注释与将原始指针存储并存储的构造函数相关联，如在下面的例子中。

```
// 'blah' must remain valid for the lifetime of this class
  MyClass(const Blah* blah) :
    blah_(blah) {
  }
```

注意

**Kudu** 代码库的较旧部分使用 **gscoped_ptr** 而不是 **unique_ptr** 。这些都是在 **Kudu** 采用 **C ++ 11** 之前进行的。新代码不应该使用 **gscoped_ptr** ，除非需要与现有代码进行接口。或者，考虑在您遇到这些问题时更新用法。

注意

严格禁止使用 **std :: auto_ptr** ，因为它的难度大且易出错的语义。此外， **std :: auto_ptr** 自 **C ++ 11** 被声明为不推荐使用。

**Smart Pointers for Multiply-Owned Pointers ( 多指针指针的智能指针 ):**

虽然 **single ownership** 是理想的，但有时候是不可能的，特别是当多个线程正在运行时，指针的生命周期没有明确定义。在这些情况下，您可以使用 **std :: shared_ptr** 或 **Kudu** 自己的 **scoped_refptr** 从 **gutil/ref_counted.hpp** 。这些机制中的每一个依赖于引用计数，以便在没有更多指针保留时自动删除指示。这两种类型的指针之间的关键区别是 **scoped_refptr** 要求对象扩展一个 **RefCounted** 基类，并将其引用计数存储在对象存储本身内，而 **shared_ptr** 在堆上维护单独的引用计数。

利弊是：

**shared_ptr**

*   可以与任何类型的对象一起使用，而不需要从特殊的基类派生对象
*   标准库的一部分，大多数 **C ++** 开发人员熟悉
*   支持 **weak_ptr** 的用例：

    *   当对象仅在存在的情况下需要被访问时才是临时所有权
    *   打破 **shared_ptr** 的循环引用，如果由于聚合存在任何存在
*   您可以将 **shared_ptr** 转换为 **weak_ptr** 并返回
*   如果使用 **std :: make_shared &lt;&gt;()**  创建一个实例，则只能进行一次分配（因为 **C ++ 11; Standard** 中的非绑定的要求）
*   如果使用 **shared_ptr &lt;T&gt; p(new T)** 创建新对象需要两个分配（一个用于创建引用计数，另一个用于创建对象）
*   引用计数可能不在堆上的对象附近，因此在访问时可能会发生额外的高速缓存未命中
*   **shared_ptr** 实例本身需要 **16** 个字节（指向 **ref** 计数的指针和指向对象的指针）

**scoped_refptr**

*   只需要一个分配，并且 **ref** 计数与对象在同一个高速缓存行上

*   指针只需要 **8** 个字节（因为引用计数在对象内）

*   当需要更多控制时，您可以手动增加或减少参考计数

*   您可以将原始指针转换回 **scoped_refptr** ，而不必担心双重释放
*   由于我们控制实现，我们可以实现功能，例如调试构建，捕获每个对象的堆栈跟踪以帮助调试泄漏。
*   引用对象必须从 **RefCounted** 继承
*   不支持 **weak_ptr** 的用例

由于 **scoped_refptr** 通常越来越小，所以尝试在新代码中使用而不是 **shared_ptr** 。现有代码在许多地方使用 **shared_ptr** 。当与该代码连接时，可以继续使用 shared_ptr 。

### Function Binding and Callbacks ( 函数绑定和回调 )

现有代码使用 **boost :: bind** 和 **boost :: function** 来进行函数绑定和回调。 对于新代码，请使用 **gutil** 中的回调和绑定类。 虽然功能较少（绑定不支持参数占位符，包装函数指针或函数对象），但它们通过参数生命周期管理的方式提供更多选项。 例如，当 **Callback** 超出范围时，绑定参数的类扩展 **RefCounted** 将在绑定期间递增，并减少。

有关详细信息，请参阅 **gutil/callback.h** 中的大文件注释，**util/callback_bind-test.cc** 作为示例。

## CMake Style Guide ( CMake 样式指南 )

**CMake** 允许以较低，上限或混合大小写的命令。 要保持 **CMake** 文件一致，请使用以下准则：

*   小写的 **built-in commands**

    ```
    add_subdirectory(some/path)
    ```

*   大写的 **built-in arguments**

    ```
    message(STATUS "message goes here")
    ```

*   大写的 **custom commands or macros**

    ```
    ADD_KUDU_TEST(some-test)
    ```

## GFlags

**Kudu** 使用 **gflags** 进行命令行和基于文件的配置。使用这些准则添加新的 **gflag** 。所有新 **gflags** 必须符合这些准则。现有的不符合要求的产品将及时符合规定。

### Name ( 名称 )

**gflag** 的名字传达了很多信息，所以选择一个好名字。该名称将传播到其他系统，如 [配置参考](/pages/viewpage.action?pageId=10813644)。

*   多字名称的不同部分应以下划线分隔。例如， **fs_data_dirs** 。
*   该名称应以其影响的上下文为前缀。例如， **webserver_num_worker_threads** 和 **cfile_default_block_size** 。上下文可能很难定义，所以请记住，这个前缀将用于将类似的 **gflags** 组合在一起。如果 **gflag** 影响整个过程，那么它不应该是前缀。
*   如果 **gflag** 是一个数量，该名称应该后缀单位。例如， **tablet_copy_idle_timeout_ms** 。
*   如有可能，请使用短名称。这将为手动输入命令行选项的人节省时间。
*   这个名称是 **Kudu** 兼容性合同的一部分，不应该没有很好的理由来改变。

### Default value ( 默认值 )

 选择默认值通常很简单，但像名称一样，它传播到其他系统中。

*   默认值是 **Kudu** 的 **compatibility contract** ( 兼容性合同 ) 的一部分，如果没有很好的理由，不应该改变。

### Description ( 描述 )

**gflag** 的描述应该补充名称并提供其他上下文和信息。与名称一样，说明传播到其他系统。

*   描述可以包括多个句子。每个都应该以一个大写字母开头，以一个句点结尾，并在之前的一个空格开始。

*   描述不应包含 **gflag** 的类型或默认值;它们是带外提供的。
*   描述应该在第三人称。不要使用像你这样的话。
*   **gflag** 描述可以自由更改;  **Kudu** 的发行预计不会保持不变。

### Tags ( 标志 )

 **Kudu** 的 **gflag** 标记机制为每个 **gflag** 添加了机器可读上下文，用于消耗系统，如文档或管理工具。请参阅 **flag_tags.h** 中的大块注释，以获取准则。

### Miscellaneous ( 杂 )

*   避免为同一个逻辑参数创建多个 **gflags** 。例如，许多 **Kudu** 二进制文件需要配置一个 **WAL** 目录。而不是创建 **foo_wal_dir** 和 **bar_wal_dir gflags** ，最好使用一个单一的 **kudu_wal_dir gflag** 来普遍使用。

## Testing ( 测试 )

### All new code should have tests. ( 所有新的代码都应该有测试。 )

在现有文件中添加新的测试，或根据需要创建新的测试文件。

### All bug fixes should have tests. ( 所有错误修复都应该有测试。 )

如果由现有测试用例触发，则可以修复错误而不添加新测试。例如，如果在 **20** 分钟左右之后运行多线程系统测试时出现了一个 **race **，那么值得尝试更有针对性的测试用例来触发该错误。但是如果这很难做，现有的系统测试就够了。

### Tests should run quickly (&lt; 1s). ( 测试应该很快运行（&lt;1s）。 )

如果要编写时间密集的测试，请使运行时依赖于通过 **KUDU_ALLOW_SLOW_TESTS** 环境变量启用的 **KuduTest#AllowSlowTests** ，并由 **Jenkins** 测试执行使用。

### Tests which run a number of iterations of some task should use a `gflags` command-line argument for the number of iterations. ( 运行一些任务的迭代的测试应该使用 gflags 命令行参数来执行迭代次数。 )

这对于编写快速压力测试或性能测试非常方便。

### Commits which may affect performance should include before/after `perf-stat(1)` output. ( 可能影响性能的提交应包括在 perf-stat(1) 输出 之前/之后。 )

这将显示性能提升或 **non-regression** ( 不回归 )。**Performance-sensitive** ( 性能敏感 ) 代码应包括一些可用作目标基准测试用例。