Последовательная проблема AWS Lambda с использованием DynamoDB - PullRequest
0 голосов
/ 26 февраля 2019

Я пытаюсь реализовать функцию, которая вызывается API Gateway.Он получает адрес электронной почты + пароль, а затем проверяет, используется ли адрес электронной почты уже.Если это не тот случай, его следует поместить в мою таблицу динамо-базы данных.

При тестировании с уже используемым адресом электронной почты операция put по-прежнему выполняется, даже если для логического значения должно быть установлено значение true.

'use strict';

var AWS = require('aws-sdk'),
  uuid = require('uuid'),
  documentClient = new AWS.DynamoDB.DocumentClient();

exports.handler = function(event, context, callback) {

  if (event.body !== null && event.body !== undefined) {

    let body = JSON.parse(event.body);
    let eMailAddress = body.mail;
    let password = body.password;
    var EmailInUse = Boolean(false);

    var paramsScan = {
      TableName: "accounts"
    };
    documentClient.scan(paramsScan, function(err, data) {
      for (var i in data.Items) {
        i = data.Items;
        if (i.EmailAddress == eMailAddress) {
          console.log("already used");
          callback(err, "Email Address already in Use!");
          EmailInUse = true;
        }
      }

    });

    console.log(EmailInUse);
    if (EmailInUse == false) {
      console.log("should not enter if email used");
      var params = {
        Item: {
          "AccountID": uuid.v1(),
          "Password": password,
          "EmailAddress": eMailAddress
        },
        TableName: "accounts"
      };

      documentClient.put(params, function(err, data) {
        if (err) {
          callback(err, null);
        } else {
          const response = {
            statusCode: "200",
            "headers": {},
            body: JSON.stringify(params),
            "isBase64Encoded": "false"
          };
          callback(null, response);
        }
      });

    }
  }
};

это мой журнал Cloudwatch для вызова 2 раза с теми же параметрами:

12:54:01
START RequestId: 281b0eda-950b-40fc-a2e2-d326cd04f8a4 Version: $LATEST
12:54:01
2019-02-26T12:54:01.434Z 281b0eda-950b-40fc-a2e2-d326cd04f8a4 false
12:54:01
2019-02-26T12:54:01.471Z 281b0eda-950b-40fc-a2e2-d326cd04f8a4 should not enter if email used
12:54:01
END RequestId: 281b0eda-950b-40fc-a2e2-d326cd04f8a4
12:54:01
REPORT RequestId: 281b0eda-950b-40fc-a2e2-d326cd04f8a4 Duration: 320.98 ms Billed Duration: 400 ms Memory Size: 128 MB Max Memory Used: 31 MB
12:54:47
START RequestId: b9df94ce-0d59-4dfb-8b61-8098db566431 Version: $LATEST
12:54:47
2019-02-26T12:54:47.591Z b9df94ce-0d59-4dfb-8b61-8098db566431 false
12:54:47
2019-02-26T12:54:47.591Z b9df94ce-0d59-4dfb-8b61-8098db566431 should not enter if email used
12:54:47
2019-02-26T12:54:47.812Z b9df94ce-0d59-4dfb-8b61-8098db566431 already used
12:54:47
END RequestId: b9df94ce-0d59-4dfb-8b61-8098db566431
12:54:47
REPORT RequestId: b9df94ce-0d59-4dfb-8b61-8098db566431 Duration: 311.87 ms Billed Duration: 400 ms Memory Size: 128 MB Max Memory Used: 31 MB

Глядя на это, я замечаю, что последний вывод журнала «уже использовался» вызывается после проверки, если адрес электронной почты уже используется.Может кто-нибудь сказать мне, как решить эту проблему?Большое спасибо заранее.

Ответы [ 3 ]

0 голосов
/ 26 февраля 2019

Проблема просто в синхронизации.

Функция documentClient.scan использует обратный вызов в вашем случае.Это означает, что перед выполнением обратного вызова вызывается следующий код (console.log(EmailInUse); и т. Д.).

