WebSockets шлюза API AWS: лямбда-функция выполняется случайным образом 2/3 раза - PullRequest
0 голосов
/ 27 мая 2019

Я создаю функцию живого чата для своего веб-сайта с помощью AWS API Gateway WebSockets, Lambda, DynamoDB, всего бездействия сервера.

Моя лямбда-функция в основном выглядит следующим образом:

let userHandler = async ({ event, body, uuid, connection, ip, requestID }) => {
  switch (body.action) {
    case "hello":
      // ...
    case "register":
      // ...
    case "ping":
      // ...
    case "list":
      // ...
    case "send":
      if (!body.msg) return JSONError();
      if (!utils.isUniqueRequest(uuid, requestID)) return JSONReply("duplicate");
      await utils.addMessageToConversation({
        from: uuid,
        to: "admin",
        msg: body.msg
      });
      await utils.markUUIDUnread(body.uuid, true);
      let sent = await utils.sendResponse({
        from: uuid,
        to: "admin",
        msg: body.msg,
        ws
      });
      return sent ? JSONReply("sent") : JSONReply("sendError");
  }
};

Я оставил только путь для отправки кода, потому что именно он вызывает у меня проблемы. Все в кодовой базе является модульным тестированием и работает должным образом в производстве - за исключением этой странной проблемы. Иногда, когда пользователь отправляет сообщение или я им отвечаю, получатель получает два или три эквивалентных ответа подряд. Я проверил всех клиентов, обращающихся к этой конечной точке WebSocket, и их сетевой трафик, и они определенно не отправляют два или три запроса случайно.

Изначально я не знал, что лямбда-функции должны были быть идемпотентными, поскольку они не гарантированно запускаются только один раз. Понимая это, я построил следующую функцию isUniqueRequest:

let isUniqueRequest = async (uuid, requestID) => {
  let Key = {
    uuid,
    timestamp: 0
  };
  let lastRequestsServed = await dynamo("get", {
    Key,
    ProjectionExpression: "lastRequestsServed"
  });
  if (!lastRequestsServed.Item) return true; // unknown uuid
  lastRequestsServed = lastRequestsServed.Item.lastRequestsServed.values;
  if (lastRequestsServed.includes(requestID)) return false;
  lastRequestsServed.push(requestID); // add new request
  if (lastRequestsServed.length >= 6) lastRequestsServed.shift(); // drop the oldest saved id if there are too many
  await dynamo("update", {
    Key,
    UpdateExpression: "SET lastRequestsServed = :newSet",
    ExpressionAttributeValues: {
      ":newSet": module.exports.DynamoDocumentClient.createSet(lastRequestsServed)
    }
  });
  return true;
};

Внутри каждого объекта диалога эта функция обращается к lastRequestsServed, установленному для проверки, был ли обработан идентификатор запроса текущего события. Он также добавляет идентификатор, если его там еще нет, и проверяет, что одновременно хранится только 5 идентификаторов, удаляя самый старый, если места нет. Эта функция также была проверена модулем / интеграцией и (в тестировании) выполняет то, что я написал.

Нет прямого способа вызвать эту проблему, которую я обнаружил, однако, в одном сеансе с несколькими десятками отправленных сообщений это, кажется, всегда происходит.

Так что теперь я в растерянности, почему это так. Я думаю, что это может быть связано с тем фактом, что новая лямбда запускается для ответа на входящие запросы, если текущий занят, и каким-то образом возникает состояние гонки, при котором обе лямбды получают доступ к элементу разговора одновременно, прежде чем любой из них можете вставить в него свой идентификатор запроса? Я действительно не знаю, что мне нужно изменить в своем запросе, чтобы этого не произошло.

Любые идеи будут оценены, спасибо!

...