W przypadku aplikacji korzystających z funkcji 1 generacji warto rozważyć migrację do funkcji 2 generacji zgodnie z instrukcjami podanymi w tym przewodniku. Funkcje 2 generacji korzystają z Cloud Run, aby zapewnić lepszą wydajność, lepszą konfigurację, lepsze monitorowanie i inne korzyści.
Przykłady w tym dokumencie 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 TypeScriptu.
Proces migracji
Funkcje 1 generacji i 2 generacji mogą współistnieć w tym samym pliku źródłowym. Dzięki temu możesz przenosić bazę kodu fragment po fragmencie, gdy będziesz gotowy. Pamiętaj jednak, że takie łączenie pakietów nie działa w obrębie pojedynczej, odrębnej funkcji.
Zalecamy przenoszenie po jednej funkcji naraz i przeprowadzanie testów oraz weryfikacji przed przejściem do kolejnej.
Sprawdzanie wersji interfejsu wiersza poleceń Firebase i firebase-functions
Upewnij się, że używasz interfejsu wiersza poleceń Firebase w wersji co najmniej 12.00 i firebase-functions w wersji co najmniej 4.3.0. Wszystkie nowsze wersje będą obsługiwać zarówno funkcje 2 generacji, jak i 1 generacji.
Aktualizowanie importów
Funkcje 2 generacji są importowane z podpakietu v2 w pakiecie SDK firebase-functions. Ta inna ścieżka importu to wszystko, czego interfejs wiersza poleceń Firebase potrzebuje, aby określić, czy kod funkcji ma zostać wdrożony jako funkcja 1 czy 2 generacji.
Podpakiet v2 jest modułowy i zalecamy importowanie tylko konkretnego modułu, którego potrzebujesz.
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 aktywatorów
Pakiet SDK drugiej generacji preferuje importy modułowe, dlatego zaktualizuj definicje aktywatorów, aby odzwierciedlały zmienione importy z poprzedniego kroku.
Zmieniły się argumenty przekazywane do wywołań zwrotnych w przypadku niektórych reguł. W tym przykładzie argumenty wywołania zwrotnego onDocumentCreated zostały skonsolidowane w jeden obiekt event. Dodatkowo niektóre reguły mają wygodne nowe funkcje konfiguracji, takie jak opcja cors reguły 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) => {
/* ... */
});
Minimalizowanie nakładu pracy związanego z przepisywaniem kodu dzięki destrukturyzacji JavaScriptu
Jeśli funkcje mają złożone treści, które w dużym stopniu opierają się na kontekście 1 generacji lub parametrach dostawcy (takich jak message lub snapshot), możesz użyć pomocników zgodności 1 generacji wbudowanych w pakiet SDK 2 generacji.
Pakiet SDK 2 generacji automatycznie uzupełnia obiekt zdarzenia o metody pobierające, które pasują do sygnatur 1 generacji. Dzięki temu możesz używać destrukturyzacji JavaScriptu do wyodrębniania tych właściwości bezpośrednio w sygnaturze modułu obsługi, co minimalizuje potrzebę przepisywania logiki funkcji.
Informacje o mapowaniu dostawców
| Dostawca | Argumenty funkcji 1 generacji | Destrukturyzacja zdarzeń z poprawkami w 2 generacji |
| Pub/Sub | (message, context)
|
({ message, context }) => { ... }
|
| Cloud Firestore | (snapshot, context)
|
({ snapshot, context }) => { ... }
|
| Cloud Storage | (object, context)
|
({ object, context }) => { ... }
|
| Realtime Database | (snapshot, context)
|
({ snapshot, context }) => { ... }
|
| Remote Config | (version, context)
|
({ version, context }) => { ... }
|
| Cloud Scheduler | (context)
|
({ context }) => { ... }
|
| Kolejka zadań | (data, context)
|
({ data, context }) => { ... }
|
Wcześniej (1 generacja):
export const myPubSubV1 = functions.pubsub.topic("my-topic").onPublish((message, context) => {
const data = message.json;
const eventId = context.eventId;
// ... rest of the logic
});
Nowa alternatywa (2 generacja z destrukturyzacją):
import { onMessagePublished } from "firebase-functions/v2/pubsub";
export const myPubSubV2 = onMessagePublished("my-topic", ({ message, context }) => {
// No need to change the function body!
const data = message.json; // Uses v1 Message wrapper
const eventId = context.eventId; // Uses v1 EventContext map
// ... rest of the logic
});
Używanie konfiguracji z parametrami
Funkcje 2 generacji nie obsługują już functions.config na rzecz bezpieczniejszego interfejsu do deklaratywnego definiowania parametrów konfiguracji w bazie kodu.
Dzięki nowemu modułowi params interfejs wiersza poleceń blokuje wdrażanie, dopóki wszystkie parametry nie będą miały prawidłowej wartości. Dzięki temu funkcja nie zostanie wdrożona z brakującą konfiguracją.
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());
// ...
}
);
Jeśli masz już konfigurację środowiska z functions.config, przenieś ją w ramach uaktualnienia do drugiej generacji.
Interfejs API functions.config został wycofany i zostanie wyłączony w marcu 2027 r.
Po tym terminie wdrożenia z użyciem functions.config będą kończyć się niepowodzeniem.
Aby zapobiec błędom wdrażania, przenieś konfigurację do Cloud Secret Managera za pomocą interfejsu wiersza poleceń Firebase. Jest to zdecydowanie zalecany, najbardziej wydajny i bezpieczny sposób migracji konfiguracji.
Eksportowanie konfiguracji za pomocą interfejsu wiersza poleceń Firebase
Użyj polecenia
config export, aby wyeksportować istniejącą konfigurację środowiska do nowego obiektu tajnego w Cloud Secret Manager:$ firebase functions:config:export i This command retrieves your Runtime Config values (accessed via functions.config()) and exports them as a Secret Manager secret. i Fetching your existing functions.config() from your project... ✔ Fetched your existing functions.config(). i Configuration to be exported: ⚠ This may contain sensitive data. Do not share this output. { ... } ✔ What would you like to name the new secret for your configuration? RUNTIME_CONFIG ✔ Created new secret version projects/project/secrets/RUNTIME_CONFIG/versions/1```Aktualizowanie kodu funkcji w celu powiązania obiektów tajnych
Aby użyć konfiguracji przechowywanej w nowym obiekcie tajnym w usłudze Cloud Secret Manager, użyj interfejsu API
defineJsonSecretw źródle funkcji. Upewnij się też, że obiekty tajne są powiązane ze wszystkimi funkcjami, które ich potrzebują.Przed
const functions = require("firebase-functions/v1"); exports.myFunction = functions.https.onRequest((req, res) => { const apiKey = functions.config().someapi.key; // ... });Po
const { onRequest } = require("firebase-functions/v2/https"); const { defineJsonSecret } = require("firebase-functions/params"); const config = defineJsonSecret("RUNTIME_CONFIG"); exports.myFunction = onRequest( // Bind secret to your function { secrets: [config] }, (req, res) => { // Access secret values via .value() const apiKey = config.value().someapi.key; // ... });Wdrażanie funkcji
Wdróż zaktualizowane funkcje, aby zastosować zmiany i powiązać uprawnienia do obiektów tajnych.
firebase deploy --only functions:<your-function-name>
Ustawianie opcji środowiska wykonawczego
Konfiguracja opcji środowiska wykonawczego zmieniła się między 1 i 2 generacją. W 2 generacji dodano 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) => {
/* ... */
});
Aktualizowanie domyślnego konta usługi (opcjonalnie)
Funkcje 1 generacji używają domyślnego konta usługi Google App Engine do autoryzowania dostępu do interfejsów API Firebase, a funkcje 2 generacji używają domyślnego konta usługi Compute Engine. Ta różnica może prowadzić do problemów z uprawnieniami w przypadku funkcji przeniesionych do 2 generacji, jeśli przyznano specjalne uprawnienia kontu usługi 1 generacji. Jeśli nie zmieniono żadnych uprawnień konta usługi, możesz pominąć ten krok.
Zalecanym rozwiązaniem jest jawne przypisanie istniejącego domyślnego konta usługi App Engine 1 generacji do funkcji, które chcesz przenieść do 2 generacji, i zastąpienie domyślnego konta usługi 2 generacji. Możesz to zrobić, upewniając się, że każda przeniesiona funkcja ustawia prawidłową wartość dla serviceAccountEmail:
const {onRequest} = require("firebase-functions/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
const {setGlobalOptions} = require("firebase-functions");
// Use the App Engine default service account for all functions
setGlobalOptions({serviceAccountEmail: '<my-project-number>@<wbr>appspot.gserviceaccount.com'});
// Now I use the App Engine default service account.
exports.date = onRequest({cors: true}, (req, res) => {
// ...
});
// I do too!
exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
// ...
});
Możesz też zmodyfikować szczegóły konta usługi, aby dopasować wszystkie niezbędne uprawnienia zarówno na domyślnym koncie usługi App Engine (w przypadku 1 generacji), jak i na domyślnym koncie usługi Compute Engine (w przypadku 2 generacji).
Używanie równoczesności
Istotną zaletą funkcji 2 generacji jest możliwość obsługi przez pojedynczą instancję funkcji więcej niż jednego żądania 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) => {
// ...
});
Dostrajanie równoczesności może poprawić wydajność i obniżyć koszty funkcji. Więcej informacji o równoczesności znajdziesz w artykule Zezwalanie na żądania równoczesne.
Sprawdzanie użycia zmiennych globalnych
Funkcje 1 generacji napisane bez uwzględnienia równoczesności mogą używać zmiennych globalnych, które są ustawiane i odczytywane przy każdym żądaniu. Gdy równoczesność jest włączona, a pojedyncza instancja zaczyna obsługiwać wiele żądań jednocześnie, może to powodować błędy w funkcji, ponieważ żądania równoczesne 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 funkcji 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ż nie pozwala ono na korzystanie z zalet funkcji 2 generacji pod względem wydajności. Zamiast tego sprawdź użycie zmiennych globalnych w funkcjach i usuń te tymczasowe ustawienia, gdy będziesz gotowy.
Przenoszenie ruchu do nowych funkcji 2 generacji
Podobnie jak w przypadku zmiany regionu funkcji lub typu aktywatora, musisz nadać funkcji 2 generacji nową nazwę i powoli przekierować do niej ruch.
Nie można uaktualnić funkcji z 1 generacji 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.
Strategia migracji zależy od typu aktywatora używanego przez funkcję.
Migracja wywoływalnych aktywatorów, aktywatorów kolejki zadań i aktywatorów HTTP
Te aktywatory są wywołaniami bezpośrednimi. Funkcja 2 generacji będzie miała nową nazwę (i nowy adres URL w przypadku aktywatorów HTTP), więc możesz przenieść ruch, aktualizując klientów.
- Zmień nazwę funkcji w kodzie (np. zmień
myCallablenamyCallableV2). - wdrożyć funkcję; Teraz działają zarówno funkcje 1 generacji, jak i 2 generacji.
- Zaktualizuj kod klienta lub element wywołujący, aby wskazywał nazwę lub adres URL nowej funkcji 2 generacji.
- Gdy cały ruch zostanie przeniesiony do nowej funkcji, usuń funkcję 1 generacji za pomocą polecenia
firebase functions:deletew interfejsie wiersza poleceń Firebase.
Migracja aktywatorów działających w tle
Aktywatory działające w tle (takie jak aktywatory Pub/Sub, Cloud Firestore i Cloud Storage) reagują na zdarzenia w projekcie. Aby uniknąć utraty zdarzeń podczas przenoszenia, musisz tymczasowo uruchomić funkcje 1 generacji i 2 generacji obok siebie.
W okresie przejściowym obie funkcje będą aktywowane przez to samo zdarzenie. Oznacza to, że logika biznesowa będzie uruchamiana 2 razy na zdarzenie. Zanim przejdziesz dalej, upewnij się, że funkcja jest idempotentna.
Dodaj funkcję 2 generacji obok funkcji 1 generacji, zachowując istniejącą funkcję 1 generacji w kodzie i dodając funkcję 2 generacji, która nasłuchuje tego samego źródła zdarzeń.
import * as functions from "firebase-functions/v1"; import { onMessagePublished } from "firebase-functions/v2/pubsub"; // --- Existing 1st gen function --- export const myPubSub = functions.pubsub.topic("my-topic").onPublish((message, context) => { console.log("V1 handler running for event:", context.eventId); // ... existing v1 function logic ... }); // --- New v2 passthrough function --- export const myPubSubV2 = onMessagePublished("my-topic", async ({ message, context }) => { console.log("v2 handler triggering V1 for event:", context.eventId); // Call the v1 function's handler await myPubSub.run(message, context); });Uruchom
firebase deploy. Obie funkcje są teraz aktywne i nasłuchują tych samych zdarzeń.Sprawdź, czy funkcja 2 generacji odbiera ruch. Monitoruj logi obu funkcji. Upewnij się, że funkcja 2 generacji jest wywoływana w przypadku wszystkich zdarzeń i że wywołania są realizowane.
Gdy upewnisz się, że funkcja działa prawidłowo, przenieś rzeczywistą logikę biznesową z funkcji 1 generacji do treści funkcji 2 generacji. Jeśli użyto metody przekazywania, usuń wywołanie
myPubSub.run().import * as functions from "firebase-functions/v1"; import { onMessagePublished } from "firebase-functions/v2/pubsub"; // --- Existing v1 function (to be removed next) --- export const myPubSub = functions.pubsub.topic("my-topic").onPublish((message, context) => { console.log("v1 handler running for event:", context.eventId); // ... existing v1 function logic ... }); // --- New v2 function with full logic --- export const myPubSubV2 = onMessagePublished("my-topic", ({ message, context }) => { console.log("v2 handler running for event:", context.eventId); // ... existing v1 function logic WAS MOVED HERE ... });Wdróż tę zmianę.
Usuń definicję funkcji 1 generacji z kodu i wdróż ponownie. Interfejs wiersza poleceń poprosi Cię o usunięcie funkcji 1 generacji z Google Cloud.