Dgraph lambda failed to mutate/query data

To facilitate better answering of questions, if you have a question, please fill in the following info. Otherwise, please delete the template.

What I want to do

Mutate and query data via dgraph lambda

Follow the guidance [charts/charts/dgraph-lambda at master · dgraph-io/charts · GitHub ] (charts/charts/dgraph-lambda at master · dgraph-io/charts · GitHub ) and solution https://discuss.dgraph.io/t/helm-install-dgraph-lambda-failed-to-pull-image-dgraph-dgraph-lambda-v1-2-4/17755 to deploy Dgraph and Lambda, I just found another issue. After finishing the last step of uploading schema, pod my-release-dgraph-alpha-0 crashed with an error message printed in logs of the pods:

dgraph alpha --my=my-release-dgraph-alpha-0.my-release-dgraph-alpha-headless.default.svc.cluster.local:7080 --zero my-release-dgraph-zero-0.my-release-dgraph-zero-headless.default.svc.cluster.local:5080
F0912 16:16:04.948493 1 flags.go:139] superflag: found invalid options in flag: lambda-url=http://my-lambda-dgraph-lambda.default.svc:80/graphql-worker.
valid options: introspection=true; debug=false; extensions=true; poll-interval=1s;

I delete dgraph/lambda and dgraph/dgraph, modified the settings when installing dgraph and redid the following steps.

  1. deploy dgraph
helm install my-release dgraph/dgraph \
  --namespace default \
  --set "alpha.extraEnvs[0].name"=lambda \
  --set "alpha.extraEnvs[0].value"=url=http://my-lambda-dgraph-lambda.default.svc:80/graphql-worker
  1. deploy lambda

deploy/dev/dgraph/lambda.yaml is the copy from the code on Github tutorial.

helm install my-lambda dgraph/dgraph-lambda \
  --set image.tag=1.2.4 \
  --namespace default \
  --values deploy/dev/dgraph/lambda.yaml \
  --set "env[0].name"=DGRAPH_URL \
  --set "env[0].value"=http://my-release-dgraph-alpha-headless.default.svc:8080
  1. forward dgraph port
kubectl port-forward --namespace default my-release-dgraph-alpha-0 8080:8080
  1. upload schema
    deploy/dev/dgraph/schema.graphql is the copy from the code on Github tutorial.
curl http://localhost:8080/admin/schema --upload-file deploy/dev/dgraph/schema.graphql

Now everything goes well, run:

kubectl get pods

it lists:

NAME READY STATUS RESTARTS AGE
my-lambda-dgraph-lambda-5969cf5cbb-ptssv 1/1 Running 0 2m56s
my-release-dgraph-alpha-0 1/1 Running 0 5m4s
my-release-dgraph-alpha-1 1/1 Running 0 4m45s
my-release-dgraph-alpha-2 1/1 Running 0 4m25s
my-release-dgraph-zero-0 1/1 Running 0 5m4s
my-release-dgraph-zero-1 1/1 Running 0 4m45s
my-release-dgraph-zero-2 1/1 Running 0 4m32s

  1. Test
    Add Author GraphQL body:
mutation MyMutation {
  addAuthor(input: {name: "AnnZhang"}) {
    numUids
  }
}

it suceeded. However, I tried the lambda resolver mutation like this, it failed.

mutation MyMutation {
  newAuthor(name: "AnnZ")
}

error message:
“errors”: [
{
“message”: “Evaluation of custom field failed because external request returned an error: unexpected error with: 400 for field: newAuthor within type: Mutation.”,
“locations”: [
{
“line”: 2,
“column”: 3
}
]
}
],

  1. list all pods

NAME READY STATUS RESTARTS AGE
my-lambda-dgraph-lambda-5969cf5cbb-fpk8s 1/1 Running 0 11s
my-release-dgraph-alpha-0 1/1 Running 0 29m
my-release-dgraph-alpha-1 1/1 Running 0 29m
my-release-dgraph-alpha-2 1/1 Running 0 28m
my-release-dgraph-zero-0 1/1 Running 0 29m
my-release-dgraph-zero-1 1/1 Running 0 29m
my-release-dgraph-zero-2 1/1 Running 0 29m

  1. get logs of pod my-lambda-dgraph-lambda-5969cf5cbb-fpk8s

