सलाह और सुझाव

इस दस्तावेज़ में, Cloud Functions को डिज़ाइन करने, लागू करने, टेस्ट करने, और डिप्लॉय करने के सबसे सही तरीके बताए गए हैं.

सही जवाब

इस सेक्शन में, Cloud Functions को डिज़ाइन और लागू करने के सबसे सही तरीकों के बारे में बताया गया है.

आइडमपोटेंट फ़ंक्शन लिखना

आपके फ़ंक्शन को एक जैसा नतीजा देना चाहिए, भले ही उन्हें कई बार कॉल किया गया हो. इससे, अगर कोड के बीच में पिछला अनुरोध पूरा नहीं हो पाता है, तो फिर से अनुरोध किया जा सकता है. ज़्यादा जानकारी के लिए, इवेंट के हिसाब से काम करने वाले फ़ंक्शन को फिर से ट्रिगर करना लेख पढ़ें.

बैकग्राउंड में चलने वाली गतिविधियां शुरू न करें

बैकग्राउंड में होने वाली गतिविधि का मतलब है, फ़ंक्शन के बंद होने के बाद होने वाली कोई भी गतिविधि. फ़ंक्शन के नतीजे दिखाने या पूरा होने का सिग्नल देने के बाद, फ़ंक्शन को शुरू करने की प्रोसेस पूरी हो जाती है. जैसे, Node.js इवेंट-ड्रिवन फ़ंक्शन में callback आर्ग्युमेंट को कॉल करके. ग्रेसफ़ुल टर्मिनेशन के बाद चलाया गया कोई भी कोड, सीपीयू को ऐक्सेस नहीं कर सकता. साथ ही, वह आगे नहीं बढ़ेगा.

इसके अलावा, जब एक ही एनवायरमेंट में बाद में कोई इनवोकेशन लागू किया जाता है, तो आपकी बैकग्राउंड गतिविधि फिर से शुरू हो जाती है. इससे नए इनवोकेशन में रुकावट आती है. इसकी वजह से, आपको अनचाहे नतीजे मिल सकते हैं और ऐसी गड़बड़ियां हो सकती हैं जिनका विश्लेषण करना मुश्किल हो. किसी फ़ंक्शन के बंद होने के बाद नेटवर्क को ऐक्सेस करने पर, आम तौर पर कनेक्शन रीसेट हो जाते हैं (ECONNRESET गड़बड़ी का कोड).

बैकग्राउंड में होने वाली गतिविधि का पता, अलग-अलग इन्वोकेशन के लॉग से लगाया जा सकता है. इसके लिए, उस लाइन के बाद लॉग की गई किसी भी चीज़ को ढूंढें जिसमें यह बताया गया हो कि इन्वोकेशन खत्म हो गया है. बैकग्राउंड में होने वाली गतिविधि कभी-कभी कोड में गहराई से छिपी हो सकती है. खास तौर पर, जब कॉलबैक या टाइमर जैसे एसिंक्रोनस ऑपरेशन मौजूद हों. अपने कोड की समीक्षा करके पक्का करें कि फ़ंक्शन को बंद करने से पहले, सभी एसिंक्रोनस ऑपरेशन पूरे हो जाएं.

कुछ समय के लिए सेव की गई फ़ाइलें हमेशा मिटाएं

टेंपररी डायरेक्ट्री में मौजूद लोकल डिस्क स्टोरेज, इन-मेमोरी फ़ाइल सिस्टम है. आपकी लिखी गई फ़ाइलें, आपके फ़ंक्शन के लिए उपलब्ध मेमोरी का इस्तेमाल करती हैं. साथ ही, कभी-कभी ये फ़ाइलें, फ़ंक्शन को कॉल करने के बीच में भी बनी रहती हैं. इन फ़ाइलों को साफ़ तौर पर न मिटाने से, मेमोरी खत्म होने की गड़बड़ी हो सकती है. साथ ही, इसके बाद कोल्ड स्टार्ट हो सकता है.

किसी फ़ंक्शन के लिए इस्तेमाल की गई मेमोरी देखने के लिए, Google Cloud Console में फ़ंक्शन की सूची में जाकर उसे चुनें. इसके बाद, मेमोरी का इस्तेमाल प्लॉट चुनें.

