Mengupgrade fungsi Node.js generasi ke-1 ke generasi ke-2

Aplikasi yang saat ini menggunakan fungsi generasi ke-1 harus mempertimbangkan migrasi ke generasi ke-2 menggunakan petunjuk dalam panduan ini. Fungsi generasi ke-2 menggunakan Cloud Run untuk memberikan performa yang lebih baik, konfigurasi yang lebih baik, pemantauan yang lebih baik, dan masih banyak lagi.

Contoh di halaman ini mengasumsikan bahwa Anda menggunakan JavaScript dengan modul CommonJS (impor gaya require), tetapi prinsip yang sama berlaku untuk JavaScript dengan ESM (impor gaya import … from) dan TypeScript.

Proses migrasi

Fungsi generasi ke-1 dan generasi ke-2 dapat berdampingan dalam file yang sama. Hal ini memungkinkan migrasi bertahap yang mudah, saat Anda siap. Sebaiknya migrasikan satu fungsi dalam satu waktu, lalu lakukan pengujian dan verifikasi sebelum melanjutkan.

Verifikasi Firebase CLI dan versi firebase-function

Pastikan Anda menggunakan setidaknya Firebase CLI versi 12.00 dan firebase-functions versi 4.3.0. Versi yang lebih baru akan mendukung generasi ke-2 serta generasi ke-1.

Memperbarui impor

Fungsi generasi ke-2 diimpor dari subpaket v2 di firebase-functions SDK. Firebase CLI membutuhkan jalur impor yang berbeda untuk menentukan apakah akan men-deploy kode fungsi Anda sebagai fungsi generasi ke-1 atau ke-2.

Subpaket v2 bersifat modular, dan sebaiknya hanya impor modul tertentu yang Anda butuhkan.

Sebelum: generasi ke-1

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

Sesudah: generasi ke-2

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

Memperbarui definisi pemicu

Karena SDK generasi ke-2 mendukung impor modular, perbarui definisi pemicu untuk mencerminkan impor yang diubah dari langkah sebelumnya.

Argumen yang diteruskan ke callback untuk beberapa pemicu telah berubah. Dalam contoh ini, perlu diperhatikan bahwa argumen ke callback onDocumentCreated telah digabungkan ke dalam satu objek event. Selain itu, beberapa pemicu memiliki fitur konfigurasi baru yang praktis, seperti opsi cors pemicu onRequest.

Sebelum: generasi ke-1

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

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

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

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

Menggunakan konfigurasi berparameter

Fungsi generasi ke-2 menghentikan dukungan untuk functions.config dan mendukung antarmuka yang lebih aman untuk menentukan parameter konfigurasi secara deklaratif di dalam codebase Anda. Dengan modul params baru, CLI akan memblokir deployment kecuali jika semua parameter memiliki nilai yang valid, sehingga memastikan bahwa fungsi tidak di-deploy jika tidak memiliki konfigurasi.

Bermigrasi ke subpaket params

Jika menggunakan konfigurasi lingkungan dengan functions.config, Anda dapat memigrasikan konfigurasi yang ada ke konfigurasi berparameter.

Sebelum: generasi ke-1

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

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

  // ...
});

Sesudah: generasi ke-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());

  // ...
});

Menetapkan parameter value

Saat pertama kali melakukan deployment, Firebase CLI akan meminta semua nilai parameter, dan menyimpan nilainya dalam file dotenv. Untuk mengekspor nilai functions.config Anda, jalankan firebase functions:config:export.

Untuk keamanan tambahan, Anda juga dapat menentukan jenis dan aturan validasi parameter.

Kasus khusus: Kunci API

Modul params terintegrasi dengan Cloud Secret Manager, yang menyediakan kontrol akses terperinci ke nilai sensitif seperti kunci API. Lihat parameter secret untuk mengetahui informasi selengkapnya.

Sebelum: generasi ke-1

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

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

Sesudah: generasi ke-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());
    // ...
  }
);

