ref.child ('route'). update Duplicate queries

1

I want to implement a Functions in Firebase but something strange happens when running a Cloud Functions.

All the flow is correct the searches are done well however when I add an update within my "if" the results are duplicated.

When I comment on the line that executes the function:

actualizatatusbroadcast(broad[c].id, cambio);

works correctly but if I let this function run the console.log prints more than once.

my code is as follows

// Import the Firebase SDK for Google Cloud Functions.
'    use strict';
import * as functions from 'firebase-functions';



// Import and initialize the Firebase Admin SDK.
const admin = require('firebase-admin');
const nodemailer = require('nodemailer');

admin.initializeApp();

exports.DeleteUserCirculo = functions.database.ref('/users/{pushId}').onUpdate((snapshot, event) => {

var after = snapshot.after.val();
var cambio = after.status;
console.log("El usuario con ID: ", after.id, "se le aplicará la regla ", after.status);


var chats_bg = admin.database().ref('chats/broadcast_group');
chats_bg.on('value', function(snapshot2)
{
    let broad = snapshot2.val();

    for (let c in broad)
    {
      console.log("esta es la vuelta", c);

      if (broad[c].type == 'broadcast_group' && broad[c].creatorUserId == after.id)
      {
        console.log("esta es la vuelta dentro del if", c);
        actualizatatusbroadcast(broad[c].id, cambio);
      }
      }
});
 return event.eventId;
       });

My function is this and what it does is to update the status of the nodes that meet the condition of the "if"

function actualizatatusbroadcast(broad, cambio)
{
 const ref = admin.database().ref();
  ref.child('chats/broadcast_group/' + broad).update(
    {
      status: cambio
    }
  );
}

This is the log if I let the function that performs the update run, you can see how my console.log is printed more than once to stop testing.

If I comment the line that executes the function that performs the update everything is well displayed

The structure of the node where I perform the update is as follows:

I hope someone can help me clarify a little about why this happens and what I am not considering

    
asked by José Roberto Nava Morales 02.08.2018 в 06:56
source

2 answers

0

Hello @Nery Ortez first, thank you very much for answering, giving a bit of retro to the points you indicate ...

Previous revisions are no problem because in Firebase I eliminated all the Cloud Functions to focus on this problem so this code is the only one I have.

Turning to the recommendation and testing the code you send me now the code is in an infinite loop which only stops when you mark a timeout.

Google's documentation indicates that the Promise is used to control an asynchronous call and the structure you send me see it correctly.

Any ideas why this is happening?

    
answered by 03.08.2018 в 03:54
0

Previous reviews:

  • That there is no other function that modifies /users/{pushId} . It may not be your case. But it's worth the clarification.

    A common mistake is to make cyclical changes. In your case changes in /users/{pushId} create changes in chats/broadcast_group/ . If there is a function that listens to those changes and in turn makes changes in /users/{pushId} then you will have changes triggering one in consequence of the other cyclically.

  • Update the logging console.

    Repeated logging in the console is a frequent annoyance. But it's just updating the feed, and it will be reloaded with the actual amount.

  • Now, I see that you have two problems with your function:

  • You need to use Promise and
  • You are editing /chats/broadcast_group in the same callback in which you are receiving changes.
  • About using Promises

    As you can read in the documentation , asynchronous functions should return null , an object or a promise ( Promise ). That way the server knows when the function is over.

    Even if with the previous points you have fixed your problem, I strongly recommend that you return a promise in your function. I'll explain:

    In your case, with the penultimate line of code return event.eventId; denotes that your function has ended ... But beware! The next call is asynchronous:

    chats_bg.on('value', function(snapshot2) // Buscar valores en la Base de Datos
    {
    // ... No se ejecuta inmediatamente. Se espera a que la Base de Datos responda.
    });
    

    Which means that the end of the function will be reached before the above is executed.

    As proof of this, in your log you can see that the termination message finished with status: 'ok' , but still logs are still displayed.

    What happens is:

  • The server receives the message that the function has finished (although there is still work to be done).
  • The server stops the function.
  • When the function is executed again, the pending work is continued, executing the missing lines of the previous session.
  • About writing /chats/broadcast_groups

    Agree to the documentation if you use ref.on('value', ...) the callback will trigger for each change in the reference:

      

    This method starts when the listener is attached and again every time the data changes (including secondary ones).

    Something like this is happening:

  • Receive value of broadcast_group and execute the callback
  • Enter the if and make a change.
  • The callback is triggered again due to the change made in step two
  • Repeats 2 and 3 indefinitely.
  • To avoid this you must read the data only once using the once() method. Take into account that once() , unlike on() , returns a Promise with the data read.

    The following code should fix both problems:

    exports.DeleteUserCirculo = functions.database.ref('/users/{pushId}').onUpdate((snapshot, event) => {
    
      var after = snapshot.after.val();
      var cambio = after.status;
      console.log("El usuario con ID: ", after.id, "se le aplicará la regla ", after.status);
    
    
      var chats_bg = admin.database().ref('chats/broadcast_group');
    
      // Devolvemos una promesa que se resuelva cuando se termine la
      // actualizacion del broadcast.
      return chats_bg.once('value').then((snapshot2) => {
        let broad = snapshot2.val();
        let promesas = []; // Un array para guardar todas las promesas
    
        for (let c in broad) {
          console.log("esta es la vuelta", c);
    
          if (broad[c].type == 'broadcast_group' && broad[c].creatorUserId == after.id) {
            console.log("esta es la vuelta dentro del if", c);
            promesas.push(actualizatatusbroadcast(broad[c].id, cambio));
            // 'actualizatatusbroadcast' devuelve un Promise, guardamos todas las promesas
            // en un array para que se resuelvan juntas.
          }
        }
    
        return Promise.all(promesas);
        // Promise.all espera a que todas las promesas se cumplan para resolverse
      });
    });
    
    function actualizatatusbroadcast(broad, cambio) {
      const ref = admin.database().ref();
      // retornamos el Promise que se devuelve con 'update'
      return ref.child('chats/broadcast_group/' + broad).update(
        {
          status: cambio
        }
      );
    }
    

    We use the promise returned by once() to return the internal promises.

    With the above, the server will not stop the promise until the update promises are resolved (or until the timeout is reached).

    The previous code should be self-explanatory, but if you still have doubts, check the documentation that I linked you above.

        
    answered by 02.08.2018 в 08:48