Websocket on invoked lambda ignore message and send the last message received instead

0

When I invoke a lambda that send data to client through the websocket API, it doesn't send the actual message/payload but the last received.

Example:

  • Lambda 1 invoke Lambda 2 with payload "test1" > Lambda 2 send nothing to client
  • Lambda 1 invoke Lambda 2 with second payload "test2" > Lambda send 2 send "test1" to client
  • Lambda 1 invoke Lambda 2 with third payload "test3" > Lambda send 2 send "test2" to client

Enter image description here

My environnement:

  • Nodejs 16
  • lib websocket: AWS.ApiGatewayManagementApi.postToConnection

There is 0 cache on Websocket API. There is no Error, just weird behavior

3 Answers
0
Accepted Answer

I found the answer, change forEach loop to a for loop.

from:

items?.forEach(async (item) => {
  await apigwManagementApi.postToConnection(params).promise()
});

to:

for (const item of items) {
  await apigwManagementApi.postToConnection(params).promise()
}

I can't explain it, but it works, message are sent instantly to websocket.

answered a year ago
0

The issue is the way Node.js works. You are calling postToConnection, but what actually happens is that an event is added to the Node.js event loop. It is not performed immediately. You get to the end of the function and the run-time is frozen. At the point we do not handle the rest of the events until you invoke the function again. We then unfreeze the run-time and handle the next event.

To fix it, you should use await when calling postToConnection.

profile pictureAWS
EXPERT
Uri
answered a year ago
0

Hello @Uri, thank you for answering.

Unfortunately, this is already the case, I am already using async/await :

const AWS = require("aws-sdk");
const apigwManagementApi = new AWS.ApiGatewayManagementApi({
  endpoint: process.env.WEBSOCKET_DOMAIN_NAME
});
const dynamo = new AWS.DynamoDB.DocumentClient({ region: process.env.AWS_REGION });

/**
 * Send Websocket Message
 */
const websocketSendMessage = async (connectionId, data) => {
  const params = {
    ConnectionId: connectionId,
    Data: JSON.stringify(data)
  };
  await apigwManagementApi.postToConnection(params).promise();
  return;
};

/**
 * Handler
 */
const handler = async (event) => {
  const { userId, data } = event;

  // Retrieve all connectionId of user from DynamoDB
  let items;
  const dynamoItem = await dynamo.scan({
    TableName: process.env.DYNAMO_TABLE_NAME,
    FilterExpression: "userId = :userId",
    ExpressionAttributeValues: { ":userId": userId }
  }).promise();
  items = dynamoItem.Items;

  // Send websocket message for each websocket client of user
  items?.forEach(async (item) => {
    await websocketSendMessage(item.connectionId, data);
  });

  return { statusCode: 200 };
};

answered a year ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions