Uaktualnij funkcje Node.js 1 generacji do 2 generacji

Aplikacje, które obecnie korzystają z funkcji 1 generacji, powinny rozważyć migrację do 2 generacji, korzystając z instrukcji zawartych w tym przewodniku. Funkcje 2 generacji korzystają z Cloud Run, aby między innymi zwiększyć wydajność, konfigurację i lepsze monitorowanie.

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 ESM (importy w stylu import … from) i TypeScript.

Proces migracji

Funkcje 1 i 2 generacji mogą współistnieć w tym samym pliku. Dzięki temu możesz łatwo przeprowadzić migrację, wykonując poszczególne kroki w miarę potrzeb. Zalecamy przenoszenie jednej funkcji naraz, a przed dalszymi działaniami przetestowanie i weryfikację.

Sprawdź wersje wiersza poleceń Firebase i firebase-function

Upewnij się, że używasz interfejsu wiersza poleceń Firebase co najmniej w wersji 12.00 i firebase-functions w wersji 4.3.0. Każda nowsza wersja obsługuje zarówno urządzenia 2 i 1 generacji.

Aktualizowanie importów

Funkcje 2 generacji importowane z podpakietu v2 w pakiecie SDK firebase-functions. Ta odmienna ścieżka importowania wystarczy, aby interfejs wiersza poleceń Firebase mógł określić, czy wdrożyć kod funkcji jako funkcję 1 czy 2 generacji.

Podpakiet v2 jest modułowy i zalecamy importowanie tylko potrzebnych modułów.

Przed: 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 reguł

Ponieważ pakiet SDK 2 generacji preferuje importowanie modułów, zaktualizuj definicje reguł, aby odzwierciedlały one zmienione importy z poprzedniego kroku.

Argumenty przekazywane do wywołań zwrotnych w przypadku niektórych aktywatorów uległy zmianie. W tym przykładzie argumenty funkcji zwracanej onDocumentCreated zostały scalone w jeden obiekt event. Poza tym niektóre aktywatory mają nowe, wygodne funkcje konfiguracji, takie jak opcja cors aktywatora onRequest.

Przed: 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żyj konfiguracji z parametrami

Funkcje 2 generacji nie obsługują już functions.config, ponieważ oferują bezpieczniejszy interfejs do deklaratywnego definiowania parametrów konfiguracji w kodzie. W nowym module params interfejs wiersza poleceń blokuje wdrożenie, chyba że wszystkie parametry mają prawidłową wartość. Dzięki temu funkcja nie zostanie wdrożona z brakującą konfiguracją.

Przejdź do podpakietu params

Jeśli korzystasz z konfiguracji środowiska z functions.config, możesz przenieść istniejącą konfigurację do konfiguracji z parametrami.

Przed: 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

Podczas pierwszego wdrożenia narzędzie wiersza poleceń Firebase wyświetli prompt z prośbą o wszystkie wartości parametrów i zapisze je w pliku dotenv. Aby wyeksportować wartości z pliku functions.config, uruchom firebase functions:config:export.

Aby zwiększyć bezpieczeństwo, możesz też określić typyreguły walidacji parametrów.

Przypadek szczególny: klucze interfejsu API

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

Przed: 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 czasu wykonywania zmieniła się między 1 a 2 generacją. 2 generacja wprowadza też nową możliwość ustawiania opcji dla wszystkich funkcji.

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

Używaj równoczesności

Jedną z istotne zalet funkcji 2 generacji jest możliwość obsługi przez pojedynczą instancję funkcji większej liczby żądań jednocześnie. 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ść od 1 do 1000:

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

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

Dostosowywanie współbieżności może poprawić wydajność i zmniejszyć koszty funkcji. Więcej informacji o równoczesności znajdziesz w artykule Zezwalanie na żądania równoczesne.

Sprawdzanie wykorzystania 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 ramach każdego żądania. Jeśli włączysz równoczesność, a pojedyncza instancja zacznie obsługiwać wiele żądań jednocześnie, może to spowodować błędy w Twojej funkcji, ponieważ równoczesne żądania zaczną jednocześnie ustawiać i czytać zmienne globalne.

Podczas uaktualniania możesz ustawić procesor funkcji na gcf_gen1, a 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 jest to jednak zalecane jako długoterminowe rozwiązanie, ponieważ powoduje utratę zalet funkcji 2 generacji. Zamiast tego sprawdź użycie zmiennych globalnych w funkcjach i usuń te tymczasowe ustawienia, gdy już to zrobisz.

Przeprowadź migrację ruchu do nowych funkcji 2 generacji

Podobnie jak w przypadku zmiany regionu lub typu reguły, musisz nadać funkcji 2 generacji nową nazwę i powoli przenosić na nią ruch.

Nie można uaktualnić funkcji 1 generacji do 2 generacji o tej samej nazwie i uruchomić funkcji firebase deploy. Spowoduje to wyświetlenie tego komunikatu o błędzie:

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 w odpowiedzi na te zdarzenia funkcja 1 generacji reaguje na zapis dwukrotnie, a funkcja 2 generacji reaguje na niego raz. Dzięki temu aplikacja będzie w spójnym stanie.

  1. Zmień nazwę funkcji w kodzie funkcji. Na przykład zmień nazwę resizeImage na resizeImageSecondGen.
  2. Wdróż funkcję, aby działała zarówno pierwotna funkcja 1 generacji, jak i 2 generacji.
    1. W przypadku wywoływalnych funkcji, kolejek zadań i aktywacji HTTP zacznij kierować wszystkich klientów do funkcji 2 generacji, aktualizując kod klienta o nazwa lub adres URL funkcji 2 generacji.
    2. W przypadku reguł działających w tle funkcje 1 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 w interfejsie wiersza poleceń Firebase.
    1. Opcjonalnie zmień nazwę funkcji 2 generacji, aby pasowała do nazwy funkcji 1 generacji.