3

I have a chat application that when I click a button after typing in a message, the message gets posted to my DynamoDB table. The application is suppose to post the message once, but somehow is posting it twice.

The pipeline is as follows:

Client clicks send button --> hits my Google Cloud Endpoint API --> triggers my Google Cloud function --> invokes my AWS lambda function --> POSTS message DynamoDB.

With the help of SO, I've isolated the problem to my Google Cloud function invoking Lambda asynchronously which is queuing the Lambda twice.

With async, requests are queued before actually executing. So, if you call it once, AWS will check if there's one executing already, if not, it will add another one.

enter image description here (AWS blog)

Ideally I'd like to invoke my Lambda synchronously but according to this Github post, I'd get billed twice (?). He mentions increasing the timeout of my function, but it's already set at 60 seconds - plenty of time for my lambda to send a response back. Is there some other mechanism that's queuing my lambda twice?

For your reference my Google cloud function is as follows:

let AWS = require('aws-sdk');

AWS.config.update({
  accessKeyId: ''
  secretAccessKey: ''
  region: ''
})

let lambda = new AWS.Lambda();

exports.helloWorld = async (req,res) =>{
  let payload = {
    pathParameters: req.query.p,
    httpMethod: req.method,
    body: req.method == "POST" ? req.body.message || req.body.user : null,
    cognitoUsername: req.query.u
  }

  let params = {
    FunctionName: '',
    InvocationType: 'RequestResponse',
    Payload: JSON.stringify(payload)
  }

  res.status(200).send( await lambda.invoke(params, function(err,data){
    if (err){throw err}
    else {return data.Payload}
  }).promise())
}

Solution:

Based on @jarmod's solution, my Cloud function is shown below. The relevant part is at the end.

let AWS = require('aws-sdk');

AWS.config.update({
  accessKeyId: ''
  secretAccessKey: ''
  region: ''
})

let lambda = new AWS.Lambda();

exports.helloWorld = async (req,res) =>{
  let payload = {
    pathParameters: req.query.p,
    httpMethod: req.method,
    body: req.method == "POST" ? req.body.message || req.body.user : null,
    cognitoUsername: req.query.u
  }

  let params = {
    FunctionName: '',
    InvocationType: 'RequestResponse',
    Payload: JSON.stringify(payload)
  }

  // code changed only here
  res.status(200).send( await lambda.invoke(params).promise())
}

Edit:

@Ngenator brought to my attention that my Google Cloud function may be getting triggered twice. For reference, this is my API yaml configuration:

swagger: '2.0'
info:
  title: Cloud Endpoints + GCF
  version: 1.0.0
host: service.run.app
x-google-endpoints:
  - name: "service.run.app"
    allowCors: "true"
schemes:
  - https
produces:
  - application/json
paths:
  /function-2:
    get:
      operationId: get 
      parameters:
        - name: p
          in: query
          required: true
          type: string
        - name: u
          in: query
          required: false
          type: string
      x-google-backend:
        address: https://to.my/function-2
      responses:
        '200':
          description: A successful response
          schema:
            type: string
    post:
      operationId: post 
      consumes:
        - application/json
      parameters:
        - name: p
          in: query
          required: true
          type: string
        - name: u
          in: query
          required: false
          type: string
        - in: body
          name: body
          schema:
            type: object
            properties:
              message:
                type: string
              user:
                type: array
                items:
                  type: string
      x-google-backend:
        address: https://to.my/function-2
      responses:
        '200':
          description: A successful response
          schema:
            type: string
11
  • 1
    You may be mis-reading the GitHub issue. The point they're making is that if Lambda #1 invokes Lambda #2 synchronously then (obviously) Lambda #1 must wait around doing nothing while waiting for the (synchronous) response from Lambda #2. Hence they overlap in time, and in that sense you are 'paying twice'. Commented Jan 6, 2020 at 23:29
  • 1
    Look at the CloudWatch Logs for your Lambda function. Does it show that the Lambda completed normally, or that it failed (or timed out)? If you look at the logs for the two invocations that you think are duplicates, do they have the same request ID, or different? Commented Jan 6, 2020 at 23:31
  • 1
    Are you sure your google cloud function isn't actually triggering twice? Perhaps due to CORS and an OPTIONS request being handled as a post request? Commented Jan 6, 2020 at 23:32
  • @jarmod, went into my logs and indeed they're completing normally with two different request IDs. Commented Jan 6, 2020 at 23:38
  • 1
    Your call to lambda.invoke is incorrect. It includes both a callback and awaits a promise. Use one or the other, preferably the latter: await lambda.invoke(params).promise() Commented Jan 7, 2020 at 0:23

1 Answer 1

3

Your call to lambda.invoke is incorrect. It includes both a callback and awaits a promise. You should use one or the other, preferably the latter:

const rc = await lambda.invoke(params).promise();
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.