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

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

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

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

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

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

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

Обновить импорт

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

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

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

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

После: 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");

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 интерфейс командной строки блокирует развертывание, если все параметры не имеют допустимого значения, гарантируя, что функция не будет развернута с отсутствующей конфигурацией.

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

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

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

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

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 запрашивает все значения параметров и сохраняет значения в файле dotenv. Чтобы экспортировать значения Function.config, запустите firebase functions:config:export .

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

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

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

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

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

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-м поколением. Второе поколение также добавляет новую возможность установки параметров для всех функций.

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

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

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

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

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

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

При обновлении вы можете установить ЦП вашей функции на 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-го поколения новое имя и медленно переносить на нее трафик.

Невозможно обновить функцию с 1-го до 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.

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

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