graphql-ruby icon indicating copy to clipboard operation
graphql-ruby copied to clipboard

GraphQL Pro Defer net::ERR_INCOMPLETE_CHUNKED_ENCODING

Open daniel-gato opened this issue 5 months ago • 2 comments

We are using Defer in production and we are having some strange behaviour. The request bellow seems to fail randomly (maybe 10-30% of the time).

It looks to us that some chunks aren't json valid. More precisely it's sending 2 chunks including http header in one stream.

The error we are facing in the browser is Failed to load resource: net::ERR_INCOMPLETE_CHUNKED_ENCODING.

It happens in GraphiQL client and Apollo integration (v3.13.8)

Controller implementation:

  def execute
    authenticate!

    result = batched_queries ? multiplex_query : execute_query

    if result.respond_to?(:context) && (deferred = result.context[:defer])
      response.headers['Last-Modified'] = Time.now.httpdate
      deferred.stream_http_multipart(response, incremental: true)
    elsif result.is_a?(Array)
      render json: result.map(&:to_h)
    else
      render json: result.to_h
    end
  rescue Exceptions::AuthError => e
    handle_auth_exceptions(e)
  rescue StandardError => e
    handle_exceptions(e)
  ensure
    response.stream.close unless response.stream.closed?
  end

Query

query GetInitial3DProductInfo($id: ID!) {
  node(id: $id) {
    ... on Product {
      id
      useViewerIntegration
      uuid
      previewUrl
      isConfigurator
      isDraftVersion
      publishedAt
      model {
        allConfigurations {
          totalCount
          nodes {
            id
            parent {
              id
              __typename
            }
            configurations {
              nodes {
                id
                __typename
              }
              __typename
            }
            variants {
              totalCount
              nodes {
                ... on Variant {
                  id
                  uuid
                  name
                  cameraPosition {
                    id
                    name
                    uuid
                    position
                    lookAt
                    __typename
                  }
                  configuration {
                    id
                    __typename
                  }
                  swatchColour
                  displayPosition
                  arAppleAsset {
                    ... on SharedAsset {
                      id
                      uuid
                      updatedAt
                      contentType
                      fileMeta
                      fileName
                      fingerprint
                      styles {
                        name
                        url
                        __typename
                      }
                      __typename
                    }
                    __typename
                  }
                  arAndroidAsset {
                    ... on SharedAsset {
                      id
                      uuid
                      updatedAt
                      contentType
                      fileMeta
                      fileName
                      fingerprint
                      styles {
                        name
                        url
                        __typename
                      }
                      __typename
                    }
                    __typename
                  }
                  hasChainAnnotations
                  status
                  previews {
                    nodes {
                      id
                      asset {
                        id
                        styles {
                          url
                          name
                          __typename
                        }
                        __typename
                      }
                      __typename
                    }
                    __typename
                  }
                  shadowAsset {
                    id
                    fileName
                    styles {
                      url
                      name
                      __typename
                    }
                    __typename
                  }
                  shadowMesh {
                    id
                    name
                    sourceAsset {
                      fileName
                      styles {
                        url
                        name
                        __typename
                      }
                      __typename
                    }
                    gltfAsset {
                      id
                      jsonContent
                      __typename
                    }
                    __typename
                  }
                  metadata
                  annotations @defer {
                    totalCount
                    nodes {
                      id
                      uuid
                      buttonText
                      buttonUrl
                      cameraLookAt
                      cameraPosition
                      content
                      markerPosition
                      title
                      __typename
                    }
                    __typename
                  }
                  textnodes @defer {
                    nodes {
                      id
                      alignment
                      cameraLookAt
                      cameraPosition
                      content
                      contentPosition
                      contentRotation
                      size
                      __typename
                    }
                    __typename
                  }
                  meshReplacements @defer {
                    totalCount
                    nodes {
                      id
                      uuid
                      replacement {
                        id
                        uuid
                        name
                        __typename
                      }
                      source {
                        id
                        uuid
                        name
                        __typename
                      }
                      __typename
                    }
                    __typename
                  }
                  materialReplacements @defer {
                    totalCount
                    nodes {
                      id
                      uuid
                      replacement {
                        id
                        uuid
                        name
                        __typename
                      }
                      source {
                        id
                        uuid
                        name
                        __typename
                      }
                      __typename
                    }
                    __typename
                  }
                  allMeshAssignments @defer   {
                    totalCount
                    nodes {
                      id
                      uuid
                      name
                      translation
                      scale
                      rotation
                      material {
                        id
                        uuid
                        __typename
                      }
                      mesh {
                        id
                        uuid
                        __typename
                      }
                      group {
                        id
                        uuid
                        __typename
                      }
                      __typename
                    }
                    __typename
                  }
                  allMeshAssignmentGroups @defer {
                    totalCount
                    nodes {
                      id
                      uuid
                      name
                      translation
                      scale
                      rotation
                      parent {
                        id
                        uuid
                        __typename
                      }
                      __typename
                    }
                    __typename
                  }
                  __typename
                }
                __typename
              }
              __typename
            }
            __typename
          }
          __typename
        }
        __typename
      }
      __typename
    }
    __typename
  }
}

Response on GraphiQL:

