You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

gqlgen中@delete指令导致返回结果不稳定的原因排查

问题描述

Schema 定义

type Account {
    id: Int
    nickname: String
}

directive @delete on FIELD

type Query {
    accounts: [Account]
}

@delete 指令的Go实现

func (d directive) Delete(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) {
    fieldContext := graphql.GetFieldContext(ctx)
    parentResult := fieldContext.Parent.Parent.Result.([]*Account)
    parentResult[0] = nil
    parentResult[1] = nil
    return next(ctx)
}

查询语句

query Accounts {
  accounts {
    nickname @delete
  }
}

异常现象

每次发送该请求返回结果都不一致:

  • 有时返回部分null:
{
  "data": {
    "accounts": [
      null,
      {
        "nickname": "Jane"
      },
      {
        "nickname": "Jack"
      }
    ]
  }
}
  • 有时仅第二个对象为null,有时无null,有时两个对象同时为null(这是预期结果)。

请问该现象的原因是什么?


原因分析

核心问题是GraphQL字段解析的并发特性加上对共享数据的不安全修改导致的竞态条件:

  1. 并发解析的竞态:GraphQL引擎处理列表类型字段时,会并行解析列表里每个Account对象的nickname字段。你的@delete指令会在每个nickname解析时,都去修改同一个parentResult数组(也就是accounts查询返回的列表)。多个goroutine同时修改同一个数组元素,执行顺序完全不确定,导致最终结果随机。

  2. 错误的修改逻辑:你在字段级指令里直接修改父级查询的返回结果,本身就违反了GraphQL的执行模型。父级字段的结果应该是只读的,字段指令的作用范围应该局限于当前字段的解析,而不是去篡改上层数据。

简单来说,就是多个指令实例在抢着改同一个数组,谁先执行完谁的修改生效,最终结果完全看并发调度的运气,所以每次返回都不一样。


内容的提问来源于stack exchange,提问作者Dan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 15:32:16