title: Online Matchmaking Issues in Cloud Function and Realtime Database
body: <p>A Cloud Function is set up to trigger when a new entry is created, and it checks whether we have enough players to start a game. If so, it creates the game at ‍‍<code>/matches</code> , and writes the <code>gameId</code> to each player's entry, like so: <code>/users/{playerId}/profile/cm</code></p>
  <p>Players listen to <code>gameId</code> on their own database entries, so they know when to transition to the GamePlay screen.</p>
  <p>Since there are a lot of players request for find opponent simultaneously, and the Cloud Function is asynchronous concurrency , the concurrency issues come out and as you can see in the picture one user added in two diffect matches.</p>
  <p><a href="https://i.stack.imgur.com/ArIhk.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ArIhk.png" alt="Firebase realtime database image" /></a>
  <a href="https://i.stack.imgur.com/nT203.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/nT203.png" alt="enter image description here" /></a></p>
  <pre class="lang-js prettyprint-override"><code>const functions = require('firebase-functions');
  
  const admin = require('firebase-admin');
  admin.initializeApp();
  
  exports.onNewRequest = functions.database.ref(&quot;/requests/{boardId}/{userId}&quot;).onCreate((change, context) =&gt; {
      var boardId = context.params.boardId;
      var createdUserId = context.params.boardId;
      // new request created
      var requests = admin.database().ref(&quot;/requests/&quot;+boardId);
      requests.once(&quot;value&quot;).then((requestsSnapshot)=&gt;{
          var waitersCount = requestsSnapshot.numChildren();
          if(waitersCount &gt; 1){
              // more than one user waiting to join the game
              // lets link users
              var creatorSnapshot;
              var guestSnapshot;
              // more than two players waiting to play a game on the board{boardId}
              var index = 0;
              requestsSnapshot.forEach((childSnapshot) =&gt; {
                  if (index === 0) creatorSnapshot = childSnapshot;    
                  if (index === 1) guestSnapshot   = childSnapshot;
                  index++;
              });
  
              // we have the creator and the guest
              // creatorSnapshot.key is the id of the user /requests/boardId/userId &lt;&lt;&lt;&lt;--
              var match = admin.database().ref(&quot;/matches&quot;).push({
                  s: 1,
                  peers:{
                      [creatorSnapshot.key]:true,
                      [guestSnapshot.key]:true
                  }
              });
              match.then(()=&gt;{
                  // delete waiters
  
                  admin.database().ref(&quot;/users/&quot;+creatorSnapshot.key+&quot;/profile/cm&quot;).set(match.key);
                  admin.database().ref(&quot;/users/&quot;+guestSnapshot.key+&quot;/profile/cm&quot;).set(match.key);
                  
                  creatorSnapshot.ref.remove();
                  guestSnapshot.ref.remove();
  
                  return null;
              }).catch((error) =&gt; {
                  console.log(error);
              });
              // var matchId = boardId + &quot;-&quot; + creatorSnapshot.key + &quot;-&quot; + guestSnapshot.key;
              // return admin.database().ref(&quot;/matches/&quot;+matchId).set({
              //     s: 1,
              //     peers:{
              //         [creatorSnapshot.key]:true,
              //         [guestSnapshot.key]:true
              //     }
              // }).then(()=&gt;{
              //     // delete waiters
              //     // creatorSnapshot.ref.remove();
              //     // guestSnapshot.ref.remove();
              //     return null;
              // }).catch((error) =&gt; {
              //     console.log(error);
              // });
          }
          return null;
      }).catch((error) =&gt; {
          console.log(error);
      });
  });
  </code></pre>