{
  "errors": [
    {
      "message": "Expected multipart chunks to be of json type. got:\nHeaders::\n[object Object]\n\nBody::\n{\"incremental\":[{\"path\":[\"node\",\"model\",\"allConfigurations\",\"nodes\",22,\"variants\",\"nodes\",2,\"allMeshAssignmentGroups\"],\"data\":{\"totalCount\":3,\"nodes\":[{\"id\":\"R3JvdXAtMjE1YWMzZjAtMDkwOS0wMTNlLTQyNDAtNjZmNTcyN2U2YzM0\",\"uuid\":\"215ac3f0-0909-013e-4240-66f5727e6c34\",\"name\":\"0b867e53c1d233ce9fe49d54549a232320240830-4-78nb1y\",\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":{\"x\":1,\"y\":1,\"z\":1},\"rotation\":{\"x\":0,\"y\":0,\"z\":0,\"w\":1},\"parent\":null,\"__typename\":\"Group\"},{\"id\":\"R3JvdXAtMjE1YjA5ODAtMDkwOS0wMTNlLTllOWMtNjZmNTcyN2U2YzM0\",\"uuid\":\"215b0980-0909-013e-9e9c-66f5727e6c34\",\"name\":\"0b867e53c1d233ce9fe49d54549a232320240830-4-kvds5x\",\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":{\"x\":1,\"y\":1,\"z\":1},\"rotation\":{\"x\":0,\"y\":0,\"z\":0,\"w\":1},\"parent\":null,\"__typename\":\"Group\"},{\"id\":\"R3JvdXAtMjE1YjUwYzAtMDkwOS0wMTNlLTBiYTctNjZmNTcyN2U2YzM0\",\"uuid\":\"215b50c0-0909-013e-0ba7-66f5727e6c34\",\"name\":\"0b867e53c1d233ce9fe49d54549a232320240830-4-xv6dyj\",\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":{\"x\":1,\"y\":1,\"z\":1},\"rotation\":{\"x\":0,\"y\":0,\"z\":0,\"w\":1},\"parent\":null,\"__typename\":\"Group\"}],\"__typename\":\"GroupConnection\"}}],\"hasNext\":true}\r\n---\r\nContent-Type: application/json\r\nContent-Length: 195\r\n\r\n{\"incremental\":[{\"path\":[\"node\",\"model\",\"allConfigurations\",\"nodes\",22,\"variants\",\"nodes\",3,\"annotations\"],\"data\":{\"totalCount\":0,\"nodes\":[],\"__typename\":\"AnnotationConnection\"}}],\"hasNext\":true},Headers::\n[object Object]\n\nBody::\n[object Object],Headers::\n[object Object]\n\nBody::\n[object Object],Headers::\n[object Object]\n\nBody::\n[object Object]",
      "stack": "@https://api.sayduck.io/assets/graphiql/rails/application-81c86557b6f6453812b8a74649ca5c83bfc9ba6001c20ecff68dff7135286876.js:2:607692\no@https://api.sayduck.io/assets/graphiql/rails/application-81c86557b6f6453812b8a74649ca5c83bfc9ba6001c20ecff68dff7135286876.js:2:607903"
    }
  ]
}

Manually pretty printed string:

Expected multipart chunks to be of json type. got:

Headers: :
[object Object]

Body: :
{
  "incremental":[
    {
      "path": [
        "node",
        "model",
        "allConfigurations",
        "nodes",
        22,
        "variants",
        "nodes",
        2,
        "allMeshAssignmentGroups"
      ],
      "data": {
        "totalCount":3,
        "nodes": [
          {
            "id": "R3JvdXAtMjE1YWMzZjAtMDkwOS0wMTNlLTQyNDAtNjZmNTcyN2U2YzM0",
            "uuid": "215ac3f0-0909-013e-4240-66f5727e6c34",
            "name": "0b867e53c1d233ce9fe49d54549a232320240830-4-78nb1y",
            "translation": {
              "x": 0,
              "y": 0,
              "z": 0
            },
            "scale": {
              "x": 1,
              "y": 1,
              "z": 1
            },
            "rotation": {
              "x": 0,
              "y": 0,
              "z": 0,
              "w": 1
            },
            "parent": null,
            "__typename": "Group"
          },
          {
            "id": "R3JvdXAtMjE1YjA5ODAtMDkwOS0wMTNlLTllOWMtNjZmNTcyN2U2YzM0",
            "uuid": "215b0980-0909-013e-9e9c-66f5727e6c34",
            "name": "0b867e53c1d233ce9fe49d54549a232320240830-4-kvds5x",
            "translation": {
              "x": 0,
              "y": 0,
              "z": 0
            },
            "scale": {
              "x": 1,
              "y": 1,
              "z": 1
            },
            "rotation": {
              "x": 0,
              "y": 0,
              "z": 0,
              "w": 1
            },
            "parent": null,
            "__typename": "Group"
          },
          {
            "id": "R3JvdXAtMjE1YjUwYzAtMDkwOS0wMTNlLTBiYTctNjZmNTcyN2U2YzM0",
            "uuid": "215b50c0-0909-013e-0ba7-66f5727e6c34",
            "name": "0b867e53c1d233ce9fe49d54549a232320240830-4-xv6dyj",
            "translation": {
              "x": 0,
              "y": 0,
              "z": 0
            },
            "scale": {
              "x": 1,
              "y": 1,
              "z": 1
            },
            "rotation": {
              "x": 0,
              "y": 0,
              "z": 0,
              "w": 1
            },
            "parent": null,
            "__typename": "Group"
          }
        ],
        "__typename": "GroupConnection"
      },
    }
  ],
  "hasNext": true
}

\r\n---\r\nContent-Type: application/json\r\nContent-Length: 195\r\n\r

{
  "incremental": [
    {
      "path": [
        "node",
        "model",
        "allConfigurations",
        "nodes",
        22,
        "variants",
        "nodes",
        3,
        "annotations"
      ],
      "data": {
        "totalCount": 0,
        "nodes": [],
        "__typename": "AnnotationConnection"
      }
    }
  ],
  "hasNext": true
},

Headers: :
[object Object]
Body: :
[object Object],

Headers: :
[object Object]
Body: :
[object Object],

Headers: :
[object Object]
Body: :
[object Object]

daniel-gato avatar Jun 07 '25 11:06 daniel-gato