title: Firebase Transaction: When data is downloaded and why every ValueEventListener get called with null when a transation is running
body: <p>I have 2 questions related to Firebase's transaction in the real-time database. It will be easier to explain with an example. This is just an example, not my real code. So, do not worry if there are some compile errors.</p>
  <p>Let say I have a building. In this building, there are have some data. I have an array of floors. Each floor can have a counter of how many chairs there are on this floor. A client can have a lot of floors, so I do not want to load all of them. I just load the ones I need for this client. I need to know how many chairs there are in total even if I do not load them all. The rules can look like this:</p>
  <pre><code>&quot;...&quot;: 
  {
  
      &quot;building&quot;:
      {
          &quot;...&quot;: 
          {
  
          },
          
          &quot;totalNbChairs&quot;: 
          {   
            &quot;.validate&quot;: &quot;newData.isNumber()&quot;
          },
          
          &quot;floors&quot;: 
          {  
               &quot;$floorId&quot;: 
              {
                  &quot;nbChairs&quot;: 
                  {   
                    &quot;.validate&quot;: &quot;newData.isNumber()&quot;
                  },
                  
                  &quot;...&quot;: 
                  {
  
                  },
              },
          },
      },
  },
  </code></pre>
  <p>As I said, this is just an example, not my actual code. DO not worry about code issues.</p>
  <p>My clients can connect on multiple devices, so I need to use transactions to adjust the &quot;totalNbChairs&quot; when a floor changes his &quot;nbChairs&quot;. Important, I need to set the actual number of chairs on the floor, not just decrease a value. If the &quot;nbChairs&quot; is 10 for a floor and the client set &quot;8&quot; on 2 devices at the same time, I can not do &quot;-2&quot; on both devices at the same time.</p>
  <p>The transaction will look like this</p>
  <pre><code>void SetNbChair(String floorId, long nbChairsToSet){
      FirebaseDatabase.getInstance().getReference()
      .child(&quot;...&quot;)
      .child(&quot;building&quot;)
      .runTransaction(new Transaction.Handler() {
              @Override
              public Transaction.Result doTransaction(MutableData mutableData) {
                  
                  //first I need to know how many chairs I have right now on the floor
                  MutableData nbChairMutableData = mutableData.child(&quot;floors&quot;).child(floorId).child(&quot;nbChairs&quot;);
                  Long nbChairLong = (Long)nbChairMutableData.getValue();
                  long nbChair = 0;
                  if(nbChairLong != null){
                      nbChair = nbChairLong;
                  }
                  
                  long diff = nbChairsToSet - nbChair;
                  
                  //now I can update the number of chair in the floor
                  nbChairMutableData.setValue(nbChairsToSet);
                  
                  //Update the totalNbChairs
                  MutableData totalNbCHairsMutableData = mutableData.child(&quot;totalNbChairs&quot;);
                  Long previousTotalChairLong = (Long)totalNbCHairsMutableData.getValue();
                  long totalChair = 0;
                  if(previousTotalChairLong != null){
                      totalChair = previousTotalChairLong;
                  }
                  
                  totalChair += diff;
                  
                  //update the value
                  totalNbCHairsMutableData.setValue(totalChair);
                  
              }
              
               @Override
              public void onComplete(DatabaseError databaseError, boolean committed,
                                     DataSnapshot currentData) {
  
                  ...
              }
          });
  }
  </code></pre>
  <p>My first question is: When are downloaded the data I need to get on the client side? Because for what I see, 2 things can happen.</p>
  <p>First, when I do this</p>
  <pre><code>FirebaseDatabase.getInstance().getReference()
      .child(&quot;...&quot;)
      .child(&quot;building&quot;)
      .runTransaction
  </code></pre>
  <p>It's possible Firebase downloads everything in (&quot;.../building&quot;). If this is the case, this is pretty bad for me. I do not want to download everything. So if all the data is downloaded for this transaction, this is really bad. If this is the case, does anyone have an idea how I can do this transaction without download all the floors?</p>
  <p>But, it's also possible Firebase downloads the data when I do this</p>
  <pre><code>Long nbChairLong = (Long)nbChairMutableData.getValue();
  </code></pre>
  <p>In this case, it's far better but not perfect. In this case, I need to download &quot;nbChairs&quot;. Wait to get it. After, I need to download &quot;totalNbChairs&quot; and wait to get it. If firebase downloads the data when we do a getValue(). Can we batch all the getValue I need in a single call to avoid waiting to download twice?</p>
  <p>But I may be wrong and firebase does something else. Can someone explain to me when and what firebase downloads to the client so I will not have a huge surprise?</p>
  <p>Now the second question. I implemented the version I show. But I can not use it before I know the answer to my first question. But, I still did some tests. Pretty fast, I found out my &quot;transaction&quot; callback got some &quot;null&quot; event if there is data in the database. Ok, the documentation said it was expected behavior. Ok, no problem with that. I protected my code and I have something like this</p>
  <pre><code>if(myMandatoryData == null){
      return Transaction.success(mutableData);
  }
  </code></pre>
  <p>and, yes, the first time, my early return is called and the function is recalled. The data is valid the second time. Ok, seems fine, but... and a BIG BUT! I noticed something pretty bad. Something to mention, I have some ValueEventListener active to know when the data changed in the database, So I have some stuff like this</p>
  <pre><code>databaseReference.addValueEventListener( new ValueEventListener(){
  
              @Override
              public void onDataChange(DataSnapshot dataSnapshot) {
                      ...
             
              }
  
              @Override
              public void onCancelled(DatabaseError databaseError) {
                  ...
              }
          } );
  </code></pre>
  <p>After my early return, every &quot;onDataChange&quot; on every ValueEventListener is called with &quot;null&quot;. So, my code in the callback handles this like if the data was deleted. So, I got an unexpected result in my Ui, it's like someone deleted all my data. When the transaction retries and has the data, the  &quot;onDataChange&quot; is recalled with the valid data. But until it does, my UI just shows like there is nothing in the database. Am I supposed to cancel every ValueEventListener when I start a simple transaction? This seems pretty bad. I do not want to cancel them all. Also, I do not want to redownload all the data after I restart them when the transaction is done. I do not want to add a hack to ignore deleted data while a transaction is running in every ValueEventListener. I can miss if some data is really deleted from another device when the transaction is running. What Am I supposed to do at this point?</p>
  <p>Thanks</p>