Home > Mobile >  Firebase - One transaction for 2 paths in real time database
Firebase - One transaction for 2 paths in real time database

Time:12-23

I have a database with the following structure:

Structure of my database

Now two paintings can be rated against each other and the rating is then updated. But it can happen that one painting is rated mutliple times at the same time. Therefore i tried using a nested transaction (see below) to avoid race conditions:

function pictureClicked(picture) {
    const db = getDatabase();
    const leftRef = ref(db, '/paintings/'   generateDbName(currentLeft));
    const rightRef = ref(db, '/paintings/'   generateDbName(currentRight));
    const winnerLeft = (picture === 'left') ? 1 : 0;

    runTransaction(leftRef, (leftP) => {
        if (leftP) {
            runTransaction(rightRef, (rightP) => {
                if (rightP) {
                    // Calculation of the new rating for each painting (Elo System)
                    const tRatingLeft = Math.pow(10, leftP.rating / 400);
                    const tRatingRight = Math.pow(10, rightP.rating / 400);

                    const expectedLeft = tRatingLeft / (tRatingLeft   tRatingRight);
                    const expectedRight = tRatingRight / (tRatingLeft   tRatingRight);

                    // Setting new rating
                    leftP.rating = leftP.rating   32 * (winnerLeft - expectedLeft);
                    rightP.rating = rightP.rating   32 * ((1 - winnerLeft) - expectedRight);
                }
                return rightP;
            });
        }
        return leftP;
    });
}

Now this solution doesn't really work and I have read that it isn't the best approach to this problem. The only solution I found was running a transaction on the root of my database but I'm afraid that this would lead to a bottleneck and many of the launched transactions wouldn't even write to the database since transactions are only retried 25 times (or is my fear here unfounded?). Are there any other solutions to this problem?

CodePudding user response:

Firebase Realtime Database transactions run against a single path in your database. There is currently no way to run a transaction against multiple path, so the common approach is to run the transaction against the lowest-level shared path (/paintings in your case).

This can indeed lead to more contention as you have more users performing concurrent transactions.


The only alternative I've used at times is to build my own transaction mechanism using:

  1. Multi-path updates, to perform both writes at the same time.
  2. Security rules, to only allow the transition that make sense in my use-case.
  3. Client-side retries, so that contention or outdated data are still handled correctly.

This is fairly non-trivial even for simple cases, and may require changing your data structure and/or adding additional values to each request. But if you get past those hurdles, it will allow you to run a "transaction" across multiple paths.

  • Related