Вы можете поместить все в обратный вызов или использовать async/await, поскольку AWS Lambda поддерживает Node.js 8.10:

var AWS = require('aws-sdk'),
  uuid = require('uuid'),
  documentClient = new AWS.DynamoDB.DocumentClient();

exports.handler = async event => {

  if (!event.body) return httpResponse(400, 'body is missing!');

  try {
    let body = JSON.parse(event.body);
    let eMailAddress = body.mail;
    let password = body.password;
    var EmailInUse = Boolean(false);

    var paramsScan = {
      TableName: "accounts"
    };
    const data = await documentClient.scan(paramsScan).promise();
    for (var i in data.Items) {
        i = data.Items;
        if (i.EmailAddress == eMailAddress) {
          console.log("already used");
          // you can just return here:
          //return httpResponse(200, "Email Address already in Use!"); 
          EmailInUse = true;
        }
    }

    console.log(EmailInUse);
    if (EmailInUse == false) {
      console.log("should not enter if email used");
      var params = {
        Item: {
          "AccountID": uuid.v1(),
          "Password": password,
          "EmailAddress": eMailAddress
        },
        TableName: "accounts"
      };

      await documentClient.put(params).promise();
      return httpResponse(200, JSON.stringify(params));
    }
  } catch (err) {
    return httpResponse(500, JSON.stringify(err));
  }
};

function httpResponse(statusCode, body) {
  return {
            statusCode,
            body,
            "isBase64Encoded": "false"
          };
}

Вы можете просто завершить процесс, когда адрес электронной почты будет найден, затем вы можете избавиться от переменной EmailInUse - это сделает ваш код короче, проще и легче рассуждать.

0 голосов
/ 26 февраля 2019

@ ttulka ответ очень точный.

Я хотел бы добавить что-то поверх его ответа, хотя:

Ваш код все еще может не работать даже после выполнения обратных вызовов - или асинхронных/ жду - разобрались.И почему это так?

DynamoDB - распределенная система.Распределенные системы по своей природе склонны использовать возможную согласованность в своей основе, и это именно то, что делает DynamoDB по умолчанию.

Это означает, что после того, как вы исправите свой код с помощью фрагмента @ ttulka, вы все равно можете попасть под возможная согласованность проблем.Если вы хотите быть абсолютно уверены, что вы читаете самые последние значения из ваших таблиц, вы должны использовать в своих запросах атрибут ConsistentRead.

Имейте в виду, что эти репликации, запускаемые DynamoDB, обычно молниеносны.быстро (в большинстве случаев это займет всего пару сотен миллисекунд), но вы можете в конечном итоге попасть в какую-то серую область, и тогда вы удивитесь, почему ваш код не работает.

Для вашего использованияслучай (проверка существующих электронных писем) это не должно иметь значения, потому что очень маловероятно, что два человека захотят зарегистрироваться на один и тот же адрес электронной почты практически в одно и то же время.Но убедитесь, что при работе с важными данными (например, банковскими счетами) вы всегда должны отдавать предпочтение ConsistentReads.Они стоят вдвое дороже по сравнению с EventualConsistentReads.

Также обратите внимание на ответ Томаса Эдвардса: операции сканирования чрезвычайно дороги (как по производительности, так и по стоимости).Вы должны избегать их любой ценой и использовать вместо них Глобальные вторичные индексы .

Надеюсь, это поможет!

РЕДАКТИРОВАТЬ :Исправлен ник тульки после того, как он указал на него:)

0 голосов
/ 26 февраля 2019

Сканирование невероятно дорого, и по мере роста вашего сайта это будет очень неэффективно.

Также имейте в виду, что для сохранения записи DynamoDB может потребоваться некоторое время, поэтому вы можете

Вам следует использовать индекс на EmailAddress в DynamoDB, если вы хотите быстро и часто выполнять поиск по нему или найти другой способ проверки на наличие дубликатов.У меня есть отдельный кешированный индекс зарегистрированных писем для проверки скорости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...