Обновление функций Node.js 1-го поколения до 2-го поколения

Приложениям, использующим функции 1-го поколения, следует рассмотреть возможность перехода на 2-е поколение, следуя инструкциям в этом руководстве. Функции 2-го поколения используют Cloud Run для обеспечения более высокой производительности, лучшей настройки, лучшего мониторинга и многого другого.

Примеры на этой странице предполагают, что вы используете JavaScript с модулями CommonJS ( require импорт стилей), но те же принципы применимы к JavaScript с ESM ( import … from импорта стилей) и TypeScript.

Процесс миграции

Функции первого и второго поколений могут располагаться параллельно в одном файле. Это позволяет легко переносить их по частям, по мере готовности. Мы рекомендуем переносить по одной функции за раз, проводя тестирование и проверку перед продолжением.

Проверьте версии Firebase CLI и firebase-function

Убедитесь, что вы используете Firebase CLI версии не ниже 12.00 и firebase-functions версии 4.3.0 . Любая более новая версия будет поддерживать как 2-е, так и 1-е поколение.

Обновление импорта

Функции второго поколения импортируются из подпакета v2 в SDK firebase-functions . Этот другой путь импорта — всё, что нужно Firebase CLI, чтобы определить, следует ли развернуть код функции как функцию первого или второго поколения.

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

До: 1-го поколения

const functions = require("firebase-functions/v1");

После: 2-го поколения

// explicitly import each trigger
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

Обновление определений триггеров

Поскольку SDK 2-го поколения отдает предпочтение модульному импорту, обновите определения триггеров, чтобы отразить измененный импорт из предыдущего шага.

Аргументы, передаваемые в обратные вызовы для некоторых триггеров, изменились. Обратите внимание, что в этом примере аргументы обратного вызова onDocumentCreated были объединены в один объект event . Кроме того, некоторые триггеры получили новые удобные функции настройки, например, параметр cors триггера onRequest .

До: 1-го поколения

const functions = require("firebase-functions/v1");

exports.date = functions.https.onRequest((req, res) => {
  // ...
});

exports.uppercase = functions.firestore
  .document("my-collection/{docId}")
  .onCreate((change, context) => {
    // ...
  });

После: 2-го поколения

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) => {
  /* ... */
});

Использовать параметризованную конфигурацию

Функции второго поколения отказываются от поддержки файла functions.config в пользу более безопасного интерфейса для декларативного определения параметров конфигурации внутри кодовой базы. С появлением нового модуля params CLI блокирует развертывание, если все параметры не имеют допустимых значений, гарантируя, что функция не будет развернута с отсутствующей конфигурацией.

Перейти к подпакету params

Если вы использовали конфигурацию среды с помощью functions.config , вы можете перенести существующую конфигурацию в параметризованную конфигурацию .

До: 1-го поколения

const functions = require("firebase-functions/v1");

exports.date = functions.https.onRequest((req, res) => {
  const date = new Date();
  const formattedDate =
date.toLocaleDateString(functions.config().dateformat);

  // ...
});

После: 2-го поколения

const {onRequest} = require("firebase-functions/v2/https");
const {defineString} = require("firebase-functions/params");

const dateFormat = defineString("DATE_FORMAT");

exports.date = onRequest((req, res) => {
  const date = new Date();
  const formattedDate = date.toLocaleDateString(dateFormat.value());

  // ...
});

Установить значения параметров

При первом развёртывании Firebase CLI запрашивает все значения параметров и сохраняет их в файле dotenv. Чтобы экспортировать значения функций.config, выполните команду firebase functions:config:export .

Для дополнительной безопасности вы также можете указать типы параметров и правила проверки .

Особый случай: API-ключи

Модуль params интегрируется с Cloud Secret Manager, который обеспечивает детальный контроль доступа к конфиденциальным данным, таким как ключи API. Подробнее см. в разделе «Параметры секрета» .

До: 1-го поколения

