2017-09-16 1 views
0

J'ai une fonction http Firebase qui ajoute des données à une liste de base de données Firebase. Une deuxième fonction est configurée pour traiter la liste lorsqu'elle change, puis mettre à jour certaines données récapitulatives. Dans mon cas, ces mises à jour viennent en rafales. J'utilise les fonctions firebase de node.js.Comment gérer l'ordre d'exécution de la fonction de déclenchement de la base de données incendie

En regardant les journaux firebase, je vois cette séquence en commençant avec une liste vide:

  1. Ajouter à la liste de http - liste comporte une élément
  2. Ajouter à la liste de http - liste dispose de 2 éléments
  3. ajouter à la liste de http - liste comporte 3 éléments
  4. ajouter à la liste de http - liste comporte 4 éléments
  5. liste de résumer avec 1 élément
  6. Résumer liste avec 3 éléments
  7. Résumer liste avec 4 éléments
  8. Résumer liste avec 2 éléments

Mon problème est le résumé ne comprend que 2 éléments au lieu de 4.

Il semblerait le Résumer Les fonctions de déclenchement sont invoquées en parallèle au lieu de séquentielles. Ainsi, lorsque plusieurs déclencheurs se trouvent à proximité, le dernier à terminer peut être l'un des premiers déclenchés à la place du dernier.

Quelles approches peuvent être utilisées pour s'assurer que le calcul de résumé a 'toutes les données' et qu'un calcul de résumé antérieur plus lent ne remplace pas un plus récent? Les déclencheurs de la fonction firebase peuvent-ils être sérialisés pour s'exécuter dans l'ordre dans lequel ils ont été initiés? Idéalement, je voudrais éviter de calculer le N fois le résumé quand une rafale arrive donc une solution où le résumé pourrait être 'programmé' pour un peu de temps dans le futur et ensuite annulé et reprogrammé si un nouvel événement arrive ce serait bien.

Répondre

1

Il n'y a absolument aucune garantie pour l'ordre de livraison pour les événements provenant de plusieurs clients ou invocations. En fait, vous aurez du mal à définir le timing des événements, car il y a beaucoup de parties mobiles variables entre le moment où un client fait une demande et le moment où le travail final dans votre fonction est terminé pour ce client. La meilleure chose que vous puissiez faire est de supposer que plusieurs clients envoient tous des requêtes dans le désordre à votre fonction, et utilisent des transactions de base de données pour empêcher toute sorte de collision pour les écritures qu'ils effectuent.

Si vous devez absolument sérialiser des choses, vous devez demander à un autre programme ou agent de définir les séquences correctes et de sérialiser tout le travail, en veillant à ce que toutes les écritures se déroulent dans un ordre prévisible.

+0

Il ne semble pas déraisonnable e dans le contexte de firebase pour qu'il finisse un 'onChange' avant de servir le suivant. Surtout depuis qu'il passe dans le nouvel état au gestionnaire. Je veux juste que le résumé final sache qu'il utilise toutes les données disponibles. Je pourrais ajouter une mise à jour périodique mais une mise à jour déclenchée par des données semble plus propre. – Glenn

+0

Comment proposeriez-vous de synchroniser toutes les instances en cours d'onChange, pour le chemin individuel (avec wildcard (s)), parmi toutes les instances de serveur qui pourraient l'exécuter? Surtout qu'ils pourraient tous faire un travail de blocage qui pourrait prendre énormément de temps? –

0

Mon contournement est de stocker un admin.database.ServerValue.TIMESTAMP avec la liste add et de vérifier dans la calculatrice de résultats qu'il a produit des résultats pour le dernier horodatage. Sinon, il essaie à nouveau. Dans la plupart des cas, il ne sera pas nécessaire de recalculer le résumé, car ma source d'entrée est normalement constituée d'une liste unique sporadique plutôt que d'ajouts localisés. J'ai implémenté ceci comme une fonction retournant une Promesse qui s'auto-appelle si nécessaire pour recalculer.Ceci est la séquence:

  1. Lire la liste actuelle et horodatage
  2. résultats sommaires Calculer et les stocker
  3. Lire l'horodatage à nouveau
  4. Si l'horodatage différent, allez à 1, sinon fait

Voici le code:

/// return a Promise that new summary and detail results will be posted 
function updateResults(regattaId, lapdataTS, depth) { 
    if (depth > 10) { 
    return Promise.reject("Too many recomputes"); 
    } 
    return admin.database().ref('/eventdata/'+regattaId).once('value') 
    .then(function (snapshot) { 
    const rawdata = snapshot.val(); 

    if (rawdata.lapdataTS === lapdataTS) { 
     // console.log("already computed"); 
     return Promise.resolve(); 
    } 
    lapdataTS = rawdata.lapdataTS ? rawdata.lapdataTS : null; 
    const results = regattaCalc.computeResults(rawdata); 

    var updates = {}; 
    updates['results/' + regattaId] = results; 
    updates['summary/' + regattaId] = results.regattaInfo; 
    return admin.database().ref().update(updates); 
    }).then(function() { 
    // read last TS and see if it matches our summary 
    return admin.database().ref('/eventdata/'+regattaId+'/lapdataTS').once('value'); 
    }).then(function (snapshot) { 
    if (snapshot.val() === lapdataTS) { 
     return Promise.resolve(); 
    } else { 
     //console.log("Need to calc again"); 
     return updateResults(regattaId, lapdataTS, depth++); 
    } 
    }).catch((reason) => { 
    console.log("Error generating summary: " + reason); 
    return Promise.reject(reason); 
    }); 
} 

exports.compupteResults = functions.database.ref('/eventdata/{regattaId}').onWrite(event => { 
return updateResults(regattaId,null,0); 
});