Uaktualnij funkcje Node.js pierwszej generacji do wersji drugiej generacji

Aplikacje korzystające obecnie z funkcji pierwszej generacji powinny rozważyć migrację do drugiej generacji, korzystając z instrukcji zawartych w tym przewodniku. Funkcje drugiej generacji korzystają z Cloud Run, aby zapewnić lepszą wydajność, lepszą konfigurację, lepsze monitorowanie i nie tylko.

W przykładach na tej stronie założono, że używasz JavaScript z modułami CommonJS ( require importu stylów), ale te same zasady dotyczą JavaScriptu z ESM ( import … from importu stylów) i TypeScript.

Proces migracji

Funkcje pierwszej i drugiej generacji mogą współistnieć obok siebie w tym samym pliku. Pozwala to na łatwą migrację kawałek po kawałku, gdy tylko będziesz gotowy. Zalecamy migrację jednej funkcji na raz, a przed kontynuowaniem przeprowadzanie testów i weryfikacji.

Sprawdź wersje interfejsu Firebase CLI i firebase-function

Upewnij się, że używasz Firebase CLI w wersji 12.00 i firebase-functions w wersji 4.3.0 . Każda nowsza wersja będzie obsługiwać zarówno 2., jak i 1. generację.

Zaktualizuj import

Import funkcji drugiej generacji z podpakietu v2 w zestawie SDK firebase-functions . Ta inna ścieżka importu to wszystko, czego potrzebuje interfejs CLI Firebase, aby określić, czy wdrożyć kod funkcji jako funkcję pierwszej czy drugiej generacji.

Podpakiet v2 jest modułowy i zalecamy importowanie tylko tego konkretnego modułu, którego potrzebujesz.

Przed: 1. gen

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

Po: 2. gen

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

Zaktualizuj definicje wyzwalaczy

Ponieważ zestaw SDK drugiej generacji faworyzuje importy modułowe, zaktualizuj definicje wyzwalaczy, aby odzwierciedlić zmienione importy z poprzedniego kroku.

Argumenty przekazywane do wywołań zwrotnych dla niektórych wyzwalaczy uległy zmianie. W tym przykładzie należy zauważyć, że argumenty wywołania zwrotnego onDocumentCreated zostały skonsolidowane w jeden obiekt event . Ponadto niektóre wyzwalacze mają nowe, wygodne funkcje konfiguracyjne, takie jak opcja cors wyzwalacza onRequest .

Przed: 1. gen

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

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

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

Po: 2. gen

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

Użyj konfiguracji sparametryzowanej

Funkcje drugiej generacji porzucają obsługę functions.config na rzecz bezpieczniejszego interfejsu do deklaratywnego definiowania parametrów konfiguracyjnych w bazie kodu. W przypadku nowego modułu params interfejs CLI blokuje wdrożenie, chyba że wszystkie parametry mają prawidłowe wartości, co gwarantuje, że funkcja nie zostanie wdrożona z brakującą konfiguracją.

Przeprowadź migrację do podpakietu params

Jeśli korzystałeś z konfiguracji środowiska za pomocą functions.config , możesz migrować istniejącą konfigurację do konfiguracji sparametryzowanej .

Przed: 1. gen

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

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

  // ...
});

Po: 2. gen

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());

  // ...
});

Ustaw wartości parametrów

Przy pierwszym wdrożeniu interfejs CLI Firebase pyta o wszystkie wartości parametrów i zapisuje je w pliku dotenv. Aby wyeksportować wartości funkcji.config, uruchom firebase functions:config:export .

Dla dodatkowego bezpieczeństwa można także określić typy parametrów i zasady sprawdzania poprawności .

Przypadek specjalny: klucze API

Moduł params integruje się z Cloud Secret Manager, który zapewnia precyzyjną kontrolę dostępu do wrażliwych wartości, takich jak klucze API. Aby uzyskać więcej informacji, zobacz tajne parametry .

