Пакетная запись более 25 наименований на DynamoDB с использованием Lambda - PullRequest
0 голосов
/ 23 октября 2019

Редактировать x1: заменен фрагмент на полный файл

В настоящее время я выполняю процесс заполнения строк размером 1,8 КБ в DynamoDB. Когда пользователь создан, эти строки должны быть сгенерированы и вставлены. Их не нужно читать сразу (скажем, менее чем за 3 - 5 секунд). В настоящее время я использую AWS Lambda, и меня сбивает исключение тайм-аута (возможно, потому, что используется больше WCU, чем предоставлено, что у меня 5 с отключенным автоматическим масштабированием).

Я пробовал искать в Google и StackOverflow, и это кажется серой областью (что довольно странно, учитывая, что DynamoDB продается как невероятное решение, обрабатывающее огромные объемы данных в секунду), в котором нетясный путь существует.

Мы знаем, что DynamoDB ограничивает вставки 25 элементами на пакет , чтобы предотвратить издержки HTTP. Это означает, что мы можем вызвать неограниченное количество batchWrite и увеличить количество WCU.

Я пытался вызвать неограниченное количество batchWrite, просто запустив его и не ожидая его (Будет ли это считаться? Я читал, что, поскольку JS однопоточный, запросы будут обрабатываться по одному в любом случае, кромечто мне не придется ждать resposne, если я не использую обещание .... В настоящее время использую Node 10 и Lambda), и, похоже, ничего не происходит. Если я обещаю вызов и жду его, я получу исключение Lambda Timeout (вероятно, потому что он исчерпал WCU).

В настоящее время у меня есть 5 WCU и 5RCU (они слишком малы для этих операций со случайным всплеском?).

Я застрял, потому что не хочу случайным образом увеличивать WCU в течение коротких периодов времени. Кроме того, я читал, что автоматическое масштабирование не включается автоматически, и Amazon будет изменять размер единиц емкости только 4 раза в день.

Что мне с этим делать?

Вот полный файл, который я использую для вставки в DynamoDB

const aws = require("aws-sdk");

export async function batchWrite(
  data: {
    PutRequest: {
      Item: any;
    };
  }[]
) {
  const client = new aws.DynamoDB.DocumentClient({
    region: "us-east-2"
  });
  // 25 is the limit imposed by DynamoDB's batchWrite:
  // Member must have length less than or equal to 25.
  // This verifies whether the data is shaped correctly and has no duplicates.
  const sortKeyList: string[] = [];
  data.forEach((put, index) => {
    const item = put.PutRequest.Item;
    const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.
    const hasPk = has.call(item, "pk");
    const hasSk = has.call(item, "sk");
    // Checks if it doesn't have a sort key. Unless it's a tenant object, which has
    // the accountType attribute.
    if (!hasPk || !hasSk) {
      throw `hasPk is ${hasPk} and hasSk is ${hasSk} at index ${index}`;
    }

    if (typeof item["pk"] !== "string" || typeof item["sk"] !== "string") {
      throw `Item at index ${index} pk or sk is not a string`;
    }

    if (sortKeyList.indexOf(item.sk) !== -1) {
      throw `The item @ index ${index} and sortkey ${item.sk} has duplicate values`;
    }

    if (item.sk.indexOf("undefined") !== -1) {
      throw `There's an undefined in the sortkey ${index} and ${item.sk}`;
    }

    sortKeyList.push(put.PutRequest.Item.sk);
  });

  // DynamoDB only accepts 25 items at a time.
  for (let i = 0; i < data.length; i += 25) {
    const upperLimit = Math.min(i + 25, data.length);
    const newItems = data.slice(i, upperLimit);
    try {
      await client
        .batchWrite({
          RequestItems: {
            schon: newItems
          }
        })
        .promise();
    } catch (e) {
      console.log("Total Batches: " + Math.ceil(data.length / 25));
      console.error("There was an error while processing the request");
      console.log(e.message);
      console.log("Total data to insert", data.length);
      console.log("New items is", newItems);
      console.log("index is ", i);
      console.log("top index is", upperLimit);
      break;
    }
  }
  console.log(
    "If no errors are shown, creation in DynamoDB has been successful"
  );
}

1 Ответ

1 голос
/ 24 октября 2019

Есть две проблемы, с которыми вы сталкиваетесь, но я попытаюсь их решить.

Полный пример написанных элементов и фактический запрос batchWrite с указанными элементами не был предоставлен, поэтому неясно, правильно ли отформатирован фактический запрос. На основании предоставленной информации и проблемы, с которой вы столкнулись, создается впечатление, что запрос неправильно отформатирован.

