I'm looking at AWS API Gateway websockets support, announced relatively recently -
https://aws.amazon.com/blogs/compute/announcing-websocket-apis-in-amazon-api-gateway/
They have a sample chat server example -
https://github.com/aws-samples/simple-websockets-chat-app/blob/master/sendmessage/app.js
which I have running, very nice.
If you send a message, the sendmessage Lambda broadcasts that message to all connected users via the following -
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB.DocumentClient({ apiVersion: '2012-08-10' });
const { TABLE_NAME } = process.env;
exports.handler = async (event, context) => {
let connectionData;
try {
connectionData = await ddb.scan({ TableName: TABLE_NAME, ProjectionExpression: 'connectionId' }).promise();
} catch (e) {
return { statusCode: 500, body: e.stack };
}
const apigwManagementApi = new AWS.ApiGatewayManagementApi({
apiVersion: '2018-11-29',
endpoint: event.requestContext.domainName + '/' + event.requestContext.stage
});
const postData = JSON.parse(event.body).data;
const postCalls = connectionData.Items.map(async ({ connectionId }) => {
try {
await apigwManagementApi.postToConnection({ ConnectionId: connectionId, Data: postData }).promise();
} catch (e) {
if (e.statusCode === 410) {
console.log(`Found stale connection, deleting ${connectionId}`);
await ddb.delete({ TableName: TABLE_NAME, Key: { connectionId } }).promise();
} else {
throw e;
}
}
});
try {
await Promise.all(postCalls);
} catch (e) {
return { statusCode: 500, body: e.stack };
}
return { statusCode: 200, body: 'Data sent.' };
};
Now unfortunately (fortunately ??) I have a Python/Erlang background, not Javascript/nodejs. So I can see what some of this is doing, namely iterating over connections in the DynamoDB table and sending the response to each. It also looks as if it's working asynchronously, via use of async and await keywords, which I guess are Promises. But I can't be sure this is acting asynchronously, which is what worries me .. if I have a single Lambda which iterates over a large number of connections and makes synchronous calls, that's not going to work.
So - specifically with respect to this portion of code -
const postCalls = connectionData.Items.map(async ({ connectionId }) => {
try {
await apigwManagementApi.postToConnection({ ConnectionId: connectionId, Data: postData }).promise();
} catch (e) {
if (e.statusCode === 410) {
console.log(`Found stale connection, deleting ${connectionId}`);
await ddb.delete({ TableName: TABLE_NAME, Key: { connectionId } }).promise();
} else {
throw e;
}
}
});
can I be sure that this is posting postData to all connections in a fully asynchronous manner ? Do I need to be worried about a single Lambda potentially pushing messages to thousands of clients ?