[email protected] start /app
pm2-runtime start ecosystem.config.js
2022-09-12T16:54:00: PM2 log: Launching in no daemon mode
2022-09-12T16:54:00: PM2 log: [Watch] Start watching dgraph-lambda
2022-09-12T16:54:00: PM2 log: App [dgraph-lambda:0] starting in -cluster mode-
2022-09-12T16:54:00: PM2 log: App [dgraph-lambda:0] online
2022-09-12T16:54:00: PM2 log: [Watch] Start watching dgraph-lambda
2022-09-12T16:54:00: PM2 log: App [dgraph-lambda:1] starting in -cluster mode-
2022-09-12T16:54:00: PM2 log: App [dgraph-lambda:1] online
2022-09-12T16:54:00: PM2 log: [Watch] Start watching dgraph-lambda
2022-09-12T16:54:00: PM2 log: App [dgraph-lambda:2] starting in -cluster mode-
2022-09-12T16:54:00: PM2 log: App [dgraph-lambda:2] online
2022-09-12T16:54:00: PM2 log: [Watch] Start watching dgraph-lambda
2022-09-12T16:54:00: PM2 log: App [dgraph-lambda:3] starting in -cluster mode-
2022-09-12T16:54:01: PM2 log: App [dgraph-lambda:3] online
Server Listening on port 8686!
Server Listening on port 8686!
Server Listening on port 8686!
Server Listening on port 8686!

I tried the same script on Dgraph cloud, the lambda resolver worked well. I have no idea why it doesn’t work from this approach.

OK, that’s not very helpful is it?

Can you share the JS code for the lambda and the full mutation query you’re making against it?

The JS code:

script:
  enabled: true
  script: |
    async function authorsByName({args, dql}) {
        const results = await dql.query(`query queryAuthor($name: string) {
            queryAuthor(func: type(Author)) @filter(eq(Author.name, $name)) {
                name: Author.name
                dob: Author.dob
                reputation: Author.reputation
            }
        }`, {"$name": args.name})
        return results.data.queryAuthor
    }

    async function newAuthor({args, graphql}) {
        // lets give every new author a reputation of 3 by default
        const results = await graphql(`mutation ($name: String!) {
            addAuthor(input: [{name: $name, reputation: 3.0 }]) {
                author {
                    id
                    reputation
                }
            }
        }`, {"name": args.name})
        return results.data.addAuthor.author[0].id
    }

    self.addGraphQLResolvers({
        "Query.authorsByName": authorsByName,
        "Mutation.newAuthor": newAuthor
    })

full mutation query: screenshot

@ matthewmcneely
On cloud, the script goes like this, the content is untouched.

async function authorsByName({args, dql}) {
    const results = await dql.query(`query queryAuthor($name: string) {
        queryAuthor(func: type(Author)) @filter(eq(Author.name, $name)) {
            name: Author.name
            dob: Author.dob
            reputation: Author.reputation
        }
    }`, {"$name": args.name})
    return results.data.queryAuthor
}

async function newAuthor({args, graphql}) {
    // lets give every new author a reputation of 3 by default
    const results = await graphql(`mutation ($name: String!) {
        addAuthor(input: [{name: $name, reputation: 3.0 }]) {
            author {
                id
                reputation
            }
        }
    }`, {"name": args.name})
    return results.data.addAuthor.author[0].id
}

self.addGraphQLResolvers({
    "Query.authorsByName": authorsByName,
    "Mutation.newAuthor": newAuthor
})

Mutation succeeded:

I would put the first one in custom dql queries instead of lambdas. Much easier to manage, plus you can add subscription support. Unfortunately, mutations need lambdas.

https://dgraph.io/docs/graphql/custom/dql/

J

Thanks jdgamble555. Unfortunately I have to use mutation. It seems that Alpha-0 doesn’t recognize the existence of lambda at all. Each time I sent the mutate request, alpha-0 printed a log Got GraphQL request over HTTP which proves that alpha-0 received it correctly, but nothing new came up in lambda container log. I tried docker compose to start dgraph alpha, zero, and lambda, it worked.
docker-compose.yml

version: "3.2"
services:
  zero:
    image: dgraph/dgraph:v21.12.0
    volumes:
      - ${PWD}/dgraph/data:/dgraph
    ports:
      - 5080:5080
      - 6080:6080
    restart: on-failure
    command: dgraph zero --my=zero:5080 --telemetry sentry=false --telemetry reports=false
  alpha:
    image: dgraph/dgraph:v21.12.0
    volumes:
      - ${PWD}/dgraph/data:/dgraph
    ports:
      - 8080:8080
      - 9080:9080
    restart: on-failure
    command: dgraph alpha --my=alpha:7080 --zero=zero:5080 --security whitelist=0.0.0.0/0 --telemetry sentry=false --telemetry reports=false --lambda url=http://dgraph_lambda:8686/graphql-worker

  dgraph_lambda:
    image: dgraph/dgraph-lambda:latest
    depends_on:
      - zero
      - alpha
    ports:
      - "8686:8686"
    environment:
      DGRAPH_URL: http://alpha:8080
    volumes:
      - ${PWD}/dgraph/gql/script.js:/app/script/script.js:ro

Right, I just finished trying in a local docker-compose setup and it worked. FYI, I’d recommend testing against v21.03.2 (that’s what’s “officially” supported and what the cloud runs)

Can you send me the Author type as is in your SDL?

The schema:

type Author {
    id: ID!
    name: String! @search(by: [hash, trigram])
    dob: DateTime
    reputation: Float
}

type Query {
    authorsByName(name: String!): [Author] @lambda
}

type Mutation {
    newAuthor(name: String!): ID! @lambda
}

someone posted a link https://discuss.dgraph.io/t/zion-v21-12-documentation-and-setting-up-a-lambda-server/16396/2 which suggested using /admin api to update lambda script. Is it still working on self-host dgraph like this case? if yes, can you please provide a sample as a guidance of correctly using that api to update the lambda script ? Thanks.

That (the updatelamdbascript) was a 20.12 feature, not up on cloud. I’ll try to replicate the issue in a cloud instance myself.

I just downgraded dgraph image version to v21.03.2 from v21.12.0 to replicate all the steps above, the error code for the same customized mutation turned to 500 from 400:

"errors": [
    {
        "message": "Evaluation of custom field failed because external request returned an error: unexpected error with: 500 for field: newAuthor within type: Mutation.",
        "locations": [
            {
                "line": 2,
                "column": 3
            }
        ]
    }
],

lambda container error message:

TypeError: Only absolute URLs are supported
at getNodeRequestOptions (/app/node_modules/node-fetch/lib/index.js:1305:9)
at /app/node_modules/node-fetch/lib/index.js:1410:19
at new Promise ()
at Function.fetch [as default] (/app/node_modules/node-fetch/lib/index.js:1407:9)
at Object.graphql (/app/src/dgraph.ts:9:31)
at graphql (/app/src/evaluate-script.ts:87:89)
at newAuthor (evalmachine.:14:27)
at /app/src/evaluate-script.ts:31:59
at Array.map ()
at GraphQLResolverEventTarget. (/app/src/evaluate-script.ts:31:45)

It’s working for me in the cloud, the schema

type Author {
  id: ID!
  name: String
  reputation: Float
}

type Mutation {
    newAuthor(name: String!): ID! @lambda
}

The script:

async function newAuthor({args, graphql}) {
    // lets give every new author a reputation of 3 by default
    const results = await graphql(`mutation ($name: String!) {
        addAuthor(input: [{name: $name, reputation: 3.0 }]) {
            author {
                id
                reputation
            }
        }
    }`, {"name": args.name})
    console.log('results--------------\n', results)
    return results.data.addAuthor.author[0].id
}

self.addGraphQLResolvers({
    "Mutation.newAuthor": newAuthor
})

