अपने एक्सटेंशन के लाइफ़साइकल इवेंट मैनेज करना

आपके एक्सटेंशन में Cloud Tasks फ़ंक्शन शामिल हो सकते हैं. ये फ़ंक्शन तब ट्रिगर होते हैं, जब एक्सटेंशन इंस्टेंस, लाइफ़साइकल के इन इवेंट में से किसी एक से गुज़रता है:

  • एक्सटेंशन का कोई इंस्टेंस इंस्टॉल किया गया हो
  • एक्सटेंशन के किसी इंस्टेंस को नए वर्शन में अपडेट किया गया है
  • एक्सटेंशन इंस्टेंस के कॉन्फ़िगरेशन में बदलाव किया गया है

इस सुविधा का सबसे अहम इस्तेमाल, डेटा बैकफ़िल करना है. उदाहरण के लिए, मान लें कि आपको एक ऐसा एक्सटेंशन बनाना है जो Cloud Storage बकेट में अपलोड की गई इमेज की थंबनेल झलक जनरेट करता है. आपके एक्सटेंशन का मुख्य काम, onFinalize Cloud Storage इवेंट से ट्रिगर होने वाले फ़ंक्शन में किया जाएगा. हालांकि, एक्सटेंशन इंस्टॉल होने के बाद अपलोड की गई इमेज ही प्रोसेस की जाएंगी. एक्सटेंशन में onInstall लाइफ़साइकल इवेंट से ट्रिगर होने वाला फ़ंक्शन शामिल करके, एक्सटेंशन इंस्टॉल होने पर पहले से मौजूद किसी भी इमेज की थंबनेल झलक जनरेट की जा सकती है.

लाइफ़साइकल इवेंट ट्रिगर के इस्तेमाल के कुछ अन्य उदाहरणों में ये शामिल हैं:

  • ऐप्लिकेशन इंस्टॉल होने के बाद सेटअप को ऑटोमेट करना (डेटाबेस रिकॉर्ड बनाना, इंडेक्सिंग करना वगैरह)
  • अगर आपको ऐसे बदलाव पब्लिश करने हैं जो पुराने वर्शन के साथ काम नहीं करते हैं, तो अपडेट होने पर डेटा को अपने-आप माइग्रेट करें

कम समय में पूरे होने वाले लाइफ़साइकल इवेंट हैंडलर

अगर आपका टास्क, ज़्यादा से ज़्यादा Cloud Functions अवधि (पहली जनरेशन के API का इस्तेमाल करके नौ मिनट) के अंदर पूरा हो सकता है, तो लाइफ़साइकल इवेंट हैंडलर को एक ऐसे फ़ंक्शन के तौर पर लिखा जा सकता है जो टास्क क्यू onDispatch इवेंट पर ट्रिगर होता है:

export const myTaskFunction = functions.tasks.taskQueue()
  .onDispatch(async () => {
    // Complete your lifecycle event handling task.
    // ...

    // When processing is complete, report status to the user (see below).
  });

इसके बाद, अपने एक्सटेंशन की extension.yaml फ़ाइल में, यह काम करें:

  1. अपने फ़ंक्शन को taskQueueTriggerप्रॉपर्टी सेट के साथ एक्सटेंशन रिसॉर्स के तौर पर रजिस्टर करें. अगर आपने खाली मैप ({}) के लिए taskQueueTrigger सेट किया है, तो आपका एक्सटेंशन डिफ़ॉल्ट सेटिंग का इस्तेमाल करके Cloud Tasks कतार उपलब्ध कराएगा. आपके पास इन सेटिंग को अपनी ज़रूरत के हिसाब से बदलने का विकल्प होता है.

    resources:
      - name: myTaskFunction
        type: firebaseextensions.v1beta.function
        description: >-
          Describe the task performed when the function is triggered by a lifecycle
          event
        properties:
          location: ${LOCATION}
          taskQueueTrigger: {}
    
  2. अपने फ़ंक्शन को एक या उससे ज़्यादा लाइफ़साइकल इवेंट के लिए हैंडलर के तौर पर रजिस्टर करें:

    resources:
      - ...
    lifecycleEvents:
      onInstall:
        function: myTaskFunction
        processingMessage: Resizing your existing images
      onUpdate:
        function: myOtherTaskFunction
        processingMessage: Setting up your extension
      onConfigure:
        function: myOtherTaskFunction
        processingMessage: Setting up your extension
    
    

    इनमें से किसी भी इवेंट के लिए फ़ंक्शन रजिस्टर किए जा सकते हैं: onInstall, onUpdate, और onConfigure. इन सभी इवेंट को ट्रैक करना ज़रूरी नहीं है.

  3. हमारा सुझाव है: अगर आपके एक्सटेंशन को काम करने के लिए प्रोसेसिंग टास्क की ज़रूरत नहीं है, तो उपयोगकर्ता के हिसाब से कॉन्फ़िगर किया गया पैरामीटर जोड़ें. इससे उपयोगकर्ता यह चुन पाएंगे कि उन्हें इसे चालू करना है या नहीं.

    उदाहरण के लिए, इस तरह का पैरामीटर जोड़ें:

    params:
      - param: DO_BACKFILL
        label: Backfill existing images
        description: >
          Should existing, unresized images in the Storage bucket be resized as well?
        type: select
        options:
          - label: Yes
            value: true
          - label: No
            value: false
    

    साथ ही, अपने फ़ंक्शन में, अगर पैरामीटर को false पर सेट किया गया है, तो फ़ंक्शन को जल्दी बंद करें:

    export const myTaskFunction = functions.tasks.taskQueue()
      .onDispatch(async () => {
        if (!process.env.DO_BACKFILL) {
          await runtime.setProcessingState(
            "PROCESSING_COMPLETE",
            "Existing images were not resized."
          );
          return;
        }
        // Complete your lifecycle event handling task.
        // ...
      });
    

