为何RetrieveRequest与SearchRequest(内部proto)要显式序列化Plan?
RetrieveRequest和SearchRequest的proto定义采用如下形式存储搜索/查询计划:
// milvus/internal/proto/internal.proto message RetrieveRequest { ... bytes serialized_expr_plan = 6; ... }
另一种方案是将PlanNode对象直接嵌入RetrieveRequest中,无需显式进行Marshal/Unmarshal操作:
message RetrieveRequest { ... plan.PlanNode plan = 6; ... }
优先选择第一种显式序列化方案的原因如下:
兼容性与版本适配:若
PlanNode结构后续变更,直接嵌入的方式会引发新旧版本proto消息兼容问题——旧服务无法解析带修改后PlanNode的新版请求,新版服务解析旧请求也可能出错。而序列化后的bytes字段,只要两端约定好序列化规则,可在一定程度上实现版本兼容,甚至能通过中间层做格式转换,降低版本迭代的兼容性风险。解耦模块依赖:直接嵌入
plan.PlanNode会让所有依赖internal.proto的模块都必须引入plan相关的proto定义,提升了模块耦合度。使用bytes字段的话,internal.proto无需依赖plan的proto,仅需知晓这是一段序列化字节数据,模块依赖关系更清晰,也便于不同模块独立迭代。灵活性扩展:后续若需更换查询计划的序列化方式(比如从Protobuf换成JSON),bytes字段方案无需修改
RetrieveRequest的proto结构,仅需调整序列化/反序列化逻辑即可。而直接嵌入PlanNode的话,更换格式必须修改proto定义,影响范围大。简化跨服务传输逻辑:在跨服务场景中,字节数据传输比嵌套proto对象更简单——网关或代理层只需透传bytes字段,无需理解
PlanNode的内部结构,减少了中间层的处理复杂度。
内容的提问来源于stack exchange,提问作者Qi Xiang

