Skip to content

GRPC errors intermittently cause firebase client to fail #6724

Open
@Hoppi164

Description

@Hoppi164

[REQUIRED] Describe your environment

  • Operating System version: MAC OS Montery 12.4
  • Browser version: Node 16.16.0
  • Firebase SDK version: 9.12.1
  • Firebase Product: Firestore

[REQUIRED] Describe the problem

Our application is experiencing problems with firebase getDocs() never responding, and random grpc connection error messages when writing documents.

At my workplace we were experiencing several issues.
Our application has a lot of reads and writes going on, so i created a test script to simulate this environment without any of our proprietary code; and i was able to semi-reliably replicate the exact error codes we are experiencing.

Steps to reproduce:

  • Create a test.js file with the following code
  • Update the config with your own values
  • Start your own firebase emulator suite
  • run node test.js
    • This will iteratively create a bunch of users and read a bunch of users, basically doing a stress test.
    • This SHOULD throw a bunch of GRPC errors, and then throw a custom error of get timeout - read took over 5 seconds
    • Run the test a few times, rarely it will happily work, but most times it repeatably throws the errors

Relevant Code:

const { initializeApp } = require("firebase/app");
const { getFirestore, collection, getDocs, addDoc, doc, deleteDoc, connectFirestoreEmulator } = require('firebase/firestore');

const config = {
    "firestoreEmulatorPort": 9098,
    "firebase": {
      "apiKey": "<<insert>>",
      "projectId": "<<insert>>",
      "appId": "<<insert>>",
    }
}

const app = initializeApp(config.firebase);
const db = getFirestore(app);
connectFirestoreEmulator(db, 'localhost', config.firestoreEmulatorPort);


function timeoutReject (timeMs, message) {
    return new Promise((resolve, reject) => setTimeout(() => reject(new Error(message)), timeMs));
}

async function run (batchIndex, runIndex) {

    // Add user to db
    console.time(`write ${batchIndex}-${runIndex}`)
    const writeDocs = addDoc(collection(db, "users"), {
        first: "Ada",
        last: "Lovelace",
        born: 1815
    });
    writeDocs.catch(error => { console.error('error writing user', error, batchIndex)})
    const user = await writeDocs
    console.timeEnd(`write ${batchIndex}-${runIndex}`)

    // Get all users
    console.time(`read ${batchIndex}-${runIndex}`)
    const query = getDocs(collection(db, "users"));
    query.catch(error => { console.error('error resolving promise', error, batchIndex); });
    const allUsers = await Promise.race([
        query,
        timeoutReject(5000, `Get Timeout - Reading took over 5 seconds ${batchIndex}`)
    ])
    console.log(`read success - ${allUsers.size}`)
    console.timeEnd(`read ${batchIndex}-${runIndex}`)

}

// Run stress test
async function runTest() {
   
    // Run twenty batches
    for (let batchIndex = 0; batchIndex < 20; batchIndex++) {

        // In each batch, do 50 "test runs"
        const runs = new Array(50).fill(null).map( (value, runIndex) => {
            return run(batchIndex, runIndex)
        })
        await Promise.all(runs)

    }
    console.log('stress test complete')
    process.exit()
}

runTest();

Example Errors

image
image
image
image

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions