Uaktualnij funkcje Node.js 1 generacji do 2 generacji

Aplikacje korzystające z funkcji 1 generacji powinny rozważyć migrację do funkcji 2 generacji zgodnie z instrukcjami w tym przewodniku. Funkcje 2 generacji korzystają z Cloud Run, aby zapewnić lepszą wydajność, lepszą konfigurację, lepsze monitorowanie i wiele innych korzyści.

Przykłady na tej stronie zakładają, że używasz JavaScriptu z modułami CommonJS (importy w stylu require), ale te same zasady dotyczą JavaScriptu z modułami ESM (importy w stylu import … from) i TypeScriptu.

Proces migracji

Funkcje 1 generacji i 2 generacji mogą współistnieć w tym samym pliku. Umożliwia to łatwą migrację krok po kroku, gdy będziesz na to gotowy. Zalecamy migrację po jednej funkcji naraz oraz przeprowadzenie testów i weryfikacji przed kontynuowaniem.

Sprawdź wersje wiersza poleceń Firebase i firebase-function

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

Aktualizowanie importów

Funkcje 2 generacji są importowane z podpakietu v2 w pakiecie SDK firebase-functions. Ta inna ścieżka importu jest wszystkim, czego potrzebuje interfejs CLI Firebase, aby określić, czy wdrożyć kod funkcji jako funkcję 1 czy 2 generacji.

Pakiet podrzędny v2 jest modułowy i zalecamy importowanie tylko konkretnego modułu, którego potrzebujesz.

Wcześniej: 1 generacja

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

Po: 2 generacja

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

Aktualizowanie definicji aktywatorów

Pakiet SDK drugiej generacji preferuje importowanie modułowe, więc zaktualizuj definicje reguł, aby odzwierciedlały zmiany importu z poprzedniego kroku.

Argumenty przekazywane do wywołań zwrotnych w przypadku niektórych wyzwalaczy uległy zmianie. W tym przykładzie zwróć uwagę, że argumenty wywołania zwrotnego onDocumentCreated zostały skonsolidowane w jeden obiekt event. Niektóre wyzwalacze mają też wygodne nowe funkcje konfiguracji, np. wyzwalacz onRequest ma opcję cors.

Wcześniej: 1 generacja

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

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

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

Po: 2 generacja

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żywanie konfiguracji sparametryzowanej

Funkcje 2 generacji nie obsługują już functions.config. Zamiast tego mają bezpieczniejszy interfejs do deklaratywnego definiowania parametrów konfiguracji w kodzie. Dzięki nowemu modułowi params interfejs CLI blokuje wdrażanie, dopóki wszystkie parametry nie będą miały prawidłowej wartości. Dzięki temu funkcja nie zostanie wdrożona bez brakującej konfiguracji.

Migracja do pakietu params

Jeśli używasz konfiguracji środowiska z functions.config, możesz przenieść dotychczasową konfigurację na konfigurację sparametryzowaną.

Wcześniej: 1 generacja

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

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

  // ...
});

Po: 2 generacja

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

  // ...
});

Ustawianie wartości parametrów

Przy pierwszym wdrożeniu interfejs Firebase CLI wyświetli prośbę o podanie wszystkich wartości parametrów i zapisze je w pliku dotenv. Aby wyeksportować wartości z pliku functions.config, uruchom polecenie firebase functions:config:export.

Aby zwiększyć bezpieczeństwo, możesz też określić typy parametrów i reguły weryfikacji.

Przypadek specjalny: klucze interfejsu API

Moduł params jest zintegrowany z usługą Cloud Secret Manager, która zapewnia szczegółową kontrolę dostępu do wartości wrażliwych, takich jak klucze interfejsu API. Więcej informacji znajdziesz w sekcji parametry tajne.

Wcześniej: 1 generacja

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

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

Po: 2 generacja

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

Ustawianie opcji środowiska wykonawczego

Konfiguracja opcji środowiska wykonawczego zmieniła się między 1 a 2 generacją. W 2 generacji dodano też nową funkcję ustawiania opcji dla wszystkich funkcji.

Wcześniej: 1 generacja

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

Po: 2 generacja

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

Korzystanie z równoczesności

Istotną zaletą funkcji 2 generacji jest możliwość obsługiwania przez jedną instancję funkcji więcej niż jednego żądania naraz. Może to znacznie zmniejszyć liczbę uruchomień „na zimno” u użytkowników. Domyślnie równoczesność jest ustawiona na 80, ale możesz ustawić dowolną wartość z zakresu 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. Więcej informacji o równoczesności znajdziesz w sekcji Zezwalanie na równoczesne żądania.

Sprawdzanie użycia zmiennych globalnych

Funkcje 1 generacji napisane bez uwzględnienia współbieżności mogą używać zmiennych globalnych, które są ustawiane i odczytywane w każdym żądaniu. Jeśli współbieżność jest włączona, a pojedyncza instancja zaczyna obsługiwać wiele żądań naraz, w funkcji mogą pojawić się błędy, ponieważ współbieżne żądania zaczynają jednocześnie ustawiać i odczytywać zmienne globalne.

Podczas uaktualniania możesz ustawić procesor funkcji na gcf_gen1 i ustawić concurrency na 1, aby przywrócić działanie 1 generacji:

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

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

Nie zalecamy jednak tego rozwiązania jako długoterminowego, ponieważ powoduje ono utratę korzyści związanych z wydajnością funkcji 2 generacji. Zamiast tego sprawdź użycie zmiennych globalnych w funkcjach i usuń te ustawienia tymczasowe, gdy będziesz gotowy(-a).

Przenoszenie ruchu do nowych funkcji 2 generacji

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

Nie można uaktualnić funkcji z 1 do 2 generacji o tej samej nazwie i uruchomić 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 czynności, upewnij się, że funkcja jest idempotentna, ponieważ podczas zmiany będą działać jednocześnie nowa i stara wersja funkcji. Jeśli na przykład masz funkcję 1 generacji, która reaguje na zdarzenia zapisu w Firestore, upewnij się, że reagowanie na zapis dwukrotnie – raz przez funkcję 1 generacji i raz przez funkcję 2 generacji – w odpowiedzi na te zdarzenia pozostawia aplikację w spójnym stanie.

  1. Zmień nazwę funkcji w jej kodzie. Na przykład zmień nazwę resizeImage na resizeImageSecondGen.
  2. Wdróż funkcję, aby działały zarówno oryginalna funkcja 1 generacji, jak i funkcja 2 generacji.
    1. W przypadku aktywatorów wywoływanych, kolejki zadań i HTTP zacznij kierować wszystkich klientów do funkcji 2 generacji, aktualizując kod klienta o nazwę lub adres URL funkcji 2 generacji.
    2. W przypadku reguł w tle zarówno funkcje 1 generacji, jak i 2 generacji będą reagować na każde zdarzenie natychmiast po wdrożeniu.
  3. Gdy cały ruch zostanie przeniesiony, usuń funkcję 1 generacji za pomocą polecenia firebase functions:delete wiersza poleceń Firebase.
    1. Opcjonalnie zmień nazwę funkcji 2 generacji, aby pasowała do nazwy funkcji 1 generacji.