सलाह और सुझाव

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

सही जवाब

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

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

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

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

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

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

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

कुछ समय तक सेव रहने वाली फ़ाइलों को हमेशा मिटाना

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

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

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

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

फ़ंक्शन फ़्रेमवर्क

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

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

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

टूल

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

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

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

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

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

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

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

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

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

Node.jsPython
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}`);
});
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.jsPython
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}`);
});
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 का फ़ायदा पाने के लिए, यहां दिया गया कोड लोड होने के समय लोडिंग और इनिशलाइज़ेशन पूरा करता है. साथ ही, मॉड्यूल लोड होने पर ब्लॉक करता है.

टीएलए का इस्तेमाल किया गया है. इसका मतलब है कि 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.jsPython
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');
});
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 परफ़ॉर्मेंस ऐटलस" वीडियो में, Cloud Functions कोल्ड बूट टाइम में परफ़ॉर्मेंस को ऑप्टिमाइज़ करने के बारे में ज़्यादा जानें.