42

Wir arbeiten an einer Anwendung, die die neuen Firebase-Cloud-Funktionen verwendet. Momentan wird eine Transaktion in den Warteschlangenknoten eingefügt. Und dann entfernt die Funktion diesen Knoten und setzt ihn in den richtigen Knoten. Dies wurde aufgrund der Fähigkeit implementiert, offline zu arbeiten.Firebase-Cloud-Funktionen sind sehr langsam

Unser aktuelles Problem ist die Geschwindigkeit der Funktion. Die Funktion selbst benötigt etwa 400ms, also ist das in Ordnung. Manchmal nehmen die Funktionen sehr lange Zeit in Anspruch (etwa 8 Sekunden), während der Eintrag bereits zur Warteschlange hinzugefügt wurde.

Wir vermuten, dass der Server braucht Zeit zum Hochfahren, denn wenn wir die Aktion noch einmal nach dem ersten. Es braucht viel weniger Zeit.

Gibt es eine Möglichkeit, dieses Problem zu beheben? Hier unten habe ich den Code unserer Funktion hinzugefügt. Wir vermuten, dass nichts falsch daran ist, aber wir haben es für alle Fälle hinzugefügt.

const functions = require('firebase-functions'); 
const admin = require('firebase-admin'); 
const database = admin.database(); 

exports.insertTransaction = functions.database 
    .ref('/userPlacePromotionTransactionsQueue/{userKey}/{placeKey}/{promotionKey}/{transactionKey}') 
    .onWrite(event => { 
     if (event.data.val() == null) return null; 

     // get keys 
     const userKey = event.params.userKey; 
     const placeKey = event.params.placeKey; 
     const promotionKey = event.params.promotionKey; 
     const transactionKey = event.params.transactionKey; 

     // init update object 
     const data = {}; 

     // get the transaction 
     const transaction = event.data.val(); 

     // transfer transaction 
     saveTransaction(data, transaction, userKey, placeKey, promotionKey, transactionKey); 
     // remove from queue 
     data[`/userPlacePromotionTransactionsQueue/${userKey}/${placeKey}/${promotionKey}/${transactionKey}`] = null; 

     // fetch promotion 
     database.ref(`promotions/${promotionKey}`).once('value', (snapshot) => { 
      // Check if the promotion exists. 
      if (!snapshot.exists()) { 
       return null; 
      } 

      const promotion = snapshot.val(); 

      // fetch the current stamp count 
      database.ref(`userPromotionStampCount/${userKey}/${promotionKey}`).once('value', (snapshot) => { 
       let currentStampCount = 0; 
       if (snapshot.exists()) currentStampCount = parseInt(snapshot.val()); 

       data[`userPromotionStampCount/${userKey}/${promotionKey}`] = currentStampCount + transaction.amount; 

       // determines if there are new full cards 
       const currentFullcards = Math.floor(currentStampCount > 0 ? currentStampCount/promotion.stamps : 0); 
       const newStamps = currentStampCount + transaction.amount; 
       const newFullcards = Math.floor(newStamps/promotion.stamps); 

       if (newFullcards > currentFullcards) { 
        for (let i = 0; i < (newFullcards - currentFullcards); i++) { 
         const cardTransaction = { 
          action: "pending", 
          promotion_id: promotionKey, 
          user_id: userKey, 
          amount: 0, 
          type: "stamp", 
          date: transaction.date, 
          is_reversed: false 
         }; 

         saveTransaction(data, cardTransaction, userKey, placeKey, promotionKey); 

         const completedPromotion = { 
          promotion_id: promotionKey, 
          user_id: userKey, 
          has_used: false, 
          date: admin.database.ServerValue.TIMESTAMP 
         }; 

         const promotionPushKey = database 
          .ref() 
          .child(`userPlaceCompletedPromotions/${userKey}/${placeKey}`) 
          .push() 
          .key; 

         data[`userPlaceCompletedPromotions/${userKey}/${placeKey}/${promotionPushKey}`] = completedPromotion; 
         data[`userCompletedPromotions/${userKey}/${promotionPushKey}`] = completedPromotion; 
        } 
       } 

       return database.ref().update(data); 
      }, (error) => { 
       // Log to the console if an error happened. 
       console.log('The read failed: ' + error.code); 
       return null; 
      }); 

     }, (error) => { 
      // Log to the console if an error happened. 
      console.log('The read failed: ' + error.code); 
      return null; 
     }); 
    }); 

function saveTransaction(data, transaction, userKey, placeKey, promotionKey, transactionKey) { 
    if (!transactionKey) { 
     transactionKey = database.ref('transactions').push().key; 
    } 

    data[`transactions/${transactionKey}`] = transaction; 
    data[`placeTransactions/${placeKey}/${transactionKey}`] = transaction; 
    data[`userPlacePromotionTransactions/${userKey}/${placeKey}/${promotionKey}/${transactionKey}`] = transaction; 
} 
+0

Ist es sicher nicht das Versprechen von oben zurückkehren ‚einmal()‘ Anrufe? – jazzgil

Antwort

46

firebaser hier

Es klingt wie Sie einen so genannten Kaltstart der Funktion bei Ihnen auftritt.