अगर आपको लंबे समय तक स्टोरेज का ऐक्सेस चाहिए, तो Cloud Storage या एनएफ़एस वॉल्यूम के साथ Cloud Runवॉल्यूम माउंट का इस्तेमाल करें.

पाइपलाइनिंग का इस्तेमाल करके, बड़ी फ़ाइलों को प्रोसेस करते समय मेमोरी की ज़रूरत को कम किया जा सकता है. उदाहरण के लिए, Cloud Storage पर किसी फ़ाइल को प्रोसेस करने के लिए, रीड स्ट्रीम बनाई जा सकती है. इसके बाद, उसे स्ट्रीम पर आधारित प्रोसेस से पास किया जा सकता है और आउटपुट स्ट्रीम को सीधे Cloud Storage में लिखा जा सकता है.

Functions Framework

यह पक्का करने के लिए कि सभी एनवायरमेंट में एक जैसी डिपेंडेंसी इंस्टॉल हों, हमारा सुझाव है कि आप अपने पैकेज मैनेजर में फ़ंक्शन फ़्रेमवर्क लाइब्रेरी को शामिल करें. साथ ही, डिपेंडेंसी को फ़ंक्शन फ़्रेमवर्क के किसी खास वर्शन पर पिन करें.

इसके लिए, काम की लॉक फ़ाइल में अपना पसंदीदा वर्शन शामिल करें. उदाहरण के लिए, Node.js के लिए package-lock.json या Python के लिए requirements.txt.

अगर फ़ंक्शन फ़्रेमवर्क को साफ़ तौर पर डिपेंडेंसी के तौर पर नहीं दिखाया गया है, तो बिल्ड प्रोसेस के दौरान इसे अपने-आप जोड़ दिया जाएगा. इसके लिए, उपलब्ध सबसे नए वर्शन का इस्तेमाल किया जाएगा.

टूल

इस सेक्शन में, Cloud Functions को लागू करने, टेस्ट करने, और उससे इंटरैक्ट करने के लिए टूल इस्तेमाल करने के दिशा-निर्देश दिए गए हैं.

लोकल डेवलपमेंट

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

Firebase डेवलपर, Firebase CLI Cloud Functions एमुलेटर का इस्तेमाल कर सकते हैं.

शुरुआत के दौरान डिप्लॉयमेंट टाइम आउट से बचना

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

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

टाइम आउट से बचने के लिए, इनमें से कोई एक तरीका अपनाएं:

डिप्लॉयमेंट के दौरान, शुरू करने वाले कोड को चलने से रोकने के लिए, onInit() हुक का इस्तेमाल करें. onInit() हुक के अंदर मौजूद कोड सिर्फ़ तब चलेगा, जब फ़ंक्शन को Cloud Run के फ़ंक्शन पर डिप्लॉय किया जाएगा. यह डिप्लॉयमेंट प्रोसेस के दौरान नहीं चलेगा.

Node.js

const { onInit } = require('firebase-functions/v2/core');
const { onRequest } = require('firebase-functions/v2/https');

// Example of a slow initialization task
function slowInitialization() {
  // Simulate a long-running operation (e.g., loading a large model, network request).
  return new Promise(resolve => {
      setTimeout(() => {
          console.log("Slow initialization complete");
          resolve("Initialized Value");
      }, 20000); // Simulate a 20-second delay
  });
}
let initializedValue;

onInit(async () => {
  initializedValue = await slowInitialization();
});

exports.myFunction = onRequest((req, res) => {
  // Access the initialized value. It will be ready after the first invocation.
  res.send(`Value: ${initializedValue}`);
});

Python

from firebase_functions.core import init
from firebase_functions import https_fn
import time

# Example of a slow initialization task
def _slow_initialization():
  time.sleep(20)  # Simulate a 20-second delay
  print("Slow initialization complete")
  return "Initialized Value"

_initialized_value = None

@init
def initialize():
  global _initialized_value
  _initialized_value = _slow_initialization()

@https_fn.on_request()
def my_function(req: https_fn.Request) -> https_fn.Response:
  # Access the initialized value. It will be ready after the first invocation.
  return https_fn.Response(f"Value: {_initialized_value}")

(दूसरा तरीका) स्मार्टवॉच को खोजने का समय बढ़ाना

