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.
(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
lambda.invokeis incorrect. It includes both a callback and awaits a promise. Use one or the other, preferably the latter:await lambda.invoke(params).promise()