Przed: 1. gen

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

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

Po: 2. gen

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

Ustaw opcje środowiska wykonawczego

Konfiguracja opcji wykonawczych uległa zmianie pomiędzy 1. i 2. generacją. Druga generacja dodaje także nową możliwość ustawiania opcji dla wszystkich funkcji.

Przed: 1. gen

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

Po: 2. gen

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

Użyj współbieżności

Istotną zaletą funkcji drugiej generacji jest zdolność pojedynczej instancji funkcji do obsługi więcej niż jednego żądania jednocześnie. Może to radykalnie zmniejszyć liczbę zimnych rozruchów doświadczanych przez użytkowników końcowych. Domyślnie współbieżność jest ustawiona na 80, ale można ustawić ją na dowolną wartość od 1 do 1000:

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

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

Dostrajanie współbieżności może poprawić wydajność i obniżyć koszty funkcji. Dowiedz się więcej o współbieżności w artykule Zezwalaj na równoczesne żądania .

Kontroluj użycie zmiennej globalnej

Funkcje pierwszej generacji napisane bez współbieżności mogą używać zmiennych globalnych, które są ustawiane i odczytywane przy każdym żądaniu. Kiedy współbieżność jest włączona, a pojedyncza instancja zaczyna obsługiwać wiele żądań jednocześnie, może to spowodować błędy w Twojej funkcji, ponieważ współbieżne żądania zaczną jednocześnie ustawiać i czytać zmienne globalne.

Podczas aktualizacji możesz ustawić procesor swojej funkcji na gcf_gen1 i ustawić concurrency na 1, aby przywrócić zachowanie pierwszej generacji:

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

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

Nie jest to jednak zalecane jako rozwiązanie długoterminowe, ponieważ powoduje utratę zalet wydajnościowych funkcji drugiej generacji. Zamiast tego sprawdź użycie zmiennych globalnych w swoich funkcjach i usuń te ustawienia tymczasowe, gdy będziesz gotowy.

Przeprowadź migrację ruchu do nowych funkcji drugiej generacji

Podobnie jak w przypadku zmiany regionu funkcji lub typu wyzwalacza , musisz nadać funkcji drugiej generacji nową nazwę i powoli przenosić do niej ruch.

Nie jest możliwe uaktualnienie funkcji z pierwszej do drugiej generacji o tej samej nazwie i uruchomienie firebase deploy . Spowoduje to błąd:

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

Zanim wykonasz te kroki, najpierw upewnij się, że Twoja funkcja jest idempotentna , ponieważ podczas zmiany zarówno nowa, jak i stara wersja Twojej funkcji będą działać w tym samym czasie. Na przykład, jeśli masz funkcję pierwszej generacji, która odpowiada na zdarzenia zapisu w Firestore, upewnij się, że dwukrotna odpowiedź na zapis, raz przez funkcję pierwszej generacji i raz przez funkcję drugiej generacji, w odpowiedzi na te zdarzenia pozostawia aplikację w stan spójny.

  1. Zmień nazwę funkcji w kodzie funkcji. Na przykład zmień nazwę resizeImage na resizeImageSecondGen .
  2. Wdróż funkcję, aby działała zarówno oryginalna funkcja 1. generacji, jak i funkcja 2. generacji.
    1. W przypadku wyzwalaczy wywoływalnych, kolejki zadań i HTTP rozpocznij wskazywanie wszystkim klientom funkcji drugiej generacji, aktualizując kod klienta nazwą lub adresem URL funkcji drugiej generacji.
    2. Dzięki wyzwalaczom w tle funkcje pierwszej i drugiej generacji będą reagować na każde zdarzenie natychmiast po wdrożeniu.
  3. Po wyłączeniu całego ruchu usuń funkcję pierwszej generacji za pomocą firebase functions:delete usuń.
    1. Opcjonalnie zmień nazwę funkcji 2. generacji, aby odpowiadała nazwie funkcji 1. generacji.