अगर onInit() का इस्तेमाल करने के लिए, कोड को रीफ़ैक्टर नहीं किया जा सकता, तो FUNCTIONS_DISCOVERY_TIMEOUT एनवायरमेंट वैरिएबल का इस्तेमाल करके, सीएलआई के डिप्लॉयमेंट टाइम आउट को बढ़ाया जा सकता है:

$ export FUNCTIONS_DISCOVERY_TIMEOUT=30
$ firebase deploy --only functions

ईमेल भेजने के लिए Sendgrid का इस्तेमाल करना

Cloud Functions, पोर्ट 25 पर आउटबाउंड कनेक्शन की अनुमति नहीं देता. इसलिए, एसएमटीपी सर्वर से असुरक्षित कनेक्शन नहीं बनाए जा सकते. ईमेल भेजने के लिए, SendGrid जैसी तीसरे पक्ष की सेवा का इस्तेमाल करने का सुझाव दिया जाता है. Google Compute Engine के लिए, इंस्टेंस से ईमेल भेजना ट्यूटोरियल में, ईमेल भेजने के अन्य विकल्प देखे जा सकते हैं.

परफ़ॉर्मेंस

इस सेक्शन में, परफ़ॉर्मेंस को ऑप्टिमाइज़ करने के सबसे सही तरीकों के बारे में बताया गया है.

एक साथ कई काम करने की सुविधा का इस्तेमाल करें

कोल्ड स्टार्ट में ज़्यादा समय लगता है. इसलिए, ट्रैफ़िक में बढ़ोतरी के दौरान, हाल ही में शुरू किए गए इंस्टेंस का फिर से इस्तेमाल करना, लोड को मैनेज करने का एक बेहतरीन तरीका है. कॉन्करंसी को सीमित करने से, मौजूदा इंस्टेंस का इस्तेमाल करने की क्षमता सीमित हो जाती है. इस वजह से, कोल्ड स्टार्ट की संख्या बढ़ जाती है.

एक साथ कई अनुरोधों को प्रोसेस करने की क्षमता बढ़ाना इससे हर इंस्टेंस के लिए कई अनुरोधों को प्रोसेस करने में देरी होती है. इससे लोड में होने वाली बढ़ोतरी को मैनेज करना आसान हो जाता है.

डिपेंडेंसी का इस्तेमाल सोच-समझकर करें

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

अगर आपके फ़ंक्शन मॉड्यूल इंपोर्ट करते हैं, तो उन मॉड्यूल को लोड होने में लगने वाला समय, कोल्ड स्टार्ट के दौरान इनवोकेशन लेटेंसी को बढ़ा सकता है. इस इंतज़ार के समय को कम किया जा सकता है. साथ ही, फ़ंक्शन को डिप्लॉय करने में लगने वाले समय को भी कम किया जा सकता है. इसके लिए, डिपेंडेंसी को सही तरीके से लोड करें और उन डिपेंडेंसी को लोड न करें जिनका इस्तेमाल आपका फ़ंक्शन नहीं करता.

आने वाले समय में, ऑब्जेक्ट को फिर से इस्तेमाल करने के लिए ग्लोबल वैरिएबल का इस्तेमाल करना

इस बात की कोई गारंटी नहीं है कि किसी फ़ंक्शन की स्थिति को आने वाले समय में इस्तेमाल करने के लिए सुरक्षित रखा जाएगा. हालांकि, Cloud Functions अक्सर पिछले इनवोकेशन के एक्ज़ीक्यूशन एनवायरमेंट को रीसाइकल करता है. अगर ग्लोबल स्कोप में कोई वैरिएबल तय किया जाता है, तो उसकी वैल्यू को बाद के इन्वोकेशन में फिर से इस्तेमाल किया जा सकता है. इसके लिए, वैल्यू को फिर से कैलकुलेट करने की ज़रूरत नहीं होती.

इस तरह, उन ऑब्जेक्ट को कैश मेमोरी में सेव किया जा सकता है जिन्हें हर बार फ़ंक्शन को शुरू करने पर फिर से बनाना महंगा हो सकता है. ऐसे ऑब्जेक्ट को फ़ंक्शन बॉडी से ग्लोबल स्कोप में ले जाने से, परफ़ॉर्मेंस में काफ़ी सुधार हो सकता है. यहां दिए गए उदाहरण में, हर फ़ंक्शन इंस्टेंस के लिए सिर्फ़ एक बार हैवी ऑब्जेक्ट बनाया जाता है. साथ ही, इसे दिए गए इंस्टेंस तक पहुंचने वाले सभी फ़ंक्शन कॉल के साथ शेयर किया जाता है:

Node.js

console.log('Global scope');
const perInstance = heavyComputation();
const functions = require('firebase-functions');

exports.function = functions.https.onRequest((req, res) => {
  console.log('Function invocation');
  const perFunction = lightweightComputation();

  res.send(`Per instance: ${perInstance}, per function: ${perFunction}`);
});

Python

import time

from firebase_functions import https_fn

# Placeholder
def heavy_computation():
  return time.time()

# Placeholder
def light_computation():
  return time.time()

# Global (instance-wide) scope
# This computation runs at instance cold-start
instance_var = heavy_computation()

@https_fn.on_request()
def scope_demo(request):

  # Per-function scope
  # This computation runs every time this function is called
  function_var = light_computation()
  return https_fn.Response(f"Instance: {instance_var}; function: {function_var}")
  

यह एचटीटीपी फ़ंक्शन, अनुरोध ऑब्जेक्ट (flask.Request) लेता है और जवाब का टेक्स्ट या वैल्यू का कोई भी सेट दिखाता है. इसे make_response का इस्तेमाल करके, Response ऑब्जेक्ट में बदला जा सकता है.

ग्लोबल स्कोप में इंटरनेट कनेक्शन, लाइब्रेरी रेफ़रंस, और एपीआई क्लाइंट ऑब्जेक्ट को कैश मेमोरी में सेव करना बेहद ज़रूरी है. उदाहरणों के लिए, नेटवर्किंग को ऑप्टिमाइज़ करना देखें.

इंस्टेंस की कम से कम संख्या सेट करके, कोल्ड स्टार्ट की संख्या कम करना

डिफ़ॉल्ट रूप से, Cloud Functions आने वाले अनुरोधों की संख्या के आधार पर, इंस्टेंस की संख्या को बढ़ाता है. इस डिफ़ॉल्ट व्यवहार को बदला जा सकता है. इसके लिए, इंस्टेंस की कम से कम संख्या सेट करें, ताकि Cloud Functions अनुरोधों को पूरा करने के लिए तैयार रहे. इंस्टेंस की कम से कम संख्या सेट करने से, आपके ऐप्लिकेशन के कोल्ड स्टार्ट कम हो जाते हैं. अगर आपके ऐप्लिकेशन में इंतज़ार का समय ज़्यादा है, तो हमारा सुझाव है कि आप इंस्टेंस की कम से कम संख्या सेट करें और लोड होने के समय ही शुरू करने की प्रोसेस पूरी करें.

रनटाइम के इन विकल्पों के बारे में ज़्यादा जानने के लिए, स्केलिंग के व्यवहार को कंट्रोल करना देखें.

कोल्ड स्टार्ट और शुरू करने के बारे में जानकारी

ग्लोबल इनिशलाइज़ेशन, लोड होने के समय होता है. इसके बिना, पहले अनुरोध को शुरू करने और मॉड्यूल लोड करने की प्रोसेस पूरी करनी होगी. इससे इंतज़ार का समय बढ़ जाएगा.

हालांकि, ग्लोबल इनिशलाइज़ेशन का असर कोल्ड स्टार्ट पर भी पड़ता है. इस असर को कम करने के लिए, सिर्फ़ उस डेटा को शुरू करें जो पहले अनुरोध के लिए ज़रूरी है. इससे पहले अनुरोध के इंतज़ार के समय को कम से कम रखा जा सकता है.

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

