Функции Firestore Firebase Работают медленно или возвращаются слишком быстро

# #firebase #google-cloud-firestore

Вопрос:

Немного новичок в этом, так что прошу прощения, если это просто.

У меня есть эта функция firebase ниже:

Некоторый Контекстный Код:

 const functions = require("firebase-functions");
const cors = require("cors")({
  origin: true,
});
const admin = require("firebase-admin");
admin.initializeApp();
const db = admin.firestore();
 

Фактическая Функция:

 exports.testFunction = functions.https.onRequest((request, response) => {
  cors(request, response, ()=>{
    functions.logger.info(request.body, {structuredData: true});
    const rbody = JSON.parse(request.body);
    const egConst = rbody.messageDets;
    egConst.sent = new Date().toUTCString();
    const setM = db.collection("egColl")
        .doc(rbody.egID)
        .collection("messages")
        .doc()
        .set({
          sent: egConst.sent,
          user: egConst.user,
          text: egConst.text,
        })
        .catch((error) => {
          response.status(400).send({code: 400, body: "Errorn"   error});
        });
    setM.then(
        response.status(200).send({code: 200, body: "Success"})
    );
  });
});
 

Сейчас этот вид работает по плану, но крайне медленно.
Во-первых, я получаю ответ 200 почти сразу, со скоростью, которую я ожидал бы, но документ не создается еще 40 секунд-1 минута.

Итак, две части…

  1. Почему я получаю ответ 200 до того, как «setM» будет выполнен.
  2. Что еще более важно, почему создание документа занимает так много времени? Если я запускаю очень похожую версию этого кода на клиенте, это происходит очень быстро, но у меня возникают проблемы с синхронизацией времени.

Заранее благодарю вас за любую помощь.

P.S. Я планирую вернуть его клиенту, но мне все равно хотелось бы получить ответы для дальнейшего понимания.

Комментарии:

1. Что вы используете, чтобы определить, сколько времени требуется для отображения в базе данных?

2. @samthecodingman Наблюдает за базой данных напрямую. Он также сопоставляется со страницей в представлении клиента. Оба отображают данные в одно и то же время. Примерно через 40 с лишним секунд. У меня есть почти идентичный код, который я запустил за пределами функций firebase, который работает мгновенно.

Ответ №1:

Проблема вызвана этими линиями:

 setM.then(
  response.status(200).send({code: 200, body: "Success"})
);
 

Здесь вместо передачи обратного then() вызова функции вы передаете результат send() функции. Поскольку это не обратный вызов, вы отправляете код ответа 200 раньше. Кроме того, как только функция отправила ответ, она считается «устаревшей», и производительность значительно снизится, пока она ожидает завершения работы Облачными функциями. Любая работа после отправки ответа может быть не завершена. Поэтому обязательно выполняйте любую работу (например, запись в базы данных и т. Д.), Прежде чем отправлять ответ. Если вы планируете выполнить длительную задачу, запишите задачу в базу данных (где она запускает другую функцию) и отправьте ответ клиенту, сообщив ему, что вы приняли задание и где искать обновления о его ходе.

Это должно быть:

 setM.then(() => {
    response.status(200).send({code: 200, body: "Success"})
});
 

или просто:

 setM.then(() =>
    response.status(200).send({code: 200, body: "Success"})
);
 

Однако в дополнение к этой проблеме вы связали эти действия в неправильном порядке. Разместив setM.then() его там, где он есть, вы эффективно сделали это:

 const setM = db.collection("egColl")
  .doc(rbody.egID)
  .collection("messages")
  .doc()
  .set({
    sent: egConst.sent,
    user: egConst.user,
    text: egConst.text,
  })
  .catch((error) => {
    response.status(400).send({code: 400, body: "Errorn"   error});
  })
  .then(() => {
    response.status(200).send({code: 200, body: "Success"})
  });
 

В приведенном выше коде, если set() вызов выдаст ошибку, обработчик, которому вы передали catch() , будет работать так, как вы ожидаете, и отправит ответ 400, но в дополнение к этому он также попытается отправить ответ 200, вызвав еще одну ошибку, потому что вы не можете отправить два разных ответа.

Далее, в вашем текущем коде вы также анализируете тело запроса вручную, если вы вызовете эту конечную точку с помощью a Content-Type application/json (как вам и следует делать), тело уже будет проанализировано для вас.

Примерно так я бы написал вашу функцию:

 exports.testFunction = functions.https.onRequest((request, response) => {
  cors(request, response, () => {
    functions.logger.info(request.body, {structuredData: true});
    const rbody = typeof request.body === "string" ? JSON.parse(request.body) : request.body;

    const { messageDets, egID } = rbody;
    const datetimeString = new Date().toUTCString();

    const messageRef = db.collection("egColl")
      .doc(egID)
      .collection("messages")
      .doc();

    messageRef
      .set({
        sent: datetimeString,
        user: messageDets.user,
        text: messageDets.text,
      })
      .then(
        () => {
          functions.logger.info(`Created new message: egColl/${egID}/messages/${messageRef.id}`);
          response.status(200).json({
            code: 200,
            message: 'Success',
            resourceId: messageRef.id
          })
        },
        (error) => {
          functions.logger.error("Error whilst setting data", error);
          response.status(400).send({
            code: 400,
            message: 'Error',
            // don't leak stack traces!
            // return Firebase error code instead of message if available
            error: error.code || error.message
          })
        }
      )
      .catch((error) => {
        // if everything is working as it should, this will never get called
        
        // you could send this to it's own logger
        functions.logger.error("Unexpected error while responding", error);
      });
  });
});
 

Комментарии:

1. Я отметил его правильно, поскольку он отвечает части 1.

2. @MasterEnzo теперь добавлена вторая часть

3. Превосходно! @samthecodingman Спасибо, что задал вопрос «Почему?». Это решило мою проблему, когда (1) получение токена OAuth из QuickBooks и запись в Firestore происходит в кратчайшие сроки на локальной машине (с использованием эмулятора Firebase), но (2) занимает около 4,5 минут (!) при развертывании в Firebase. Мое return заявление было сразу рядом с функцией, которая записывает данные в Firestore. Это должно привести к охлаждению экземпляра «Облачной функции», что приведет к наблюдаемым длительным RTTS.

Ответ №2:

Я бы предложил обновить консоль Firebase, но, скорее всего, проблема не в этом. Я бы рекомендовал реструктурировать ваш код таким образом и проверить, сохраняется ли проблема по-прежнему:

 exports.testFunction = functions.https.onRequest((request, response) => {
    cors(request, response, () => {
        functions.logger.info(request.body, { structuredData: true });
        const rbody = JSON.parse(request.body);
        const egConst = rbody.messageDets;
        egConst.sent = new Date().toUTCString();
        return db.collection("egColl")
            .doc(rbody.egID)
            .collection("messages")
            .doc()
            .set({
                sent: egConst.sent,
                user: egConst.user,
                text: egConst.text,
            }).then(() => {
                return response.status(200).send({ code: 200, body: "Success" })
            })
            .catch((error) => {
                return response.status(400).send({ code: 400, body: "Errorn"   error });
            });
    });
});
 

или вам придется использовать асинхронное ожидание. Он docRef.set() возвращает обещание, так что вам придется его дождаться. Но ваш существующий код не будет ждать его, потому что у вас его нет await , и, следовательно, он вернет 200 даже до того, как запрос будет обработан и завершит работу облачной функции.