Weird numbers from multiple Cloud Functions transactions with Firebase

Viewed 287

I am new to Javascript, Promises, and fairly new to Firebase, so this may simply me be misunderstanding something basic. I appreciate any guidance!

The problem I am having: I am keeping 3 counters for certain events. So every time an 'item' is added to a 'user' I update the global count of items, the project count of items, and the user count.

This works perfectly whenever I use it and test it, however something is going wrong where sometimes I log on to check the data after a few days and the global counts seemed to have doubled. For example I will look at a user's count at it will be 200, however then if I look at the actual data there are only 100 items, making me think that the count transactions are incrementing by two instead of one (or being called twice).

I am unable to replicate it myself, and whenever I use this function all of the counts increment properly by one. But if I leave it for a few days the numbers start to increment by 2 or more.

My Firebase Function listening to the DB:

"use strict";

// Firebase requirements
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const utils = require('./utils');
admin.initializeApp(functions.config().firebase);

exports.updatePatientCount = functions.database.ref('/projects/{projectId}/users/{userId}/itemList/{itemId}').onWrite(event => {
    // Extract the projectKey and userId
    var projectId = event.params.projectId
    var userId = event.params.userId

    return utils.saveGlobals(admin, event, "itemCount", projectId, userId);
});

saveGlobals function:

"use strict";

const counter = function (up, count) {
    if (up) {
        return (count || 0) + 1;
    }

    return (count || 0) - 1;
};

exports.saveGlobals = function (admin, event, ref, projectId, userId) {
    const gloRef = admin.database().ref("global-stats/global");
    const gloProjRef = admin.database().ref("global-stats/projects");

    var up = false;
    // If data was created increment by 1
    if (event.data.exists() && !event.data.previous.exists()) {
        up = true;
    } else if (!event.data.exists() && event.data.previous.exists()) {
        // If the data was deleted deincrement by 1
        up = false;
    } else {
        // If the data was updated return
        return;
    }

    // Update the global count
    const setGlo = gloRef.child(ref).transaction(current => {
        return counter(up, current);
    }).then(() => {
        console.log('Global updated.');
    });

    // Update the project count + set the project ID 
    const setPro = gloProjRef.child(projectId).child("projectId").set(projectId);
    const setGloPro = gloProjRef.child(projectId).child(ref).transaction(current => {
        return counter(up, current);
    }).then(() => {
        console.log('Global project updated.');
    });

    // Update the user count + set the ID
    const setUse = gloProjRef.child(projectId).child("users").child("ID-" + userId).child("userId").set(userId);
    const setGloUse = gloProjRef.child(projectId).child("users").child("ID-" + userId).child(ref).transaction(current => {
        return counter(up, current);
    }, ).then(() => {
        console.log('Global user updated.');
    });

    // Return the promises
    return setGlo && setPro && setGloPro && setUse && setGloUse;
}

The example I used to put this together: https://github.com/firebase/functions-samples/tree/master/child-count

I extracted the saveGlobals() part because it is used by multiple Cloud Functions that increment different counters.

Can anyone see any reason the transactions should start incrementing the counts by more than one when an item is added to the list?

0 Answers
Related