अगर वैरिएबल को ग्लोबल स्कोप में शुरू किया जाता है, तो भाषा के आधार पर, शुरू होने में लगने वाले ज़्यादा समय की वजह से दो तरह के व्यवहार हो सकते हैं: - भाषाओं और एसिंक्रोनस लाइब्रेरी के कुछ कॉम्बिनेशन के लिए, फ़ंक्शन फ़्रेमवर्क एसिंक्रोनस तरीके से चल सकता है और तुरंत नतीजे दिखा सकता है. इससे कोड बैकग्राउंड में चलता रहता है. इससे सीपीयू को ऐक्सेस न कर पाने जैसी समस्याएं हो सकती हैं. इससे बचने के लिए, आपको मॉड्यूल शुरू होने पर ब्लॉक करना चाहिए. इसके बारे में यहां बताया गया है. इससे यह भी पक्का होता है कि शुरू करने की प्रोसेस पूरी होने तक, अनुरोधों को पूरा न किया जाए. - दूसरी ओर, अगर शुरू करने की प्रोसेस सिंक्रोनस है, तो शुरू करने में लगने वाले ज़्यादा समय की वजह से, कोल्ड स्टार्ट में ज़्यादा समय लगेगा. यह समस्या खास तौर पर तब हो सकती है, जब लोड बढ़ने के दौरान कम कॉनकरेंसी वाले फ़ंक्शन इस्तेमाल किए जा रहे हों.

एसिंक्रोनस node.js लाइब्रेरी को प्रीवॉर्म करने का उदाहरण

Firestore के साथ Node.js, एसिंक्रोनस node.js लाइब्रेरी का एक उदाहरण है. min_instances का फ़ायदा पाने के लिए, यहां दिया गया कोड लोड होने के समय ही लोड और शुरू हो जाता है. साथ ही, मॉड्यूल को लोड होने से रोकता है.

टीएलए का इस्तेमाल किया गया है. इसका मतलब है कि ES6 ज़रूरी है. इसके लिए, node.js कोड के लिए .mjs एक्सटेंशन का इस्तेमाल करें या package.json फ़ाइल में type: module जोड़ें.

{
  "main": "main.js",
  "type": "module",
  "dependencies": {
    "@google-cloud/firestore": "^7.10.0",
    "@google-cloud/functions-framework": "^3.4.5"
  }
}

Node.js

import Firestore from '@google-cloud/firestore';
import * as functions from '@google-cloud/functions-framework';

const firestore = new Firestore({preferRest: true});

// Pre-warm firestore connection pool, and preload our global config
// document in cache. In order to ensure no other request comes in,
// block the module loading with a synchronous global request:
const config = await firestore.collection('collection').doc('config').get();

functions.http('fetch', (req, res) => {

// Do something with config and firestore client, which are now preloaded
// and will execute at lower latency.
});

ग्लोबल इनिशलाइज़ेशन के उदाहरण

Node.js

const functions = require('firebase-functions');
let myCostlyVariable;

exports.function = functions.https.onRequest((req, res) => {
  doUsualWork();
  if(unlikelyCondition()){
      myCostlyVariable = myCostlyVariable || buildCostlyVariable();
  }
  res.status(200).send('OK');
});

Python

from firebase_functions import https_fn

# Always initialized (at cold-start)
non_lazy_global = file_wide_computation()

# Declared at cold-start, but only initialized if/when the function executes
lazy_global = None

@https_fn.on_request()
def lazy_globals(request):

  global lazy_global, non_lazy_global

  # This value is initialized only if (and when) the function is called
  if not lazy_global:
      lazy_global = function_specific_computation()

  return https_fn.Response(f"Lazy: {lazy_global}, non-lazy: {non_lazy_global}.")
  

यह एचटीटीपी फ़ंक्शन, लेज़ी-इनिशलाइज़ किए गए ग्लोबल वैरिएबल का इस्तेमाल करता है. यह अनुरोध ऑब्जेक्ट (flask.Request) लेता है और जवाब का टेक्स्ट या वैल्यू का कोई भी सेट दिखाता है. इसे make_response का इस्तेमाल करके, Response ऑब्जेक्ट में बदला जा सकता है.

यह खास तौर पर तब ज़रूरी होता है, जब एक ही फ़ाइल में कई फ़ंक्शन तय किए जाते हैं और अलग-अलग फ़ंक्शन अलग-अलग वैरिएबल का इस्तेमाल करते हैं. अगर लेज़ी इनिशलाइज़ेशन का इस्तेमाल नहीं किया जाता है, तो हो सकता है कि आप उन वैरिएबल पर संसाधन बर्बाद कर दें जिन्हें इनिशलाइज़ तो किया गया है, लेकिन कभी इस्तेमाल नहीं किया गया.

अन्य संसाधन

"Google Cloud Performance Atlas" वीडियो Cloud Functions कोल्ड बूट टाइम में, परफ़ॉर्मेंस को ऑप्टिमाइज़ करने के बारे में ज़्यादा जानें.