Iteration 13 从 5/7 开始到 5/20 结束,为期两周。这个周期成功合并了不少之前提交的 PR,感觉非常有成就感。

difftastic

difftastic 是一个使用 Rust 开发的能理解语义的 diff 工具。

在这个周期中,我合并了两个 PR:

fix: Bad padding of column numbers at the end of files 修复了文件末尾的 padding 不正确问题:

这里的 str2 应当需要跟上面的 Violets 悬空对齐,但是并没有。简单的调试后发现是计算 padding 的时候错误地忽略了一些情况,去掉之后就修复了:

feat: Improve binary content guess 则是改进了二进制内容的检测算法。

在过去,difftastic 使用一种比较简单粗暴的方法来检测二进制文件:

let num_replaced = String::from_utf8_lossy(bytes)
    .to_string()
    .chars()
    .take(1000)
    .filter(|c| *c == std::char::REPLACEMENT_CHARACTER || *c == '\0')
    .count();

num_replaced > 20

在前一千个字节中寻找非法的 Unicode 字符以及 \0,如果超过 20 个就认为是二进制文件。在 Issue Treat PDFs as binary files 中,作者希望能够检测 PDF 并将其作为二进制文件处理。给定一个文件头,要求我们判断是否为二进制文件,其实很容易可以想到根据 magic number 来判断。我在 PR 中引入了 tree_magic_mini 来检测这段内容的 MIME,并对常见的二进制类型做了统一的处理:

let mime = tree_magic_mini::from_u8(bytes);
match mime {
    // Treat pdf as binary.
    "application/pdf" => return true,
    // Treat all image content as binary.
    v if v.starts_with("image/") => return true,
    // Treat all audio content as binary.
    v if v.starts_with("audio/") => return true,
    // Treat all video content as binary.
    v if v.starts_with("video/") => return true,
    // Treat all font content as binary.
    v if v.starts_with("font/") => return true,
    _ => {}
}

这样 difftastic 就能够正确地处理绝大多数二进制类型了。

databend

正如上个周报中提到的,这个周期我花了不少功夫来提升 Databend 配置的兼容性。在 RFC: Config Backward Compatibility 的推动下,我为 databend-querydatabend-meta 都加上了配置的兼容层。同时在 PR refactor: Reuse StorageConfig in stage 中还为内部的 Storage 相关配置做了大范围重构,使得内部的逻辑都可以共享同一个配置文件,不需要再重复实现相似的逻辑。

处理 databend-meta 的配置兼容比较坎坷,因为内部的配置项特别多,又经历了不少重构,所以 meta 有不少配置项,其中不少配置项还都没有遵循统一的命名风格。所以在纠结了很久之后,我发现了 serfig 的全新用法:

pub fn load() -> MetaResult<Self> {
    let arg_conf = Self::parse();

    let mut builder: serfig::Builder<Self> = serfig::Builder::default();

    // Load from the config file first.
    {
        let config_file = if !arg_conf.config_file.is_empty() {
            arg_conf.config_file.clone()
        } else if let Ok(path) = env::var("METASRV_CONFIG_FILE") {
            path
        } else {
            "".to_string()
        };

        builder = builder.collect(from_file(Toml, &config_file));
    }

    // Then, load from env.
    let cfg_via_env: ConfigViaEnv = serfig::Builder::default()
        .collect(from_env())
        .build()
        .map_err(|e| MetaError::InvalidConfig(e.to_string()))?;
    builder = builder.collect(from_self(cfg_via_env.into()));

    // Finally, load from args.
    builder = builder.collect(from_self(arg_conf));

    builder
        .build()
        .map_err(|e| MetaError::InvalidConfig(e.to_string()))
}

用户可以维护一个独立的 cfg env wrapper,从 env loading 数据并转换成 cfg。不过这种用法对于少量的 env 不兼容来说还是比较麻烦。@DCjanus 在 Issue Add bind attribute support 中同样提到了类似的需求。

总结

下个周期后专注于 Databend 读取压缩文件的支持,后面 databend 将能够实现直接读取 gzip,zstd 等压缩文件~