const functions = require("firebase-functions/v1");

exports.getQuote = functions.https.onRequest(async (req, res) => {
  const quote = await fetchMotivationalQuote(functions.config().apiKey);
  // ...
});

После: 2-го поколения

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());
    // ...
  }
);

Установить параметры выполнения

Конфигурация параметров среды выполнения изменилась между 1-м и 2-м поколениями. 2-е поколение также добавляет новую возможность задавать параметры для всех функций.

До: 1-го поколения

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) => {
    // ...
  });

После: 2-го поколения

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) => {
  /* ... */
});

Использовать параллелизм

Значительным преимуществом функций второго поколения является способность одного экземпляра функции обслуживать несколько запросов одновременно. Это может значительно сократить количество холодных запусков, с которыми сталкиваются конечные пользователи. По умолчанию уровень параллелизма равен 80, но вы можете задать любое значение от 1 до 1000:

const {onRequest} = require("firebase-functions/v2/https");

exports.date = onRequest({
    // set concurrency value
    concurrency: 500
  },
  (req, res) => {
    // ...
});

Настройка параллельной обработки может повысить производительность и снизить стоимость функций. Подробнее о параллельной обработке см. в статье «Разрешить параллельные запросы» .

Аудит использования глобальных переменных

Функции первого поколения, написанные без учёта параллелизма, могут использовать глобальные переменные, которые устанавливаются и считываются при каждом запросе. Когда параллелизм включён, и один экземпляр начинает обрабатывать несколько запросов одновременно, это может привести к ошибкам в вашей функции, поскольку параллельные запросы начинают одновременно устанавливать и считывать глобальные переменные.

Во время обновления вы можете установить CPU вашей функции на gcf_gen1 и concurrency на 1, чтобы восстановить поведение 1-го поколения:

const {onRequest} = require("firebase-functions/v2/https");

exports.date = onRequest({
    // TEMPORARY FIX: remove concurrency
    cpu: "gcf_gen1",
    concurrency: 1
  },
  (req, res) => {
    // ...
});

Однако это не рекомендуется в качестве долгосрочного решения, поскольку оно снижает преимущества в производительности функций второго поколения. Вместо этого проверьте использование глобальных переменных в своих функциях и удалите эти временные настройки, когда будете готовы.

Перенос трафика на новые функции 2-го поколения

Как и при изменении региона функции или типа триггера , вам нужно будет дать функции 2-го поколения новое имя и медленно перевести на нее трафик.

Невозможно обновить функцию с первого до второго поколения с тем же именем и запустить firebase deploy . Это приведёт к ошибке:

Upgrading from GCFv1 to GCFv2 is not yet supported. Please delete your old function or wait for this feature to be ready.

Прежде чем выполнять эти шаги, убедитесь, что ваша функция является идемпотентной , поскольку во время изменения обе её версии будут выполняться одновременно. Например, если у вас есть функция первого поколения, которая реагирует на события записи в Firestore, убедитесь, что двойная реакция на запись (один раз функцией первого поколения и один раз функцией второго поколения) в ответ на эти события сохраняет ваше приложение в согласованном состоянии.

  1. Переименуйте функцию в коде вашей функции. Например, переименуйте resizeImage в resizeImageSecondGen .
  2. Разверните функцию так, чтобы работали как исходная функция 1-го поколения, так и функция 2-го поколения.
    1. В случае вызываемых триггеров, триггеров очереди задач и HTTP начните направлять всех клиентов на функцию 2-го поколения, обновив код клиента именем или URL-адресом функции 2-го поколения.
    2. Благодаря фоновым триггерам функции как 1-го, так и 2-го поколения будут реагировать на каждое событие немедленно после развертывания.
  3. После переноса всего трафика удалите функцию 1-го поколения с помощью firebase functions:delete .
    1. При желании переименуйте функцию 2-го поколения, чтобы она соответствовала имени функции 1-го поколения.