Open
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:
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();