Документацию по операции batchWrite в AWS Javascript SDK можно найти здесь и предыдущий ответ здесь показывает решение для правильного построения и форматирования запроса batchWrite.

Тем не менее, даже если запрос отформатирован правильно, все еще существует вторая проблема, заключающаяся в том, что имеется достаточно ресурсов для обработки запросов на запись для вставки 1800 записей в требуемое количество времени, которое имеет верхний предел5 секунд.

TL; DR быстрое и простое решение проблемы с пропускной способностью состоит в переключении с Предоставленная емкость на По требованию емкость. Как показано ниже, математика показывает, что если у вас нет согласованных и / или прогнозируемых требований к емкости, большую часть времени емкость по требованию будет не только устранять накладные расходы на управление выделенной емкости, но такжебыть значительно дешевле.

В соответствии с документацией AWS DynamoDB для выделенной емкости здесь , Write Capacity Unit или WCU оплачивается и, таким образом, определяется следующим образом:

Каждый вызов API для записи данных в вашу таблицу является запросом на запись. Для элементов размером до 1 КБ один WCU может выполнять один стандартный запрос записи в секунду.

Документация AWS для batchWrite / batchWriteItem API здесь указывает, что batchWrite Запрос API поддерживает до 25 элементов на запрос, а отдельные элементы могут быть до 400 КБ. Кроме того, количество WCU, необходимое для обработки запроса batchWrite, зависит от размера элементов в запросе. Документация AWS по управлению емкостью в DynamoDB здесь сообщает, что количество WCU, необходимое для обработки запроса batchWrite, рассчитывается следующим образом:

BatchWriteItem - записывает до25 предметов для одной или нескольких таблиц. DynamoDB обрабатывает каждый элемент в пакете как отдельный запрос PutItem или DeleteItem (обновления не поддерживаются). Таким образом, DynamoDB сначала округляет размер каждого элемента до следующей границы в 1 КБ, а затем вычисляет общий размер. Результат не обязательно совпадает с общим размером всех элементов. Например, если BatchWriteItem записывает элемент размером 500 байт и элемент размером 3,5 КБ, DynamoDB вычисляет размер как 5 КБ (1 КБ + 4 КБ), а не 4 КБ (500 байт + 3,5 КБ).

Размер элементов в запросе batchWrite не предоставлен, но ради этого ответа делается предположение, что они составляют <1 КБ каждый. Если в запросе содержится 25 элементов размером менее 1 КБ, то для обработки одного запроса batchWrite требуется минимальная выделенная емкость в 25 WCU <em>в секунду . Предполагая, что подготовлено минимум 25 необходимых WCU, учитывая 5-секундный лимит времени на вставку элементов, при условии предоставления всего 25 WCU, можно сделать только один запрос с 25 элементами в секунду, что составляет 125 элементов, вставленных за 5-секундный интервал времени. Исходя из этого, для достижения цели вставки 1800 элементов за 5 секунд для достижения цели необходимо 360 WCU.

Исходя из текущих расценок на установленную пропускную способность, найденных здесь , 360 выделенных мощностей WCU обойдутся примерно в 175 долларов в месяц (без учета бесплатных уровней).

Существует два варианта решения этой проблемы

  1. Увеличение выделенной емкости. Чтобы набрать 1800 предметов за 5 секунд, вам нужно подготовить 360 WCU.
  2. Лучше всего просто переключиться на On Demand емкость. В вопросе упоминалось, что запросы на запись являются «случайными операциями». Если запросы на запись не являются предсказуемыми и последовательными операциями над таблицей, то результатом часто является чрезмерное предоставление таблицы и оплата простоя. Емкость «по требованию» решает эту проблему и придерживается философии «без сервера» - платить только за то, что вы используете, когда вам выставляется счет только за то, что вы потребляете. В настоящее время цена по требованию составляет 1,25 долл. США на 1 млн. Куб. Исходя из этого, если каждый новый пользователь генерирует 1800 новых элементов для вставки, потребуется 97 223 новых пользователя в месяц, прежде чем предоставление емкости для таблицы будет конкурентоспособным по сравнению с использованием емкости по требованию. Иными словами, пока новый пользователь не регистрируется в среднем каждые 26 секунд, математика предлагает придерживаться емкости по требованию (стоит отметить, что это не учитывает RCU или другие элементы в таблице или другие схемы доступа).
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...