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

Aplikasi yang 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 guna 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.

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

Jika Anda memiliki konfigurasi lingkungan yang ada dengan functions.config, migrasikan konfigurasi ini sebagai bagian dari upgrade ke generasi ke-2.

functions.config API yang tidak digunakan lagi akan dihentikan pada Maret 2027. Setelah tanggal tersebut, deployment dengan functions.config akan gagal.

Untuk mencegah kegagalan deployment, migrasikan konfigurasi Anda ke Cloud Secret Manager menggunakan Firebase CLI. Cara ini sangat direkomendasikan karena merupakan cara paling efisien dan aman untuk memigrasikan konfigurasi Anda.

  1. Konfigurasi ekspor dengan Firebase CLI

    Gunakan perintah config export untuk mengekspor konfigurasi lingkungan yang ada ke secret baru di 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```
    
  2. Perbarui kode fungsi untuk mengikat secret

    Untuk menggunakan konfigurasi yang disimpan di secret baru di Cloud Secret Manager, gunakan defineJsonSecret API di sumber fungsi Anda. Selain itu, pastikan secret terikat ke semua fungsi yang memerlukannya.

    Sebelum

    const functions = require("firebase-functions/v1");
    
    exports.myFunction = functions.https.onRequest((req, res) => {
      const apiKey = functions.config().someapi.key;
      // ...
    });
    

    Setelah

    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;
        // ...
    });
    
  3. Deploy Fungsi

    Deploy fungsi yang telah diupdate untuk menerapkan perubahan dan mengikat izin secret.

    firebase deploy --only functions:<your-function-name>
    

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

Memperbarui akun layanan default (opsional)

Meskipun fungsi generasi ke-1 menggunakan akun layanan default Google App Engine untuk mengizinkan akses ke Firebase API, fungsi generasi ke-2 menggunakan akun layanan default Compute Engine. Perbedaan ini dapat menyebabkan masalah izin untuk fungsi yang dimigrasikan ke generasi ke-2 jika Anda telah memberikan izin khusus ke akun layanan generasi ke-1. Jika belum mengubah izin akun layanan, Anda dapat melewati langkah ini.

Solusi yang direkomendasikan adalah menetapkan akun layanan default App Engine generasi ke-1 yang ada secara eksplisit ke fungsi yang ingin Anda migrasikan ke generasi ke-2, sehingga menggantikan default generasi ke-2. Anda dapat melakukannya dengan memastikan setiap fungsi yang dimigrasikan menetapkan nilai yang benar untuk 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) => {
  // ...
});

Atau, Anda dapat memastikan untuk mengubah detail akun layanan agar sesuai dengan semua izin yang diperlukan di akun layanan default App Engine (untuk Generasi ke-1) dan akun layanan default Compute Engine (untuk Generasi ke-2).

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 konkurensi dapat meningkatkan performa dan mengurangi biaya fungsi. Pelajari lebih lanjut konkurensi 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.