लंबे समय तक चलने वाले टास्क पूरे करना

अगर आपका टास्क, ज़्यादा से ज़्यादा Cloud Functions अवधि में पूरा नहीं हो सकता, तो टास्क को सबटास्क में बांटें. इसके बाद, Admin SDK के TaskQueue.enqueue() तरीके का इस्तेमाल करके, हर सबटास्क को क्रम से पूरा करें.

उदाहरण के लिए, मान लें कि आपको Cloud Firestore का डेटा बैकफ़िल करना है. क्वेरी कर्सर का इस्तेमाल करके, दस्तावेज़ के कलेक्शन को हिस्सों में बांटा जा सकता है. किसी चंक को प्रोसेस करने के बाद, शुरुआती ऑफ़सेट को आगे बढ़ाएं और फ़ंक्शन को फिर से शुरू करें. इसे नीचे दिखाया गया है:

import { getFirestore } from "firebase-admin/firestore";
import { getFunctions } from "firebase-admin/functions";

exports.backfilldata = functions.tasks.taskQueue().onDispatch(async (data) => {
  // When a lifecycle event triggers this function, it doesn't pass any data,
  // so an undefined offset indicates we're on our first invocation and should
  // start at offset 0. On subsequent invocations, we'll pass an explicit
  // offset.
  const offset = data["offset"] ?? 0;

  // Get a batch of documents, beginning at the offset.
  const snapshot = await getFirestore()
    .collection(process.env.COLLECTION_PATH)
    .startAt(offset)
    .limit(DOCS_PER_BACKFILL)
    .get();
  // Process each document in the batch.
  const processed = await Promise.allSettled(
    snapshot.docs.map(async (documentSnapshot) => {
      // Perform the processing.
    })
  );

  // If we processed a full batch, there are probably more documents to
  // process, so enqueue another invocation of this function, specifying
  // the offset to start with.
  //
  // If we processed less than a full batch, we're done.
  if (processed.length == DOCS_PER_BACKFILL) {
    const queue = getFunctions().taskQueue(
      "backfilldata",
      process.env.EXT_INSTANCE_ID
    );
    await queue.enqueue({
      offset: offset + DOCS_PER_BACKFILL,
    });
  } else {
      // Processing is complete. Report status to the user (see below).
  }
});

पिछले सेक्शन में बताए गए तरीके से, अपने extension.yaml में फ़ंक्शन जोड़ें.

रिपोर्टिंग की स्थिति

जब सभी प्रोसेसिंग फ़ंक्शन पूरे हो जाएं, तब Admin SDK के एक्सटेंशन रनटाइम के तरीकों का इस्तेमाल करके, टास्क की स्थिति की रिपोर्ट करें. ये फ़ंक्शन, सही तरीके से पूरे हो सकते हैं या इनमें कोई गड़बड़ी हो सकती है. उपयोगकर्ता, इस स्थिति को Firebase कंसोल में एक्सटेंशन की ज़्यादा जानकारी वाले पेज पर देख सकते हैं.

प्रोसेस पूरी हो गई और नुकसान न पहुंचाने वाली गड़बड़ियां हुईं

एक्सटेंशन के सही तरीके से काम करने और नुकसान न पहुंचाने वाली गड़बड़ियों (ऐसी गड़बड़ियां जिनकी वजह से एक्सटेंशन काम नहीं करता) की रिपोर्ट करने के लिए, Admin SDK के setProcessingState() एक्सटेंशन रनटाइम तरीके का इस्तेमाल करें:

import { getExtensions } from "firebase-admin/extensions";

// ...

getExtensions().runtime().setProcessingState(processingState, message);

ये स्टेटस सेट किए जा सकते हैं:

नुकसान न पहुंचाने वाली गड़बड़ियों की स्थितियां
PROCESSING_COMPLETE

इस कुकी का इस्तेमाल, टास्क पूरा होने की जानकारी देने के लिए किया जाता है. उदाहरण:

getExtensions().runtime().setProcessingState(
  "PROCESSING_COMPLETE",
  `Backfill complete. Successfully processed ${numSuccess} documents.`
);
PROCESSING_WARNING

इसका इस्तेमाल, कुछ हद तक पूरा होने की जानकारी देने के लिए किया जाता है. उदाहरण:

