迁移实体和根字段
将实体字段从一个子图传输到另一个子图
作为你的supergraph增长时,你可能想要将一个 subgraph的一部分移动到另一个子图。例如,假设您的 Payments 子图定义了一个 Bill
实体:
type Bill @key(fields: "id") {id: ID!amount: Int!payment: Payment}type Payment {# ...}
随着你的 图的发展,你决定为你的 supergraph 添加一个 专用 计费 子图。合理地将计费功能及其账单金额移动到那里。你希望 子图模式 看起来如下:
type Bill @key(fields: "id") {id: ID!payment: Payment}type Payment {# ...}
type Bill @key(fields: "id") {id: ID!amount: Int!}
本指南展示了如何使用 @override
指令将一个 amount
字段从 @override
指令 指令从一个子图迁移到另一个子图。
使用 @override
迁移
💡 提示
Apollo 建议 企业许可证 的组织逐步使用递进的 @override
迁移。请参阅 递增迁移与递进 @override 部分。
按照以下步骤一次性将一个字段从一个子图迁移到另一个子图。这些步骤使用了页面顶部描述的示例付款和计费子图中的 amount
字段,但你也可以使用任何实体字段。
如果 @override 指令尚未导入,请将其包含在你的模式 @link 导入中:
计费子图extend schema@link(url: "https://specs.apollo.dev/federation/v2.7",import: ["@key", "@shareable", "@override"])部署新的计费子图版本,该版本定义并解析你想要移动的字段。在这种情况下,该模式向
Bill
实体 添加了一个新的amount
字段。
type Bill @key(fields: "id") {id: ID!+ amount: Int! @override(from: "Payments")}
- 应用 @override 指令告知路由器解析计费子图中的 amount 字段,而不是付款子图中的字段。
使用 rover subgraph publish 将计费子图更新后的模式发布到 GraphOS。
当路由器接收到其更新的supergraph 规范时,它立即开始解析 Billing 子图中
Bill.amount
字段,同时继续解析 Payments 子图中的Bill.payment
。ⓘ 注意
您可以通过单个更改迁移多个实体字段。要这样操作,将
@override
应用于您想要移动的每个实体字段。您甚至可以通过这种方式迁移整个实体。现在由于
Bill.amount
已解析在 Billing 子图中,您可以安全地删除该解析器及其字段从 Payments 子图中:Payments子图type Bill @key(fields: "id") {id: ID!- amount: Int!payment: Payment}type Payment {# ...}计费子图type Bill @key(fields: "id") {id: ID!amount: Int! @override(from: "Payments")}在此更改后,重新部署 Payments 子图并发布其更新后的规范。
ⓘ 注意
由于router已经预先忽略 Payments 子图中的
Bill.amount
(得益于@override
),您可以安全地按任何顺序发布更新后的规范或部署子图。从 Billing 子图中移除
@override
指令,因为其不再有任何效果:Payments子图type Bill @key(fields: "id") {id: ID!payment: Payment}type Payment {# ...}计费子图type Bill @key(fields: "id") {id: ID!- amount: Int! @override(from: "Payments")+ amount: Int!}
随着您部署 Billing 子图并发布此最终规范更改,您已成功实现了Bill.amount
到 Billing 子图的迁移,且无中断服务。
渐进式迁移与逐步 @override
自从2.7
渐进式@override
是 GraphOS 路由器的企业特性之一,并且需要具有GraphOS 企业计划的组织。如果您的组织没有企业计划,您可以通过注册免费的企业试用来试用它。
按照以下步骤逐步将一个字段从其中一个子图迁移到另一个子图。这些步骤使用页面顶部描述的 Payments 和 Billing 子图中的示例金额
字段,但您可以将这些步骤应用于任何实体字段。
如果 @override 指令尚未导入,请将其包含在你的模式 @link 导入中:
计费子图extend schema@link(url: "https://specs.apollo.dev/federation/v2.7",import: ["@key", "@shareable", "@override"])部署 Billing 子图的新版本,该版本定义并解析要迁移的字段。在这种情况下,该方案为
Bill
实体添加了一个新的amount
字段。
type Bill @key(fields: "id") {id: ID!+ amount: Int! @override(from: "Payments", label: "percent(1)")}
使用
@override
指令告诉路由器在计费子图而不是付款子图中解析amount
字段。将
label
参数添加到@override
指令中可以设置要直接分配到计费子图的流量百分比。从小百分比开始。设置label: "percent(1)"
意味着计费子图将解析amount
的查询的 1%,而其余的 99% 将由付款子图解析。
使用
rover subgraph publish
将计费子图的更新架构发布到 GraphOS。当路由器接收其更新的超级图架构时,它开始大约 1% 的时间从计费子图解析
Bill.amount
字段,而其余 99% 仍然从付款子图解析。ⓘ 注意
您可以通过单个更改迁移多个实体字段。要这样操作,将
@override
应用于您想要移动的每个实体字段。您甚至可以通过这种方式迁移整个实体。逐步迭代地增加分配给计费子图的流量百分比,更新您的路由器的超级图架构,并验证计费子图的性能。继续进行,直到迁移完成后以
label: "percent(100)"
关键字,并且所有流量都由计费子图解析。计费子图type Bill @key(fields: "id") {id: ID!amount: Int! @override(from: "Payments", label: "percent(100)")}现在,由于
Bill.amount
的解析已移至计费子图,您可以安全地从付款子图中删除该字段及其解析器:Payments子图type Bill @key(fields: "id") {id: ID!- amount: Int!payment: Payment}type Payment {# ...}计费子图type Bill @key(fields: "id") {id: ID!amount: Int! @override(from: "Payments")}在此更改后,重新部署 Payments 子图并发布其更新后的规范。
ⓘ 注意
由于router已经预先忽略 Payments 子图中的
Bill.amount
(得益于@override
),您可以安全地按任何顺序发布更新后的规范或部署子图。从 Billing 子图中移除
@override
指令,因为其不再有任何效果:Payments子图type Bill @key(fields: "id") {id: ID!payment: Payment}type Payment {# ...}计费子图type Bill @key(fields: "id") {id: ID!- amount: Int! @override(from: "Payments")+ amount: Int!}
随着您部署 Billing 子图并发布此最终规范更改,您已成功实现了Bill.amount
到 Billing 子图的迁移,且无中断服务。
安全使用渐进式 @override
使用渐进式 @override
时,单个 操作 可能会导致多个 查询计划。路由器会缓存查询计划,其中唯一的、被覆盖的标签组成为缓存密钥。
在渐进式 @override
之前,为给定的操作生成了一个查询计划。使用渐进式 @override
,操作 "路径" 中的每个唯一标签都将使查询计划数量加倍。
缓解此问题的一些策略
- 不要无限期地保留渐进式
@override
。尽快将字段和从@override
指令中移除label
参数迁移。 - 在迁移在一起的字段间共享标签。例如,如果您正在一起迁移
Bill.amount
和Bill.payment
,为这两个字段使用相同的标签。这将确保迁移不会导致查询计划数量增加。 - 使用小而已知的标签集合(例如
percent(5)
、percent(25)
、percent(50)
)。
使用功能标志服务自定义渐变@override
行为
直接使用时,该router支持根据给定百分比解析标签的percent(x)
语法。不幸的是,更新此数字需要发布一个subgraph并重新部署router。为了避免这种情况,您可以使用功能标志服务动态更新标签值。
该router为协处理器和Rhai脚本提供了一个接口,用于解析任意标签。这允许您在上传无需发布subgraph的情况下调整或禁用标签的上线状态。实现此功能的协处理器或Rhai脚本应采取以下步骤:
- 实现
SupergraphService
- 检查
apollo_override::unresolved_labels
上下文键以确定哪些标签在模式中尚未由router解析。 - 使用您的功能标志服务(或其他机制)解析标签。
- 将解析后的标签添加到
apollo_override::labels_to_override
上下文键。
ⓘ 注意
未解析的标签是该模式中尚未由router解析的标签。它们可能并非都与传入的operation相关。作为最后一步,该router将过滤解析的标签。它将仅保留与该操作相关的标签。这样,它最小化了查询计划缓存键中标签的集合。协处理器或Rhai脚本应解决模式中的所有标签,而不仅仅是与操作相关的标签。
有关使用Darkly解决标签的协处理器的示例实现,请参阅启动,请参阅router存储库中的示例。
使用手动组合优化频繁部署
⚠️ 警告
该方法需要在subgraph和router更新之间进行仔细协调。如果没有严格控制部署和模式更新的顺序,可能会导致中断。对于大多数用例,Apollo建议使用上面的@override
方法。
使用@override
迁移entity字段field允许我们以零停机时间增量迁移字段。然而,这样做需要三个单独的模式发布。如果您使用手动组合,则每项模式更改都需要重新部署router。在仔细协调的情况下,您可以使用单个router重新部署执行相同的迁移。
在计费 子图 中,定义
Bill
实体及其对应的 解析器。这些新的解析器应与被替代的支付 子图 解析器表现相同。Payments子图type Bill @key(fields: "id") {id: ID!amount: Int!payment: Payment}type Payment {# ...}计费子图type Bill @key(fields: "id") {id: ID!amount: Int!}将更新后的计费 子图 部署到您的环境中,但还不需要发布更新后的模式。
- 此时,计费 子图 可以成功解析
Bill
对象,但因为其 超图模式 尚未更新,所以路由器尚不知情。发布模式将导致 组合 错误。
- 此时,计费 子图 可以成功解析
在支付 子图 中,从
Bill
实体及其相关的 解析器 中删除迁移的字段(但尚未部署此更改):Payments子图type Bill @key(fields: "id") {id: ID!payment: Payment}type Payment {# ...}计费子图type Bill @key(fields: "id") {id: ID!amount: Int!}使用
rover supergraph compose
以您常用的配置组成更新的 超图模式。- 该更新后的 超图模式 表明计费 子图 可解析
Bill.amount
,而支付 子图则不解析。
- 该更新后的 超图模式 表明计费 子图 可解析
假设 CI 成功完成,使用新超图模式部署您的 router 的更新版本。
当此部署完成后, router 将开始在计费 子图中解析
Bill
字段,而不是支付子图。当您的新 router 实例正在部署时,您可能会遇到两种不同的
Bill.amount
字段解析方式(旧实例仍从支付中解析它)。两个 子图 必须以完全相同的方式解析该字段,否则在过渡期间您的客户端可能会看到不一致的数据。
部署不带迁移字段的更新后的支付 子图。
- 此时可以安全地删除此定义,因为您的 router 实例正在专用计费 子图中使用。
就这样!迁移的字段已移至新的 子图,只有一个 router 重部署。