Wenn Ihre Funktion in einer bestimmten Zeit nicht ausgeführt wurde, versetzt Cloud Functions sie in einen Modus, der weniger Ressourcen verbraucht. Wenn Sie die Funktion erneut drücken, wird die Umgebung in diesem Modus wiederhergestellt. Die Zeit, die für die Wiederherstellung benötigt wird, besteht aus Fixkosten (z. B. Wiederherstellen des Containers) und Kosten für Teilevariablen (z. B. wenn Sie viele Knotenmodule verwenden, kann dies länger dauern).

Wir überwachen kontinuierlich die Leistung dieser Vorgänge, um eine optimale Mischung zwischen Entwicklererfahrung und Ressourcennutzung sicherzustellen. Erwarten Sie also, dass sich diese Zeiten im Laufe der Zeit verbessern.

Die gute Nachricht ist, dass Sie dies nur während der Entwicklung erleben sollten. Wenn Ihre Funktionen häufig in der Produktion angestoßen werden, werden sie wahrscheinlich nie wieder einen Kaltstart erleben.

+8

Ich habe derzeit das gleiche Problem. Manchmal dauert es 8 bis 15 Sekunden, bis die Funktion ausgeführt wird. Selbst wenn die Funktion zuletzt wie vor 5 Minuten ausgeführt wurde. Wird das verbessert? – Brianvdb

+1

@Frank Wäre es sinnvoll, Dummy-Anfragen zu senden, um die Funktionen am Leben zu erhalten? Wenn ja, wie regelmäßig würden Sie empfehlen, dass sie erledigt werden müssten? (Unsere Produktion ist noch nicht populär genug, um diese Verzögerungen zu vermeiden) –

+0

@PatrixWilliams Wenn Sie 8-15s Lags nach 5m erleben, würde ich Ihre Logs scannen, um sicherzustellen, dass Sie keine unbehandelte Ausnahme bekommen, die uns erfordert geben Sie eine neue Instanz (und damit einen neuen Kaltstart). –

8

aktualisieren - sieht aus wie viele dieser Probleme process.env.FUNCTION_NAME wie hier zu sehen mit der verborgene Variable gelöst werden können: https://github.com/firebase/functions-samples/issues/170#issuecomment-323375462

Dies sollte für eine viel sauberere Lösung erlauben als das, was ich habe unten getan (obwohl die Erklärung unten hält immer noch).


Es sieht aus wie erfordern Dateien und allgemeine Initialisierung im globalen Bereich geschieht eine große Ursache der Verlangsamung während des Kaltstarts.

Als ein Projekt der globalen Bereich mehr Funktionen bekommt mehr und mehr verschmutzt das Problem noch schlimmer zu machen -. Vor allem, wenn Sie Ihre Umfang Funktionen in separate Dateien (wie zB durch Object.assign(exports, require('./more-functions.js')); in Ihrem index.js mit

Ich habe es geschafft, riesige Gewinne in Kaltstartleistung zu sehen, indem wie unten alle meine erfordert in ein init-Methode bewegt und dann als die erste Zeile in jeder Funktionsdefinition für diese Datei aufrufen. ZB:

const functions = require('firebase-functions'); 
const admin = require('firebase-admin'); 
// Late initialisers for performance 
let initialised = false; 
let handlebars; 
let fs; 
let path; 
let encrypt; 

function init() { 
    if (initialised) { return; } 

    handlebars = require('handlebars'); 
    fs = require('fs'); 
    path = require('path'); 
    ({ encrypt } = require('../common')); 
    // Maybe do some handlebars compilation here too 

    initialised = true; 
} 

ich habe Verbesserungen gesehen von etwa 7-8 Sekunden auf 2-3 Sekunden, wenn man diese Technik auf ein Projekt anwendet t mit ~ 30 Funktionen über 8 Dateien. Dies scheint auch dazu zu führen, dass Funktionen weniger häufig kalt gebootet werden müssen (vermutlich aufgrund des geringeren Speicherbedarfs).)

Leider macht dies immer noch HTTP-Funktionen kaum benutzbar für die Benutzer gerichtete Produktion verwenden.

In der Hoffnung, dass das Firebase-Team in Zukunft einige Pläne hat, um die richtigen Funktionen zu definieren, so dass nur die relevanten Module für jede Funktion geladen werden müssen.

0

Ich habe diese Dinge auch getan, die die Leistung verbessert, sobald die Funktionen aufgewärmt sind, aber der Kaltstart bringt mich um. Eines der anderen Probleme, mit denen ich konfrontiert war, ist mit cors, denn es dauert zwei Reisen zu den Cloud-Funktionen, um die Arbeit zu erledigen. Ich bin mir sicher, dass ich das beheben kann.

Wenn Sie eine App in der frühen (Demo) Phase haben, in der sie nicht häufig verwendet wird, ist die Leistung nicht besonders gut. Dies sollte berücksichtigt werden, da Early Adopters mit einem frühen Produkt vor potenziellen Kunden/Investoren ihr Bestes geben müssen. Wir waren von der Technologie begeistert und migrierten daher von älteren, bewährten Frameworks, aber unsere App scheint zu diesem Zeitpunkt ziemlich träge zu sein. Ich werde als nächstes einige Aufwärmstrategien versuchen, damit es besser aussieht.

0

Ein anderes Problem ist freier Plan (Spark).

spark

Sobald ich auf bezahlten Plan (Blaze in meinem Fall) wechseln, dann beginnen meine Funktionen schnell zu arbeiten.

blaze