getExtensions().runtime().setProcessingState(
  "PROCESSING_WARNING",
  `Backfill complete. ${numSuccess} documents processed successfully.`
    + ` ${numFailed} documents failed to process. ${listOfErrors}.`
    + ` ${instructionsToFixTheProblem}`
);
PROCESSING_FAILED

इस विकल्प का इस्तेमाल उन गड़बड़ियों की शिकायत करने के लिए करें जिनकी वजह से टास्क पूरा नहीं हो पाता. हालांकि, इससे एक्सटेंशन का इस्तेमाल नहीं किया जा सकता. उदाहरण:

getExtensions().runtime().setProcessingState(
  "PROCESSING_FAILED",
  `Backfill failed. ${errorMsg} ${optionalInstructionsToFixTheProblem}.`
);

अगर एक्सटेंशन का इस्तेमाल नहीं किया जा सकता है, तो गड़बड़ियों की शिकायत करने के लिए, setFatalError() पर कॉल करें.

NONE

इस विकल्प का इस्तेमाल, टास्क की स्थिति को हटाने के लिए किया जाता है. इसका इस्तेमाल, कंसोल से स्थिति के मैसेज को मिटाने के लिए किया जा सकता है. उदाहरण के लिए, PROCESSING_COMPLETE सेट करने के बाद कुछ समय बीत जाने पर. उदाहरण:

getExtensions().runtime().setProcessingState("NONE");

गंभीर गड़बड़ियां

अगर कोई ऐसी गड़बड़ी होती है जिसकी वजह से एक्सटेंशन काम नहीं करता है, तो setFatalError() के साथ इस गंभीर गड़बड़ी की शिकायत करें. उदाहरण के लिए, सेटअप से जुड़ा कोई ज़रूरी टास्क पूरा नहीं हो पाता है:

import { getExtensions } from "firebase-admin/extensions";

// ...

getExtensions().runtime().setFatalError(`Post-installation setup failed. ${errorMessage}`);

टास्क की कतार को ट्यून करना

taskQueueTrigger प्रॉपर्टी को {} पर सेट करने पर, आपका एक्सटेंशन डिफ़ॉल्ट सेटिंग के साथ Cloud Tasks की एक कतार उपलब्ध कराएगा. ऐसा तब होगा, जब एक्सटेंशन का कोई इंस्टेंस इंस्टॉल किया जाएगा. इसके अलावा, टास्क क्यू की एक साथ काम करने की सीमाओं और फिर से कोशिश करने के व्यवहार को बेहतर बनाया जा सकता है. इसके लिए, ये वैल्यू दें:

resources:
  - name: myTaskFunction
    type: firebaseextensions.v1beta.function
    description: >-
      Perform a task when triggered by a lifecycle event
    properties:
      location: ${LOCATION}
      taskQueueTrigger:
        rateLimits:
          maxConcurrentDispatches: 1000
          maxDispatchesPerSecond: 500
        retryConfig:
          maxAttempts: 100  # Warning: setting this too low can prevent the function from running
          minBackoffSeconds: 0.1
          maxBackoffSeconds: 3600
          maxDoublings: 16
lifecycleEvents:
  onInstall: 
    function: myTaskFunction
    processingMessage: Resizing your existing images
  onUpdate:
    function: myTaskFunction
    processingMessage: Setting up your extension
  onConfigure:
    function: myOtherTaskFunction
    processingMessage: Setting up your extension

इन पैरामीटर के बारे में ज़्यादा जानने के लिए, Google Cloud के दस्तावेज़ में Cloud Tasks की सूचियां कॉन्फ़िगर करना लेख पढ़ें.

taskQueue() फ़ंक्शन में टास्क के पैरामीटर पास करके, उन्हें तय करने की कोशिश न करें. इन सेटिंग को अनदेखा कर दिया जाता है. इसके बजाय, extension.yaml में मौजूद कॉन्फ़िगरेशन और कॉन्फ़िगरेशन के डिफ़ॉल्ट का इस्तेमाल किया जाता है.

उदाहरण के लिए, यह काम नहीं करेगा:

export const myBrokenTaskFunction = functions.tasks
  // DON'T DO THIS IN AN EXTENSION! THESE SETTINGS ARE IGNORED.
  .taskQueue({
    retryConfig: {
      maxAttempts: 5,
      minBackoffSeconds: 60,
    },
    rateLimits: {
      maxConcurrentDispatches: 1000,
      maxDispatchesPerSecond: 10,
    },
  })
  .onDispatch(
    // ...
  );

extension.yaml में मौजूद taskQueueTrigger प्रॉपर्टी का इस्तेमाल करके ही, एक्सटेंशन की टास्क कतारों को कॉन्फ़िगर किया जा सकता है.

उदाहरण

आधिकारिक storage-resize-images, firestore-bigquery-export, और firestore-translate-text एक्सटेंशन, डेटा को बैकफ़िल करने के लिए लाइफ़साइकल इवेंट हैंडलर का इस्तेमाल करते हैं.