Menetapkan opsi runtime

Konfigurasi opsi runtime telah berubah antara generasi ke-1 dan ke-2. Generasi ke-2 juga menambahkan kemampuan baru untuk menetapkan opsi untuk semua fungsi.

Sebelum: generasi ke-1

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

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

Gunakan permintaan serentak

Keuntungan signifikan dari fungsi generasi ke-2 adalah kemampuan instance fungsi tunggal untuk menayangkan lebih dari satu permintaan sekaligus. Hal ini dapat secara drastis mengurangi jumlah cold start yang dialami pengguna akhir. Secara default, permintaan serentak ditetapkan pada 80, tetapi Anda dapat menetapkannya ke nilai apa pun dari 1 hingga 1.000:

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

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

Menyesuaikan permintaan serentak dapat meningkatkan performa dan mengurangi biaya fungsi. Pelajari lebih lanjut permintaan serentak di Mengizinkan permintaan serentak.

Mengaudit penggunaan variabel global

Fungsi generasi ke-1 yang ditulis tanpa mempertimbangkan konkurensi mungkin menggunakan variabel global yang ditetapkan dan dibaca pada setiap permintaan. Jika konkurensi diaktifkan dan satu instance mulai menangani beberapa permintaan sekaligus, hal ini dapat menimbulkan bug dalam fungsi Anda saat permintaan serentak mulai menetapkan dan membaca variabel global secara bersamaan.

Saat mengupgrade, Anda dapat menetapkan CPU fungsi ke gcf_gen1 dan menetapkan concurrency ke 1 untuk memulihkan perilaku generasi ke-1:

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

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

Namun, tindakan ini tidak direkomendasikan untuk perbaikan jangka panjang, karena menyebabkan performa dari fungsi generasi ke-2 terdampak secara negatif. Sebagai gantinya, lakukan audit terhadap penggunaan variabel global di fungsi Anda, dan hapus setelan sementara ini jika Anda sudah siap.

Memigrasikan traffic ke fungsi generasi ke-2 yang baru

Sama seperti saat mengubah region fungsi atau jenis pemicu, Anda harus memberi nama fungsi generasi ke-2 dan memigrasikan traffic ke sana secara perlahan.

Anda tidak dapat mengupgrade fungsi dari generasi ke-1 ke generasi ke-2 dengan nama yang sama dan menjalankan firebase deploy. Tindakan tersebut akan menghasilkan error:

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

Sebelum mengikuti langkah-langkah ini, pastikan fungsi Anda idempoten, karena versi baru dan versi lama fungsi akan berjalan pada waktu yang sama selama perubahan. Misalnya, jika Anda memiliki fungsi generasi ke-1 yang merespons peristiwa operasi tulis di Firestore, pastikan merespons penulisan dua kali, sekali dengan fungsi generasi ke-1 dan sekali dengan fungsi generasi ke-2, sebagai respons terhadap peristiwa tersebut, keluar dari aplikasi Anda di status yang konsisten.

  1. Ganti nama fungsi di kode fungsi Anda. Misalnya, ganti nama resizeImage menjadi resizeImageSecondGen.
  2. Deploy fungsi, sehingga fungsi generasi ke-1 dan fungsi generasi ke-2 yang asli sedang berjalan.
    1. Dalam kasus pemicu callable, Task Queue, dan HTTP, mulai arahkan semua klien ke fungsi generasi ke-2 dengan memperbarui kode klien dengan nama atau URL fungsi generasi ke-2.
    2. Dengan pemicu latar belakang, fungsi generasi ke-1 dan generasi ke-2 akan langsung merespons setiap peristiwa setelah deployment.
  3. Saat semua traffic dimigrasikan, hapus fungsi generasi ke-1 menggunakan perintah firebase functions:delete firebase CLI.
    1. ATau, ganti nama fungsi generasi ke-2 agar cocok dengan nama fungsi generasi ke-1.