欢迎光临散文网 会员登陆 & 注册

解决 Databend 命令行参数加载问题

2022-06-02 17:06 作者:Databend  | 我要投稿

前言

Iteration 11[1] 从 4/9 开始到 4/22 结束,为期两周。

这个周期非常快乐,我造了一堆轮子来解决 Databend 的命令行使用体验问题:

  • serde-bridge[2]:将一个值在不同的 serde 实现中进行转换

  • serde-env[3]:支持将环境变量解析为嵌套的结构体

  • serfig[4]:基于 serde 实现的多层配置系统,支持从环境变量,配置文件,自身等多个地方读取并合并配置

最终实现的效果是 Databend 能够按照指定的顺序依次加载来自配置文件,环境变量和命令行参数中的配置:

背景

通过命令行参数配置:Databend 经历早期的野蛮生长之后,现在终于有时间可以稍微打磨一下使用体验。首当其冲是繁复而不成体系的配置项,以配置 S3 存储的 Bucket 为例:

通过命令行参数配置:

通过环境变量配置:

通过配置文件配置:

出现这种状况的一大原因是 clap 的不良设计导致用户使用中出现的畸形姿势:

clap 的 Parser 不支持结构体,所有 args 都必须平铺,导致用户必须为所有的结构体加上 #[clap(flatten)]:

更糟糕的是,clap 依赖字段名来唯一区分参数,这就要求整个结构体中不得出现重名的字段。比如下列这样的代码能编译,但是无法正常运行:

所以大家开始写这样的代码:

Args 与 Env 的关系已经非常混乱了,databend 还需要支持从配置文件中加载。为了保障正确的加载顺序,社区甚至开始写宏来强行再次加载环境变量:


思考

在动手改进之前,首先考虑最理想的状况是怎样的:

• 正确的加载顺序:同名字段会按照 config -> env -> args 的顺序记载,后者覆盖前者

• 统一的命名体系:同一个字段在不同地方使用统一的命名风格,比如说 storage.s3.bucket, --storage-s3-bucket, STORAGE_S3_BUCKET

• 减少冗余代码:尽可能减少维护者需要写的重复代码

社区在 Issue bug: config overwrite when specify --config and any other command line args.[5] 中贡献了一个 idea:将 config-rs[6] 与 clap 结合起来,让 clap 能够作为 config-rs 的一个 Source。我为 config-rs 提交了 proposal: Implement serde::Serializer and Source/AsyncSource for Value[7],但是在尝试实现 demo 的时候遇到了无法解决的问题,以至于我开始觉得我们需要新的方法和新的思路。

好,跳出来思考这个问题:

配置加载实际上就是按照顺序从不同的地方加载数据,解析成我们的 Config 结构体并进行合并的过程。所以我们需要:

  • 将环境变量解析为嵌套的结构体

  • 一个统一的数据表示方式

  • 将来自不同的地方的数据进行合并  实现  实现

serde-env

最开始我尝试使用了 envy[8],但是它不支持将环境变量解析为嵌套的结构体,为此我开发了 serde-env[9]:

思路其实很简单,serde-env 内部将环境变量表示为使用 _ 分隔的 tree,于是上述例子中的 Test.cargo.home 实际上就能转化为 CARGO_HOME。

延续这样的思路,serde-env 还能够支持形如这样的结构体:

有效解决了环境变量转化为结构体的问题。

serde-bridge

为了能够处理配置之间的合并,我开发了 serde-bridge[10]:

它是一个到 serde API one-to-one 的 mapping,跟 serde-value[11] 相似,但是更加完整,同时实现了 {De,S}erialize[r] 等类型。任何 serde 实现都可以基于 serde_bridge::Value 作为中间层来进行转换。

serfig

在上述库的支持下,serfig 通过 serde_bridge::Value 来合并配置并对外暴露 Builder 的接口:

跟 clap 的整合也非常容易,强大的 serde_bridge::Value 使得我们能够将结构体本身也作为一个数据源 from_self,以 Databend 为例:

我们首先使用 Config::parse() 来加载命令参数,然后在最后使用 from_self(arg_conf) 来覆盖前面获取到的数据。

后续

目前的实现还有不少问题,我们仍未解决 #[clap(flatten)] 导致的各种问题:


• 相同的字段还是会冲突• 需要手动指定 clap 的 long 字段未来可能会想办法自行实现 clap Parser 来彻底解决这些不一致的问题。


总结

快乐的造轮子周期,以至于这周一直在发 #今天用 而不是 #今天学,下个周期还是要多输入一些东西~


引用链接

 Iteration 11: https://github.com/users/Xuanwo/projects/2/views/1?filterQuery=iteration%3A%22Iteration+11%22
 serde-bridge: https://github.com/Xuanwo/serde-bridge
 serde-env: https://github.com/Xuanwo/serde-env
 serfig: https://github.com/Xuanwo/serfig
 bug: config overwrite when specify --config and any other command line args.: https://github.com/datafuselabs/databend/issues/4362
 config-rs: https://github.com/mehcode/config-rs
 proposal: Implement serde::Serializer and Source/AsyncSource for Value: https://github.com/mehcode/config-rs/issues/315
 envy: https://github.com/softprops/envy
 serde-env: https://github.com/Xuanwo/serde-env
 serde-bridge: https://github.com/Xuanwo/serde-bridge
 serde-value: https://github.com/arcnmx/serde-value


关于 Databend

Databend 是一款开源、弹性、低成本,基于对象存储也可以做实时分析的新式数仓。期待您的关注,一起探索云原生数仓解决方案,打造新一代开源 Data Cloud。

  • Databend 文档:https://databend.rs/

  • twitter:https://twitter.com/Datafuse_Labs

  • Slack:https://datafusecloud.slack.com/

  • Wechat:Databend

  • GitHub :https://github.com/datafuselabs/databend


文章首发于公众号:Databend


解决 Databend 命令行参数加载问题的评论 (共 条)

分享到微博请遵守国家法律