Если в вашем приложении используются функции первого поколения, рекомендуем перейти на второе поколение, следуя инструкциям в этом руководстве. Функции второго поколения используют Cloud Run, чтобы обеспечить более высокую производительность, улучшенные возможности настройки и мониторинга и т. д.
В примерах в этом документе предполагается, что вы используете JavaScript с модулями CommonJS (импорт в стиле require), но те же принципы применимы к JavaScript с ESM (импорт в стиле import … from) и TypeScript.
Процесс переноса
Функции первого и второго поколений могут сосуществовать в одном исходном файле. Это позволяет переносить базу кода по частям, по мере готовности. Однако обратите внимание, что такое смешивание пакетов не работает в рамках одной отдельной функции.
Рекомендуем переносить функции по одной, выполняя тестирование и проверку перед продолжением.
Проверьте версии Firebase CLI и firebase-functions
Убедитесь, что вы используете по крайней мере Firebase CLI версии 12.00 и firebase-functions версии 4.3.0. Более новые версии поддерживают как первое, так и второе поколение.
Обновить импорт
Функции второго поколения импортируются из подпакета v2 в SDK firebase-functions. Этого достаточно, чтобы интерфейс командной строки Firebase определил, какую версию функции нужно развернуть.
Подпакет v2 является модульным, и мы рекомендуем импортировать только тот модуль, который вам нужен.
До: первое поколение
const functions = require("firebase-functions/v1");
После: второе поколение
// explicitly import each trigger
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
Обновить определения триггеров
Поскольку SDK второго поколения поддерживает модульный импорт, обновите определения триггеров, чтобы отразить изменения импорта, внесенные на предыдущем шаге.
Аргументы, передаваемые обратным вызовам для некоторых триггеров, изменились. Обратите внимание, что в этом примере аргументы обратного вызова onDocumentCreated объединены в один объект event. Кроме того, в некоторых триггерах появились новые удобные функции настройки, например параметр cors в триггере onRequest.
До: первое поколение
const functions = require("firebase-functions/v1");
exports.date = functions.https.onRequest((req, res) => {
// ...
});
exports.uppercase = functions.firestore
.document("my-collection/{docId}")
.onCreate((change, context) => {
// ...
});
После: второе поколение
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
exports.date = onRequest({cors: true}, (req, res) => {
// ...
});
exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
/* ... */
});
Как минимизировать усилия по переписыванию кода с помощью деструктуризации JavaScript
Если у ваших функций сложные тела, которые в значительной степени зависят от контекста первого поколения или параметров, специфичных для поставщика (например, message или snapshot), вы можете использовать помощники совместимости первого поколения, встроенные в SDK второго поколения.
SDK второго поколения автоматически исправляет объект события с помощью геттеров, которые соответствуют сигнатурам первого поколения. Это позволяет использовать деструктуризацию JavaScript для извлечения этих свойств непосредственно в сигнатуре обработчика, сводя к минимуму необходимость переписывать логику функции.
Справочные материалы по сопоставлению поставщиков
| Поставщик | Аргументы первого поколения | Деструктуризация исправленных событий второго поколения |
| Pub/Sub | (message, context)
|
({ message, context }) => { ... }
|
| Cloud Firestore | (snapshot, context)
|
({ snapshot, context }) => { ... }
|
| Cloud Storage | (object, context)
|
({ object, context }) => { ... }
|
| Realtime Database | (snapshot, context)
|
({ snapshot, context }) => { ... }
|
| Remote Config | (version, context)
|
({ version, context }) => { ... }
|
| Cloud Scheduler | (context)
|
({ context }) => { ... }
|
| Очередь задач | (data, context)
|
({ data, context }) => { ... }
|
До (первое поколение):
export const myPubSubV1 = functions.pubsub.topic("my-topic").onPublish((message, context) => {
const data = message.json;
const eventId = context.eventId;
// ... rest of the logic
});
Новый вариант (второе поколение с деструктуризацией):
import { onMessagePublished } from "firebase-functions/v2/pubsub";
export const myPubSubV2 = onMessagePublished("my-topic", ({ message, context }) => {
// No need to change the function body!
const data = message.json; // Uses v1 Message wrapper
const eventId = context.eventId; // Uses v1 EventContext map
// ... rest of the logic
});
Использовать параметризованную конфигурацию
Функции второго поколения не поддерживают functions.config. Вместо этого используется более безопасный интерфейс для декларативного определения параметров конфигурации в базе кода.
С новым модулем params CLI блокирует развертывание, если не все параметры имеют допустимое значение. Это гарантирует, что функция не будет развернута с отсутствующей конфигурацией.
До: первое поколение
const functions = require("firebase-functions/v1");
exports.getQuote = functions.https.onRequest(async (req, res) => {
const quote = await fetchMotivationalQuote(functions.config().apiKey);
// ...
});
После: второе поколение
const {onRequest} = require("firebase-functions/v2/https");
const {defineSecret} = require("firebase-functions/params");
// Define the secret parameter
const apiKey = defineSecret("API_KEY");
exports.getQuote = onRequest(
// make the secret available to this function
{ secrets: [apiKey] },
async (req, res) => {
// retrieve the value of the secret
const quote = await fetchMotivationalQuote(apiKey.value());
// ...
}
);
Если у вас есть конфигурация среды с functions.config, перенесите ее в рамках обновления до второго поколения.
Поддержка functions.config API прекращена, и он будет отключен в марте 2027 г.
После этой даты развертывание с помощью functions.config будет невозможно.
Чтобы избежать сбоев при развертывании, перенесите конфигурацию в Cloud Secret Manager с помощью CLI Firebase. Это наиболее эффективный и безопасный способ переноса конфигурации.
Как экспортировать конфигурацию с помощью интерфейса командной строки Firebase
Используйте команду
config export, чтобы экспортировать существующую конфигурацию среды в новый секрет в Cloud Secret Manager:$ firebase functions:config:export i This command retrieves your Runtime Config values (accessed via functions.config()) and exports them as a Secret Manager secret. i Fetching your existing functions.config() from your project... ✔ Fetched your existing functions.config(). i Configuration to be exported: ⚠ This may contain sensitive data. Do not share this output. { ... } ✔ What would you like to name the new secret for your configuration? RUNTIME_CONFIG ✔ Created new secret version projects/project/secrets/RUNTIME_CONFIG/versions/1```Обновите код функции, чтобы связать секреты
Чтобы использовать конфигурацию, хранящуюся в новом секрете в Cloud Secret Manager, используйте
defineJsonSecretAPI в источнике функции. Также убедитесь, что секреты привязаны ко всем функциям, которым они нужны.До
const functions = require("firebase-functions/v1"); exports.myFunction = functions.https.onRequest((req, res) => { const apiKey = functions.config().someapi.key; // ... });После
const { onRequest } = require("firebase-functions/v2/https"); const { defineJsonSecret } = require("firebase-functions/params"); const config = defineJsonSecret("RUNTIME_CONFIG"); exports.myFunction = onRequest( // Bind secret to your function { secrets: [config] }, (req, res) => { // Access secret values via .value() const apiKey = config.value().someapi.key; // ... });Развертывание функций
Разверните обновленные функции, чтобы применить изменения и привязать разрешения для секретов.
firebase deploy --only functions:<your-function-name>
Как задать настройки среды выполнения
Конфигурация параметров среды выполнения в первом и втором поколении различается. Во втором поколении также добавлена возможность задавать параметры для всех функций.
До: первое поколение
const functions = require("firebase-functions/v1");
exports.date = functions
.runWith({
// Keep 5 instances warm for this latency-critical function
minInstances: 5,
})
// locate function closest to users
.region("asia-northeast1")
.https.onRequest((req, res) => {
// ...
});
exports.uppercase = functions
// locate function closest to users and database
.region("asia-northeast1")
.firestore.document("my-collection/{docId}")
.onCreate((change, context) => {
// ...
});
После: второе поколение
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
const {setGlobalOptions} = require("firebase-functions/v2");
// locate all functions closest to users
setGlobalOptions({ region: "asia-northeast1" });
exports.date = onRequest({
// Keep 5 instances warm for this latency-critical function
minInstances: 5,
}, (req, res) => {
// ...
});
exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
/* ... */
});
Обновите сервисный аккаунт по умолчанию (необязательно)
Функции первого поколения используют сервисный аккаунт по умолчанию Google App Engine для авторизации доступа к API Firebase, а функции второго поколения – сервисный аккаунт по умолчанию Compute Engine. Это может привести к проблемам с разрешениями для функций, перенесенных во второе поколение, если вы предоставили специальные разрешения сервисному аккаунту первого поколения. Если вы не меняли разрешения сервисного аккаунта, этот шаг можно пропустить.
Рекомендуемое решение – явно назначить существующий сервисный аккаунт App Engine первого поколения по умолчанию функциям, которые вы хотите перенести во второе поколение, переопределив настройки по умолчанию второго поколения. Для этого убедитесь, что каждая перенесенная функция задает правильное значение для serviceAccountEmail:
const {onRequest} = require("firebase-functions/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
const {setGlobalOptions} = require("firebase-functions");
// Use the App Engine default service account for all functions
setGlobalOptions({serviceAccountEmail: '<my-project-number>@<wbr>appspot.gserviceaccount.com'});
// Now I use the App Engine default service account.
exports.date = onRequest({cors: true}, (req, res) => {
// ...
});
// I do too!
exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
// ...
});
Вы также можете изменить сведения о сервисном аккаунте, чтобы они соответствовали всем необходимым разрешениям как для сервисного аккаунта по умолчанию App Engine (для первого поколения), так и для сервисного аккаунта по умолчанию Compute Engine (для второго поколения).
Использовать параллелизм
Значительным преимуществом функций второго поколения является способность одного экземпляра функции обслуживать более одного запроса одновременно. Это может значительно сократить количество холодных запусков, с которыми сталкиваются конечные пользователи. По умолчанию установлено значение 80, но вы можете задать любое значение от 1 до 1000.
const {onRequest} = require("firebase-functions/v2/https");
exports.date = onRequest({
// set concurrency value
concurrency: 500
},
(req, res) => {
// ...
});
Настройка параллелизма может повысить производительность и снизить стоимость функций. Подробнее о параллельных запросах…
Проверьте использование глобальных переменных
Функции первого поколения, написанные без учета параллелизма, могут использовать глобальные переменные, которые устанавливаются и считываются при каждом запросе. Если параллелизм включен и один экземпляр начинает обрабатывать несколько запросов одновременно, это может привести к ошибкам в вашей функции, поскольку параллельные запросы начинают устанавливать и считывать глобальные переменные одновременно.
При обновлении вы можете задать для ЦП функции значение gcf_gen1 и установить для параметра concurrency значение 1, чтобы восстановить поведение первого поколения:
const {onRequest} = require("firebase-functions/v2/https");
exports.date = onRequest({
// TEMPORARY FIX: remove concurrency
cpu: "gcf_gen1",
concurrency: 1
},
(req, res) => {
// ...
});
Однако это не рекомендуется в качестве долгосрочного решения, поскольку оно лишает преимуществ производительности функций второго поколения. Вместо этого проверьте использование глобальных переменных в своих функциях и удалите эти временные настройки, когда будете готовы.
Перенос трафика на новые функции второго поколения
Как и при изменении региона или типа триггера функции, вам нужно будет дать функции второго поколения новое имя и постепенно перенести на нее трафик.
Невозможно обновить функцию с первого поколения до второго с тем же названием и запустить firebase deploy. В противном случае возникнет ошибка:
Upgrading from GCFv1 to GCFv2 is not yet supported. Please delete your old function or wait for this feature to be ready.
Стратегия переноса зависит от типа триггера, который использует функция.
Как перенести триггеры Callable, очереди задач и HTTP
Эти триггеры являются прямыми вызовами. Поскольку у функции второго поколения будет новое название (и новый URL для триггеров HTTP), вы можете перенести трафик, обновив клиенты.
- Переименуйте функцию в коде (например, измените
myCallableнаmyCallableV2). - Разверните функцию. Теперь работают функции обоих поколений.
- Обновите код клиента или вызывающий объект, чтобы указать имя или URL новой функции второго поколения.
- Когда весь трафик будет перенаправлен на новую функцию, удалите функцию первого поколения с помощью команды
firebase functions:deleteв Firebase CLI.
Перенос фоновых триггеров
Фоновые триггеры (например, Pub/Sub, Cloud Firestore и Cloud Storage) реагируют на события в проекте. Чтобы не пропустить ни одного события во время перехода, необходимо временно запустить функции первого и второго поколения параллельно.
В течение переходного периода обе функции будут срабатывать при одном и том же событии. Это означает, что бизнес-логика будет выполняться дважды для каждого события. Прежде чем продолжить, убедитесь, что функция идемпотентна.
Добавьте функцию второго поколения рядом с функцией первого поколения, сохранив существующую функцию первого поколения в своем коде и добавив функцию второго поколения, прослушивающую тот же источник событий.
import * as functions from "firebase-functions/v1"; import { onMessagePublished } from "firebase-functions/v2/pubsub"; // --- Existing 1st gen function --- export const myPubSub = functions.pubsub.topic("my-topic").onPublish((message, context) => { console.log("V1 handler running for event:", context.eventId); // ... existing v1 function logic ... }); // --- New v2 passthrough function --- export const myPubSubV2 = onMessagePublished("my-topic", async ({ message, context }) => { console.log("v2 handler triggering V1 for event:", context.eventId); // Call the v1 function's handler await myPubSub.run(message, context); });Выполните команду
firebase deploy. Обе функции теперь активны и прослушивают одни и те же события.Убедитесь, что функция второго поколения получает трафик. Отслеживайте журналы для обеих функций. Убедитесь, что функция второго поколения вызывается для всех событий и вызовы выполняются успешно.
Убедившись, что функция работает правильно, перенесите фактическую бизнес-логику из функции первого поколения в тело функции второго поколения. Если вы использовали метод сквозной передачи, удалите вызов
myPubSub.run().import * as functions from "firebase-functions/v1"; import { onMessagePublished } from "firebase-functions/v2/pubsub"; // --- Existing v1 function (to be removed next) --- export const myPubSub = functions.pubsub.topic("my-topic").onPublish((message, context) => { console.log("v1 handler running for event:", context.eventId); // ... existing v1 function logic ... }); // --- New v2 function with full logic --- export const myPubSubV2 = onMessagePublished("my-topic", ({ message, context }) => { console.log("v2 handler running for event:", context.eventId); // ... existing v1 function logic WAS MOVED HERE ... });Разверните это изменение.
Удалите определение функции первого поколения из кода и повторно разверните его. CLI предложит удалить функцию первого поколения из Google Cloud.