Lorawan-stack: 调查gogo proto插件的未来

创建于 2020-06-25  ·  15评论  ·  资料来源: TheThingsNetwork/lorawan-stack

概括

https://github.com/gogo/protobuf不再维护https://github.com/gogo/protobuf/issues/691 (当前)

我们为什么需要这个?

这是我们的依赖,它与新的golang/protobuf版本不兼容,越来越多的包依赖它,因此我们需要替换golang/protobuf版本,这取决于我们直接依赖的过时版本,甚至可能以这种方式破坏包裹

什么已经存在? 你现在看到了什么?

gogo/protobuf依赖

缺什么? 你要看什么?

把这个想出来(解决;计算出;弄明白

您建议如何实施?

弄清楚是否会出现新的维护者或具有功能奇偶校验的不同插件?
只使用香草 protobuf?

你打算如何测试这个?

测试

你能自己做这个并提交一个拉取请求吗?

是的

dependencies technical debt

最有用的评论

请计划下周的电话会议,以便我们可以离线讨论。

我认为我们应该经历这个痛苦的过程,并在一两周内集中精力解决这个问题。 并且为了避免我们做其他事情,否则这会导致很多冲突。 拥有尽可能多的手需要确切地知道在哪些情况下我们要做什么,尽可能多地分配任务并密切关注奖品。

所有15条评论

我们使用的验证插件不再支持 GoGo
https://github.com/envoyproxy/protoc-gen-validate/pull/340

我认为最好的前进方式是跟随生态系统并从 gogo/protobuf 迁移。 随着越来越多的其他依赖项远离 gogo,我认为继续使用它会变得越来越困难。 当然,迁移需要大量的工作,所以如果我们这样做,我们需要想出一个好的计划。

@rvolosatovs可能更了解我们gogottn生成器中设置的自定义选项,但这是我在 proto 文件中发现的显式选项的内容:

  • 我们可以从删除gogoproto.customnamegogoproto.stdtimegogoproto.stdduration以及goproto_enum_prefix选项开始。 这些相对容易删除,因为 Go 编译器会立即抱怨任何由此产生的问题。
  • 删除gogoproto.embed选项意味着我们不能再访问嵌入的字段(Go 编译器会帮助我们找到那些),并且消息不再满足某些接口(这可能更困难)。
  • gogoproto.nullable选项会做更多的工作,因为我们将不得不开始使用 getter,并添加 nil-checks。 Go 编译器可能不会发现由此产生的问题。 可能的解决方法是暂时将这些字段设为私有,然后重写为 getter/setter,最后再次将这些字段设为公开。
  • 有问题的是使用gogoproto.customtype的字段和使用gogoproto.enum_stringer选项的枚举。 对于那些我们经常将它们编组/解组为 JSON 的方式。 对于自定义的bytes字段,例如 EUI、DevAddr 等,我们可以将类型(在 proto 消息中)更改string (二进制兼容)。 对于枚举,我担心它会破坏 JSON API,因为它们现在被接受(由 UnmarshalJSON)作为字符串和整数。

也许这也是开始考虑我们的 v4 API 的好时机,因为我可以想象我们可能会发现更多(API 突破)惊喜。

我认为最好的方法是首先尝试https://github.com/alta/protopatch。 根据结果​​:

  • 如果我们所有的需求都被满足 -> 迁移并忘记这个(应该只是在api目录中搜索和替换)
  • 如果只有我们的一些需求覆盖,有没有覆盖的插件自定义选项- >我们应该评估对每个选项的基础,然后删除这些自定义选项,或者,也许有助于protopatch如果它是一个省力的特点。 不过,这实际上取决于选项 - 如果我们谈论的是customtype - IMO 肯定有理由做出贡献,但也许像stdtime - 不是那么多。

展望未来,我认为我们不应该在运行时直接在内部组件中使用 vanilla protobuf protos(鉴于今天提供的 protobuf 功能集)。
仅将 protobuf 用于(反)序列化才有意义,因此用于存储和 API 层。 然而,在内部,使用普通的普通生成的 Go 原型对我来说没有意义。
因此,例如,NS:

  1. 从注册表中获取*ttnpb.EndDevice (vanilla 生成的 Go 类型),从存储的二进制数据中反序列化
  2. *ttnpb.EndDevice转换为T_device ,(注意:也许最初或永远只是一个包装器)
  3. 在 NS 内部使用T_device
  4. T_device转换为*ttnpb.EndDevice (注意:如果我们使用包装器,这可能是一项微不足道的、非常快速的任务,因为我们只需要修改更改的字段,甚至可以在二进制文件上执行直接数据)
  5. set *ttnpb.EndDevice ,序列化成二进制数据

我不赞成 gogo 的(较小的)替代方案。 感觉就像推罐头一样。 让我们尽可能保持原状,尤其是当我们需要再次决定前进的最佳方式时。

我同意我们可以考虑在某些地方使用中间类型,而不是到处依赖生成的 protos。 它基本上将数据传输对象(DTO:原型,也用于存储)与数据访问对象(DAO:我们如何使用它们)分开。 如果这主要是阅读,我们还可以声明接口,看看我们能做到多远。

也就是说,我不会将整个 NS 更改为使用T_device ,而是根据需要使用特定的结构和/或接口。

让我们将此讨论移至 11 月

@rvolosatovs您反对使用自定义 JSON 封送

如果我们最终直接使用 vanilla protos,那么巨大的迁移负担和大量的样板文件。
不过我并不真的反对,我只是认为我们应该首先尝试找到一个简单的非侵入性替代方案,如果这不可能,那么就重新处理整个事情。

恐怕我们开始依赖的任何插件都会在某个时候处于无人维护的状态。 一般来说,我赞成让事情尽可能接近香草。 如果这意味着nil检查的频率比我们喜欢的要多,那么就这样吧。 它也可以对我们有利,因为我们知道事情没有设置,而不是初始化的结构。

我担心无论我们如何重构我们的整个代码库都会很痛苦。 我们的(gogo 生成的)proto 结构现在到处都在使用(gRPC API、HTTP API、事件、错误、内部、Redis DB 等),因此更改为其他内容(无论其他内容是什么)都会有所影响我们代码库中的几乎所有内容,以及它现在的样子,都同时进行。

硬性要求是我们不会破坏 v3 API 的兼容性。 即使我们决定在这种情况下开始使用 v4 API(至少在内部),我们仍然必须继续为现有用户支持该 v3 API。

从长远来看,我认为通过将我们的(版本化的、主要内部稳定的)外部 API 与我们的内部(非版本化、次要的稳定)API 和我们的(版本化的、稳定的)数据库文档分离,我们会对自己有很大的帮助. 然后我们可以编写或生成函数来在我们的内部 API 和其他 API 之间进行转换。

但我认为我们现在可以采取一些步骤:

JSON

为了保持我们的 v3 JSON API 兼容,我认为我们的第一个 TODO 是生成 JSON 编组器和解组器,它们了解如何编组/解组我们的自定义类型。 我认为这样做是明智的,因为Go 的协议缓冲区 JSON 格式的实现没有稳定性承诺,所以我们最好自己控制它。 这样做还可以让我们在编组为 JSON 时考虑字段掩码。 在grpc-gateway运行时我们可以注册编解码器,所以我们可以编写一个编解码器来调用我们自己的(生成的)(un)编组器而不是 {gogo,golang}/protobuf 的 jsonpb。

生成新的 protos _next to_ 旧的

我已经在这里尝试过: https :

这确实使 protobuf 抱怨类型注册表,因此我们可能需要从旧的 protos 中删除golang_proto.RegisterType以使其工作。 删除它可能会破坏google.protobuf.Any解析,但我们只在错误和事件中使用它们,因此我们可能会找到针对这些特定情况的解决方法。

生成在新旧原型之间转换的函数

这仅适用于过渡期,但对于长期解决方案,我们希望生成类似的转换器。

一一更新服务

我已经在这里尝试了一个简单的服务: https :

请注意,这只会更改 grpc 服务本身。 grpc-gateway 在 JSON 端仍然使用旧的 gogo 东西,然后调用内部 gRPC 服务器,然后运行新的实现。

在此处推送了一些初始依赖项更新和向后兼容性解决方法: https :

我们越来越多的依赖项正在升级到 protobuf 1.4 和 V2 API,我们保持开放的时间越长,我们在尝试升级依赖项时遇到的问题就越多。

我们真的应该给予更多优先考虑,并决定我们将如何处理这一切。

请计划下周的电话会议,以便我们可以离线讨论。

我认为我们应该经历这个痛苦的过程,并在一两周内集中精力解决这个问题。 并且为了避免我们做其他事情,否则这会导致很多冲突。 拥有尽可能多的手需要确切地知道在哪些情况下我们要做什么,尽可能多地分配任务并密切关注奖品。

下一步:

  1. @rvolosatovs将工具更新为尽可能接近“vanilla”:

    • 删除诸如unconvertgofumpt东西以及我们在 protoc 之上所做的任何其他事情

    • protoc-gen-gogottn切换到protoc-gen-gofast (或最接近香草的任何东西)

    • 在我们的 proto 文件中显式添加(gogoproto.*)选项,以便它们呈现与现在相同

  2. 我们需要重构我们的代码以使用 Getters 而不是直接的字段访问。 也许像goplsrf可以帮助解决这个问题。
  3. 我们开始一一删除(gogoproto.*)选项并更新使用它的代码。 也许像goplsrf可以帮助解决这个问题。

    • 删除gogoproto.populate并更新测试(https://github.com/TheThingsNetwork/lorawan-stack/issues/342)

    • 删除gogoproto.customname和更改EUI -> Eui等。

    • 删除gogoproto.embed 。 我们确实需要确保消息仍然实现诸如ValidateContext(context.Context) errorExtractRequestFields(m map[string]interface{})

    • 删除gogoproto.nullable并确保我们在可能的情况下使用 Getters,否则进行 nil 检查。

    • ...

@rvolosatovs让我们尝试为 v3.11.3 迈出第一步。 完成后,请重新添加其他受让人,让我们再次讨论。

此页面是否有帮助?
0 / 5 - 0 等级