Home > Enterprise >  multiple connections and huge download overhead in cloud function trigged by writes in firebase real
multiple connections and huge download overhead in cloud function trigged by writes in firebase real

Time:03-15

I wrote the following cloud function writetest trigged by writes in the location /test/{folder}

The function writetest just write in the location /register/{folder} the (approximated) time the functions was trigged.

We can see bellow the html file testfirebase.html (I removed the info of my project). When you load this html in your browser, first you login in the app and after you click the "Upload" button you upload 1KB of data 100 times in 100 different locations /test/0, /test/1, /test/2, ...,/test/99.

Of course this triggers the cloud function writetest 100 times. My problem is that this also generate more than 80 connections in the usage tab of the project console and specially in the "Usage" tab in the project console you can see a downloaded data that is around 440KB. For me it is surprising that there is downloaded data at all, and it is even more surprising that the downloaded data is many times larger than the uploaded data. Maybe is this related with the 3.5Kb "handshake" in every connection? Since my real app makes these uploads very often, and each upload is quite small (~1KB), this can increase the cost of using cloud functions trigged by writes quite significantly. Is this additional cost unavoidable?

This is the code of the cloud function

exports.writetest = functions.database.ref(
    "/test/{folder}")
    .onWrite(async (snapshot, context)=> {
      return admin.database().ref("/register/" 
        context.params.folder).update(
          {
            "time": admin.database.ServerValue.TIMESTAMP});
    });

The html file testfirebase.html (you must login with an user authorized to write in /test/)

<html lang="en">
 
<head>
   
    <script src=
   "https://www.gstatic.com/firebasejs/8.7.0/firebase.js">
    </script>
    
    
    <script src="https://www.gstatic.com/firebasejs/ui/6.0.1/firebase-ui-auth.js"></script>
    <link type="text/css" rel="stylesheet" href="https://www.gstatic.com/firebasejs/ui/6.0.1/firebase-ui-auth.css" />
    
     <script>
         
         const firebaseConfig = {
           apiKey: "XX",
           authDomain: "XX.firebaseapp.com",
           databaseURL: "https://XX-default-rtdb.firebaseio.com",
           projectId: "XX",
           storageBucket: "XX.appspot.com",
           messagingSenderId: "XX",
           appId: "XX",
           measurementId: "${config.measurementId}"
         };
         
         // Initialize Firebase
         firebase.initializeApp(firebaseConfig);
         
         
         var ui = new firebaseui.auth.AuthUI(firebase.auth());
         
         var dbase=firebase.database();
         
         
         
         const uiConfig = {
           callbacks: {
             signInSuccessWithAuthResult(authResult, redirectUrl) {
                 
               return true;
             },
             uiShown() {
             },
           },
           signInFlow: 'popup',
           signInSuccessUrl: 'https://mydomain.app/testfirebase.html',
           signInOptions: [
             firebase.auth.EmailAuthProvider.PROVIDER_ID,
             firebase.auth.GoogleAuthProvider.PROVIDER_ID,
           ],
         };


         firebase.auth().onAuthStateChanged( async (user) => {
           if (user) {
             console.log("user logged");
               
               console.log("user id:"  user.uid);
               
           } else {
               
               console.log("no user logged");
               ui.start('#firebaseui-auth-container', uiConfig);
           }
         });
         
         
         async function dbset(path,data)
         {
             
             return new Promise ((resolve,reject)=>{
                 
             
             var reference = dbase.ref(path);
             
             reference.set(data).then(()=>{resolve("OK"); console.log("OK");}).catch((error) => {
                 reject("ERROR DBSET:" error);
               });
             });
             
         }
         
         async function upload()
         {
             
            var onekbytestring="";
            for (let k=0;k< 1000;k  ) {onekbytestring = onekbytestring   "1";}
             
          for (let j=0; j< 100;j  )
          {
            dbset("test/" j,{b:onekbytestring });
          }
          }
        
     </script>
     
     
     
     </head>
     <body>
    

    <div id="firebaseui-auth-container"></div>
         
      <div >
      
      <button  onclick="upload()">Upload</button>
      
      </div>
     
</body>
 
</html>

CodePudding user response:

Calling update on a path does not download data from that path.

But since you use the Admin SDK, it will need to establish a connection web socket connection over HTTPS to the server, which includes about 4Kb of overhead for establishing the TLS/SSL connection.

Since these client connection, each will also count as a connection. These connection may linger for a few minutes on the server, while it waits for the socket to timeout. If that happens, you will also see those connections showing up in the console.


Note that in a real use-case the connections are likely going to be spread out more. In that case, I expect that you'll see more re-use of the already established connection, as the invocations are more likely to hit a Cloud Functions instance that has been used before and where the SDK thus may still have an established connection.


You could try using the REST API to send the update to see if that makes any difference. While that'll definitely reduce the connection count (as the REST API for updates is connectionless), but I doubt it'll make a big difference for the data size. Still, it may be worth a shot if this is a concern for you.

  • Related