Firebase transaction on where query

I am working on a game with in-game matchmaking where a user can create a game and wait for an other player to join it.

In Firebase, I created a "games" collection where the document structure look like this: enter image description here

The matchmaking algorithm is really simple :

  • User query for an open game he can join (isStarted == true && player2 == null)
    • if a document is returned, user join the game, update player2 property by user data and add user id in participants array.
    • if no document returned, create a game and wait for another player to join

Actually, the game is in production and I observed a problem : when there is high traffic on app, some games have been joined by 2 users (so there is 3 users in game instead of 2).

Ideally, I would like to make a transaction with a query to make sure document has not changed (and not joined by another user). If document has changed because an other user has already joined the game, then get another document. Unfortunately, Firebase docs mention that transaction don't work with query.

Do anyone have an idea to fix this without read too much documents ? Thank you !


Unfortunately, Firebase docs mention that transaction don't work with query.

This is true for the client SDKs (including the FlutterFire plugin) but not for the Admin SDKs. The Admin SDKs use pessimistic concurrency controls and therefore you can make a transaction on the documents returned by a query. More generic info here in the doc.

So you can use a Cloud Function to execute your algorithm. The most appropriate is probably a Callable Cloud Function. The doc for Transactions for the Node.js Admin SDK (to be used in a Clooud Function) can be found here. In particular, you'll see that you can pass a Query to the get() method.