The mutation:

mutation {
	newAuthor(name: "Foo")
}

Query results

{
  "data": {
    "queryAuthor": [
      {
        "id": "0xfffd8d6ab27a9454",
        "name": "Foo",
        "reputation": 3
      }
    ]
  }
<snip>
}

It works for me as well on Cloud. It is frustrating that it doesn’t work on self-hosted cluster.

Oh, wait… I thought you were having an issue on a cloud instance.

I’ll try to recreate it locally.

matthewmcneely Cloud works fine. Local deploy using docker-compose with Dgraph v21.12.0 works as well. The core issue is deploying to AWS via helm install. It seems that Lambda Javascript in yaml file was never uploaded successfully. For dgraph v21.12.0 there wasn’t even an error in lambda container log but a response code 400, for v21.03.2 the error is TypeError: Only absolute URLs are supported…with response code 500.

I figured out how to resolve it. Thanks to https://discuss.dgraph.io/t/how-to-setup-lambda-server/16585/4?u=ann_zhang

The javascript in my-lambda.yaml of https://github.com/dgraph-io/charts/tree/master/charts/dgraph-lambda was never uploaded correctly though, hope it can be fixed.

Switch to dgraph v21.12.0. After deploying Dgraph and Lambda resolver, use the following request to update to Lambda Script.
URL: http://localhost:8080/admin
request method: post
GraphQL body:

mutation{
	updateLambdaScript(input:{set:{script:
    """async function authorsByName({args, dql}) {
            const results = await dql.query(`query queryAuthor($name: string) {
                queryAuthor(func: type(Author)) @filter(eq(Author.name, $name)) {
                    name: Author.name
                    dob: Author.dob
                    reputation: Author.reputation
                }
            }`, {"$name": args.name})
            return results.data.queryAuthor
        }

        async function newAuthor({args, graphql}) {
            // lets give every new author a reputation of 3 by default
            const results = await graphql(`mutation ($name: String!) {
                addAuthor(input: [{name: $name, reputation: 3.0 }]) {
                    author {
                        id
                        reputation
                    }
                }
            }`, {"name": args.name})
            return results.data.addAuthor.author[0].id
        }

        self.addGraphQLResolvers({
            "Query.authorsByName": authorsByName,
            "Mutation.newAuthor": newAuthor
        })
    """
	
	}}){
		lambdaScript{script}
	}
}

Response:

{
    "data": {
        "updateLambdaScript": {
            "lambdaScript": {
                "script": "async function authorsByName({args, dql}) {\n    const results = await dql.query(`query queryAuthor($name: string) {\n        queryAuthor(func: type(Author)) @filter(eq(Author.name, $name)) {\n            name: Author.name\n            dob: Author.dob\n            reputation: Author.reputation\n        }\n    }`, {\"$name\": args.name})\n    return results.data.queryAuthor\n}\n\nasync function newAuthor({args, graphql}) {\n    // lets give every new author a reputation of 3 by default\n    const results = await graphql(`mutation ($name: String!) {\n        addAuthor(input: [{name: $name, reputation: 3.0 }]) {\n            author {\n                id\n                reputation\n            }\n        }\n    }`, {\"name\": args.name})\n    return results.data.addAuthor.author[0].id\n}\n\nself.addGraphQLResolvers({\n    \"Query.authorsByName\": authorsByName,\n    \"Mutation.newAuthor\": newAuthor\n})"
            }
        }
    },
    "extensions": {
        "tracing": {
            "version": 1,
            "startTime": "2022-09-12T22:00:53.209702215Z",
            "endTime": "2022-09-12T22:00:53.212745638Z",
            "duration": 3043444
        }
    }
}

Test mutation:

mutation MyMutation {
  newAuthor(name: "AnnZ")
}

Response:

{
    "data": {
        "newAuthor": "0x3"
    },
    "extensions": {
        "tracing": {
            "version": 1,
            "startTime": "2022-09-12T22:01:04.270299555Z",
            "endTime": "2022-09-12T22:01:04.296470949Z",
            "duration": 26171399
        }
    }
}