Обновление документов Firestore с использованием облачных функций Firebase происходит очень медленно

Я пытаюсь обновить документы с помощью облачных функций Firebase. Мои обновления документов не должны учитывать существующие значения, и, поскольку они в основном включают обновления отдельных документов с минимальным риском конфликтов, я действую без обработки транзакций.

Проблема в том, что выполнение даже одного тестового примера занимает около 3 минут. Будучи младшим разработчиком, я никогда раньше не сталкивался с такими задержками при работе с облачными функциями и изо всех сил пытаюсь определить основную причину проблемы.

Ниже приведен мой полный код и созданные на его основе журналы:

const functions = require("firebase-functions");
const admin = require("firebase-admin");

admin.initializeApp();

exports.authenticator = functions.region('asia-northeast1').https.onRequest(async (req, res) => {
  const startTime = new Date().getTime();
  console.info("Function execution started at: ", new Date().toISOString());

  if (req.method !== "POST") {
    console.info("Method Not Allowed");
    return res.status(405).send("Method Not Allowed");
  }

  const data = req.body;

  if (!data || !data.data || !Array.isArray(data.data)) {
    console.info("Bad Request");
    return res.status(400).send("Bad Request");
  }

  res.status(200).send("Processing...");
  console.info("Process started at: ", new Date().toISOString());

  for (const item of data.data) {
    if (!item || !item.phoneNum || !item.authCode) {
      console.info("Invalid data: ", item);
      continue;
    }

    try {
      const updateStart = new Date().getTime();
      console.info("Update started for authCode: ", item.authCode, " at: ", new Date().toISOString());

      await admin.firestore().collection("auth").doc(item.authCode).update({
        phoneNum: item.phoneNum,
        verified: true,
        timestamp: admin.firestore.FieldValue.serverTimestamp()
      });

      const updateEnd = new Date().getTime();
      console.info("Success: ", item.authCode, " at: ", new Date().toISOString(), " Update took: ", (updateEnd - updateStart) / 1000, " seconds");
    } catch (err) {
      console.error(`Error processing ${item.authCode}:`, err);
    }
  }

  const endTime = new Date().getTime();
  console.info("Process ended at: ", new Date().toISOString());
  console.info("Total execution time: ", (endTime - startTime) / 1000, " seconds");
});

Выполнение функции началось Выполнение функции началось в: 2024-07-09T21:28:39.167Z

Процесс начался: 2024-07-09T21:28:39.188Z.

Обновление началось для кода авторизации: 519504f79458497cb8c4709739fbc877 по адресу: 2024-07-09T21:28:39.188Z

Выполнение функции заняло 80 мс и завершилось с кодом состояния: 200.

Успех: 519504f79458497cb8c4709739fbc877 по адресу: 2024-07-09T21:31:36.657Z

Обновление заняло: 177,469 секунды

Процесс завершился в: 2024-07-09T21:31:36.957Z.

Общее время выполнения: 177,79 секунды.

Регион моих облачных функций — asia-northeast1, а Firestore — asia-northeast3. Я изменил регионы из-за опасений по поводу потенциальных проблем, связанных с регионом, но раньше я быстро обрабатывал обновления даже в регионе us-central1.

Для справки: мой код получает данные, обработанные Gmail, через запрос POST от скриптов Google Apps.

Сколько документов вы обновляете for (const item of data.data) {?

Frank van Puffelen 10.07.2024 01:46

@FrankvanPuffelen Я провел тест по обновлению одного документа. Упомянутые 3 минуты были потрачены буквально на обновление всего одного документа.

한경완 10.07.2024 02:39
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы отправляете res.status(200).send("Processing..."); перед обновлением Firestore. Учитывая это, вам повезет, если какое-либо обновление документа вообще пройдет.

Переместите код, который отправляет ответ клиенту, до тех пор, пока вы не обновите все документы.


Кроме того, вы теперь используете await при каждом вызове обновления базы данных, а это значит, что все обновления выполняются последовательно — одно за другим. Поскольку данные, похоже, этого не требуют, будет быстрее, если вы обновите их все параллельно. Самый простой способ сделать это — собрать все обещания updateDoc в массиве, а затем await Promise.all(...) в этом массиве.


console.info("Process started at: ", new Date().toISOString());

const promises = [];
for (const item of data.data) {
  if (!item || !item.phoneNum || !item.authCode) {
    console.info("Invalid data: ", item);
    continue;
  }

  try {
    promises.push(admin.firestore().collection("auth").doc(item.authCode).update({
      phoneNum: item.phoneNum,
      verified: true,
      timestamp: admin.firestore.FieldValue.serverTimestamp()
    }));
  } catch (err) {
    console.error(`Error processing ${item.authCode}:`, err);
  }
}

await Promise.all(promises);

const endTime = new Date().getTime();
console.info("Process ended at: ", new Date().toISOString());
console.info("Total execution time: ", (endTime - startTime) / 1000, " seconds");

res.status(200).send("Processing...");

Я реализовал логику для параллельной обработки всех запросов с помощью Promise, но возникла та же проблема. Поэтому я написал приведенный выше код как потенциальное решение. Для реального использования я, скорее всего, буду использовать Promise. Спасибо за ваш совет.

한경완 10.07.2024 02:40

Кроме того, ранняя отправка ответа POST является запланированным поведением. Скрипты Google Apps имеют ограниченную квоту на время выполнения функции, поэтому они предназначены для отправки данных и немедленного завершения.

한경완 10.07.2024 02:40

Кроме того, я вспомнил важный момент. В предыдущем коде, где ответ POST не был отправлен раньше, обновление документов Firestore занимало примерно 5 секунд. Кажется, ответ POST может быть причиной проблемы. Есть ли способ отправить ответ POST как можно раньше, гарантируя при этом правильную обработку обновлений Firestore?

한경완 10.07.2024 02:41

Было бы лучше загрузить для этого новый вопрос? Я не очень знаком с переполнением стека.

한경완 10.07.2024 02:41

«Ранняя отправка ответа POST — это запланированное поведение». Возможно, это было намеренно с вашей стороны, но это меняет реакцию Cloud Functions. Как только вызывающему объекту будет отправлен полный ответ, он может завершить работу контейнера в любое время, поэтому есть большая вероятность, что ваши вызовы базы данных не смогут завершиться. --- Если вы не хотите, чтобы вызывающая сторона ждала завершения вызова, реализуйте это таким образом - не заставляя вызывающую сторону ждать, а не прерывая действие сервера.

Frank van Puffelen 10.07.2024 15:05

Другие вопросы по теме