Мы работаем над приложением, которое использует новые облачные функции Firebase. В настоящее время происходит то, что транзакция помещается в узел очереди. Затем функция удаляет этот узел и помещает его в правильный узел. Это было реализовано из-за возможности работы в автономном режиме.
Наша текущая проблема - это скорость функции. Сама функция занимает около 400 мс, так что ничего страшного. Но иногда функции занимают очень много времени (около 8 секунд), когда запись уже была добавлена в очередь.
Мы подозреваем, что серверу требуется время для загрузки, потому что, когда мы выполняем действие еще раз после первого. На это уходит меньше времени.
Есть ли способ решить эту проблему? Здесь я добавил код нашей функции. Мы подозреваем, что в этом нет ничего плохого, но добавили на всякий случай.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const database = admin.database();
exports.insertTransaction = functions.database
.ref('/userPlacePromotionTransactionsQueue/{userKey}/{placeKey}/{promotionKey}/{transactionKey}')
.onWrite(event => {
if (event.data.val() == null) return null;
// get keys
const userKey = event.params.userKey;
const placeKey = event.params.placeKey;
const promotionKey = event.params.promotionKey;
const transactionKey = event.params.transactionKey;
// init update object
const data = {};
// get the transaction
const transaction = event.data.val();
// transfer transaction
saveTransaction(data, transaction, userKey, placeKey, promotionKey, transactionKey);
// remove from queue
data[`/userPlacePromotionTransactionsQueue/${userKey}/${placeKey}/${promotionKey}/${transactionKey}`] = null;
// fetch promotion
database.ref(`promotions/${promotionKey}`).once('value', (snapshot) => {
// Check if the promotion exists.
if (!snapshot.exists()) {
return null;
}
const promotion = snapshot.val();
// fetch the current stamp count
database.ref(`userPromotionStampCount/${userKey}/${promotionKey}`).once('value', (snapshot) => {
let currentStampCount = 0;
if (snapshot.exists()) currentStampCount = parseInt(snapshot.val());
data[`userPromotionStampCount/${userKey}/${promotionKey}`] = currentStampCount + transaction.amount;
// determines if there are new full cards
const currentFullcards = Math.floor(currentStampCount > 0 ? currentStampCount / promotion.stamps : 0);
const newStamps = currentStampCount + transaction.amount;
const newFullcards = Math.floor(newStamps / promotion.stamps);
if (newFullcards > currentFullcards) {
for (let i = 0; i < (newFullcards - currentFullcards); i++) {
const cardTransaction = {
action: "pending",
promotion_id: promotionKey,
user_id: userKey,
amount: 0,
type: "stamp",
date: transaction.date,
is_reversed: false
};
saveTransaction(data, cardTransaction, userKey, placeKey, promotionKey);
const completedPromotion = {
promotion_id: promotionKey,
user_id: userKey,
has_used: false,
date: admin.database.ServerValue.TIMESTAMP
};
const promotionPushKey = database
.ref()
.child(`userPlaceCompletedPromotions/${userKey}/${placeKey}`)
.push()
.key;
data[`userPlaceCompletedPromotions/${userKey}/${placeKey}/${promotionPushKey}`] = completedPromotion;
data[`userCompletedPromotions/${userKey}/${promotionPushKey}`] = completedPromotion;
}
}
return database.ref().update(data);
}, (error) => {
// Log to the console if an error happened.
console.log('The read failed: ' + error.code);
return null;
});
}, (error) => {
// Log to the console if an error happened.
console.log('The read failed: ' + error.code);
return null;
});
});
function saveTransaction(data, transaction, userKey, placeKey, promotionKey, transactionKey) {
if (!transactionKey) {
transactionKey = database.ref('transactions').push().key;
}
data[`transactions/${transactionKey}`] = transaction;
data[`placeTransactions/${placeKey}/${transactionKey}`] = transaction;
data[`userPlacePromotionTransactions/${userKey}/${placeKey}/${promotionKey}/${transactionKey}`] = transaction;
}
источник
Ответы:
Огнебазер здесь
Похоже, вы испытываете так называемый холодный запуск функции.
Если ваша функция не выполнялась какое-то время, Cloud Functions переводит ее в режим, в котором используется меньше ресурсов. Затем, когда вы снова нажимаете функцию, она восстанавливает среду из этого режима. Время, необходимое для восстановления, состоит из фиксированной стоимости (например, восстановление контейнера) и частично переменной стоимости (например, если вы используете много узловых модулей, это может занять больше времени).
Мы постоянно отслеживаем производительность этих операций, чтобы обеспечить наилучшее сочетание опыта разработчика и использования ресурсов. Так что ожидайте, что эти времена со временем улучшатся.
Хорошая новость в том, что вы должны испытать это только во время разработки. Если ваши функции будут часто запускаться в производственной среде, скорее всего, они никогда больше не начнут холодный запуск.
источник
Обновление май 2020 г.Спасибо за комментарий от maganap - в Node 10+
FUNCTION_NAME
заменяется наK_SERVICE
(FUNCTION_TARGET
это сама функция, а не ее имя, заменаENTRY_POINT
). Примеры кода ниже были обновлены ниже.Дополнительная информация на https://cloud.google.com/functions/docs/migrating/nodejs-runtimes#nodejs-10-changes.
Обновление - похоже, что многие из этих проблем могут быть решены с помощью скрытой переменной,
process.env.FUNCTION_NAME
как показано здесь: https://github.com/firebase/functions-samples/issues/170#issuecomment-323375462Обновление с помощью кода - например, если у вас есть следующий индексный файл:
Затем все ваши файлы будут загружены, и все требования к этим файлам также будут загружены, что приведет к большим накладным расходам и загрязнению вашей глобальной области видимости для всех ваших функций.
Вместо этого разделяйте ваши включения как:
Это загрузит только требуемый файл (ы), когда эта функция специально вызывается; позволяя вам сохранить вашу глобальную осциллографию намного чище, что должно привести к более быстрой холодной загрузке.
Это должно позволить найти гораздо более аккуратное решение, чем то, что я сделал ниже (хотя объяснение ниже все еще актуально).
Оригинальный ответ
Похоже, что требуемые файлы и общая инициализация, происходящая в глобальной области видимости, является огромной причиной замедления во время холодной загрузки.
По мере того, как проект получает больше функций, глобальная область видимости становится все более и более загрязненной, что усугубляет проблему - особенно если вы ограничиваете свои функции отдельными файлами (например, используя
Object.assign(exports, require('./more-functions.js'));
в вашемindex.js
.Мне удалось увидеть огромный прирост производительности при холодной загрузке, переместив все свои требования в метод инициализации, как показано ниже, а затем вызвав его в качестве первой строки внутри любого определения функции для этого файла. Например:
Я заметил улучшения с 7-8 до 2-3 секунд при применении этой техники к проекту с ~ 30 функциями в 8 файлах. Это также приводит к тому, что функции должны реже запускаться в холодном режиме (предположительно из-за меньшего использования памяти?)
К сожалению, это по-прежнему делает функции HTTP практически непригодными для использования в производственной среде.
Надеемся, что у команды Firebase есть планы на будущее, которые позволят правильно определить объем функций, чтобы для каждой функции нужно было загружать только соответствующие модули.
источник
process.env.FUNCTION_NAME
и использовать ее для условного включения файлов, необходимых для этой функции. Комментарий на github.com/firebase/functions-samples/issues/ ... дает действительно хорошее описание этой работы! Это гарантирует, что глобальная область не загрязнена методами и не включает нерелевантные функции.FUNCTIONS_NAME
действительно только для узлов 6 и 8, как описано здесь: cloud.google.com/functions/docs/… . Узел 10 следует использоватьFUNCTION_TARGET
K_SERVICE
соответствии с документом на cloud.google.com/functions/docs/migrating/… - Я обновил свой ответ.Я сталкиваюсь с аналогичными проблемами с облачными функциями firestore. Самое большое - это производительность. Особенно в случае стартапов на ранней стадии, когда вы не можете позволить своим первым клиентам видеть «вялые» приложения. Простая функция создания документации, например, дает следующее:
- Выполнение функции заняло 9522 мс, завершено с кодом состояния: 200
Затем: у меня была простая страница с условиями. С облачными функциями выполнение из-за холодного старта временами занимало 10-15 секунд. Затем я переместил его в приложение node.js, размещенное в контейнере appengine. Время сократилось до 2-3 секунд.
Я сравнивал многие функции mongodb с firestore, и иногда мне тоже интересно, стоит ли на этом раннем этапе моего продукта перейти на другую базу данных. Самым большим советом, который у меня был в firestore, была функциональность триггера onCreate, onUpdate объектов документа.
https://db-engines.com/en/system/Google+Cloud+Firestore%3BMongoDB
В принципе, если на вашем сайте есть статические части, которые можно выгрузить в среду appengine, возможно, неплохая идея.
источник
Я тоже делал это, что улучшает производительность после того, как функции прогреты, но холодный старт меня убивает. Одна из других проблем, с которыми я столкнулся, - это cors, потому что для выполнения работы требуется два обращения к облачным функциям. Я уверен, что смогу это исправить.
Когда у вас есть приложение на ранней (демонстрационной) стадии, когда оно не используется часто, производительность не будет высокой. Это то, что следует учитывать, так как те, кто раньше внедряет продукт, должны выглядеть лучше перед потенциальными клиентами / инвесторами. Нам понравилась эта технология, поэтому мы перешли со старых проверенных временем фреймворков, но на данный момент наше приложение кажется довольно медленным. Я собираюсь попробовать несколько разогревающих стратегий, чтобы он выглядел лучше
источник
ОБНОВЛЕНИЕ / РЕДАКТИРОВАНИЕ: новый синтаксис и обновления появятся в мае 2020 г.
Я только что опубликовал пакет под названием
better-firebase-functions
, он автоматически выполняет поиск в вашем каталоге функций и правильно размещает все найденные функции в вашем объекте экспорта, одновременно изолируя функции друг от друга для повышения производительности при холодной загрузке.Если вы выполняете отложенную загрузку и кешируете только те зависимости, которые вам нужны для каждой функции в рамках области модуля, вы обнаружите, что это самый простой и легкий способ сохранить свои функции оптимально эффективными в быстрорастущем проекте.
источник