firebase / firebase/firebase-js-sdk

GRPC errors intermittently cause firebase client to fail

Open
#6,724 7 comments 1 reaction 1 assignee Claimed by @dconeybe View on GitHub
api: firestore bug
Dominant language
TypeScript
Stars
5.1k
Forks
1k
Avg merge
2d 21h
Merged PRs (30d)
37

Description

### [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:
```javascript
const { initializeApp } = require("firebase/app");
const { getFirestore, collection, getDocs, addDoc, doc, deleteDoc, connectFirestoreEmulator } = require('firebase/firestore');

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

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](https://user-images.githubusercontent.com/9586453/197666481-2e6a86e6-2f96-4da2-aa65-8058342b23be.png)
![image](https://user-images.githubusercontent.com/9586453/197667402-bf9d5f76-f6b5-4ffb-81e6-fef0c081f411.png)
![image](https://user-images.githubusercontent.com/9586453/197667520-b4cb9d44-e85c-4bae-b653-86bcc568a560.png)
![image](https://user-images.githubusercontent.com/9586453/197667556-39c712f8-a1d8-4953-8309-ec5bc84d3d36.png)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.