Shopify functions

10,000 字节 metafield 陷阱:折扣 Function 悄悄失效的那一刻

你搭了正确的架构:业务规则放在 JSON metafield 里,Discount Function 负责读取,改促销不用重新部署。直到商家多加了几个价格档位——全店折扣突然不再生效。结账没报错,订单没异常。这篇文章讲清楚为什么,以及能活下来的设计。

这是“Shopify Functions 生产实战”系列的第二篇。第一篇讲了如何干净地拒绝折扣码。这一篇讲一种正因为“看起来什么都没坏”才会真金白银亏钱的失效模式。

没有人提前警告过你的场景

关于可维护的 Shopify Function,标准建议是对的:把业务规则和代码分开。不要把“VIP 客户满 100 美元打 85 折”写死在代码里,而是把规则放进一个 JSON metafield(或 app 自有的 metaobject),让 Function 通过输入查询读取。商家想加新档位,改配置就行——不用找开发,不用重新部署。

测试里跑得很好,生产上也稳定跑了几个月。然后某天,商家加上了第四十个价格档位,保存配置——全店的折扣悄悄不再生效。

商家发现问题的途径,是顾客发邮件问“我的 VIP 折扣去哪了”——或者更糟,月底营收报表上折扣那一栏是零。

背后的两条限制

两条都写在 Shopify Functions API 文档里,但埋在一张限制表里,大多数人只在搭建时读过一次,然后就忘了。

限制一:输入查询是静态的,上限 3,000 字节。 Function 的输入查询在构建时就固定了(run.graphql)。商家每发明一种新的规则类型,你不可能在运行时往查询里加新字段——查询会很快撑爆上限。这正是“配置放进一个 JSON blob”这个模式存在的原因:查询里只取一个 metafield 字段,结构任意复杂的内容放在 JSON 里面。到这里都没问题。

限制二:超过 10,000 字节的 metafield 值不会被返回。 这才是陷阱。Shopify 文档原文:“Metafields with values exceeding 10,000 bytes in size will not be returned.” 不是截断——是缺席。metafield 节点返回为空,就像从来没设置过一样。

跟着这条失效链路走一遍:

  1. 配置 JSON 长到超过 10,000 字节(几十个带条件的价格档位,比你想象的更快到达)。
  2. 输入查询请求这个 metafield;Shopify 扣住不给。
  3. Function 看到的是“没有配置”,于是执行未配置时的行为——通常是:解析不到任何东西,匹配不到任何规则,返回空操作列表。
  4. 这个折扣是挂在 Function 上的自动折扣,于是店铺干脆不打折了。结账有效,订单成交,没有人收到通知。

失效在每一层都是静默的。而你最后的防线——Function 日志——也救不了你:日志写入上限 1 kB,超出即截断。

能活下来的设计

不需要放弃“配置放 metafield”这个模式——它仍然是正确的,Shopify 官方最佳实践也建议复杂配置用 JSON metafield。要做的是让它对大小有感知、失效时有声音

1. 给字节数做预算,并且真的去量。 10,000 字节是硬顶,把 ~8,000 当成预警线。商家编辑配置的后台界面,应该显示当前 JSON 的字节数,而不是只有一个保存按钮。一行 new TextEncoder().encode(json).length——注意数的是字节不是字符,规则名里的中文字符一个占 3 字节——就是“我们早知道”和“顾客告诉我们”之间的区别。

2. 失效要响,不要静默。 事先决定:配置 metafield 返回空时,Function 该怎么做?并且让“缺席”和“本来就是空的”可区分。可选做法:应用一个安全的默认折扣,同时由 app 后端发出告警;或者什么都不折扣,但通过 app 自己的监控发通知(比如一个每日任务,通过 Admin API 读取 metafield——在 Admin API 里它是完整返回的——检查大小)。最差的选择是默认行为:耸耸肩,返回空操作。

3. 在撞墙之前拆分。 如果规则真的需要超过 ~10 KB,按业务域拆开配置——VIP 档位一个 metafield,数量折扣一个,B2B 规则一个——放在你的保留 $app: 命名空间下,作为不同字段分别查询。三个 4 KB 的 metafield 都能正常返回;一个 12 KB 的 metafield 返回的是空。拆分时记得查询侧的上限:列表参数最多 100 个元素,输入查询的计算成本上限是 30。

4. 测试边界,不只测正常路径。 在开发店铺的测试套件里加一个故意超过 10,000 字节的夹具配置。你要锁死的行为是“我们会收到告警”,而不是“折扣悄悄消失”。

今天就检查一下自己有没有暴露

如果你有生产环境里的 Discount Function 在读 metafield 配置:

  1. 通过 Admin GraphQL API 取到配置 metafield,量一下 value 的字节长度。(Admin API 不管大小都会完整返回——10,000 字节的截断只发生在 Function 输入查询这一侧。)
  2. 如果在 8 KB 以下:加上大小显示和告警,并设个日历提醒,随规则数量增长定期复查。
  3. 如果已经超过:你的折扣可能已经是间歇性失效——metafield 是在有人保存了更大配置的那一刻才越线的。检查最近的订单里有没有缺失的折扣记录,然后拆分。

Agent 在这里的位置

上面这些都是一次性的设计修正。持续的风险是漂移:规则数量慢慢涨、有人往 metafield 里粘了一大段 JSON、新的促销类型让配置翻倍。这是一个监测循环,正是店铺运营 Agent 能扛下来的事:盯着配置大小、盯着带折扣订单的比例,在异常出现时带着证据提醒店主——赶在顾客发现之前。


本文按 Shopify Functions API 官方文档(输入查询限制、固定资源限制)核实,截至 2026 年 7 月。失效模式的现场报告来自 Shopify Community 一条关于 Function 可维护性的讨论——那位实践者给出的数字与官方文档完全一致。