Context
My team is building a next-gen, realtime, virtual book club (not really, but it simplifies my app's actual use case) with Firestore.
When it's time for a book club to meet, users will join their respective book club and vote on which books they'd like to read next. Book clubs tend to have between 10-15 users, and each club tends to be voting on between 25-30 books. Each user can cast their votes on as many books as they wish. These book club voting sessions last around 3-4 minutes and involve all of the club's users casting their votes in this time period.
Current Firestore Architecture
My data structure looks like:
- bookClubs (collection)
- bookClub1 (document)
- books (collection)
- book1 (document)
- book2 (document)
- ...
- bookX (document) where X is usually < 30
- users (collection)
- user1 (document)
- user2 (document)
- ...
- userY (document) where Y is usually < 15
- books (collection)
- bookClub2 (document)
- ...
- bookClub1 (document)
Votes are stored in an array of maps on the book document, where each map contains the user's uid, name, and other metadata. Each of the book club users are listening for changes using snapshotChanges() on the bookClubX document, along with the nested books collection and the nested users collection.
We do this so that each user client can see in realtime how many votes are being cast on each book and each book's voters.
Limitations
In development, things seemed to work great with clubs that have between 3-4 users and are voting on 8-12 books. Last night, we had our first real book club meeting in production with 15 users and around 30-35 books, and things crashed and burned. Devices became unresponsive, mobile web browsers began to crash, and data got out of sync.
After adjourning the book club meeting and having to sadly vote with old school paper and pen, our dev team went back to the drawing board to understand where things could've failed.
We came across the following recommended best practice in the Firestore docs:
Keep the rate of documents the database pushes to an individual client under 1 document/second.
In our case, each user who joins a bookClubZ document and calls snapshotChanges() has to pull down the bookClubZ document along with each book document and user document in the two bookClubZ sub-collections. This amounts to 1 bookClubZ doc + 30 book docs + 15 user docs = ~46 docs that are being read on initialization for each and every book club user client, not to mention every update that occurs when a user casts a vote on a book.
Have we blown past the capabilities of Firestore? It definitely seems like we've grossly violated the recommended best practice above. Is this scenario better suited for something like Socket.io, PubNub, Ably, etc.? Is our Firestore data structure dumb and inefficient?