docs: synchronized int flush(long txnId)
      {
          if (tmLog.isDebugEnabled()) {
              tmLog.debug("Flush backlog with txnId:" + TxnEgo.txnIdToString(txnId) +
                      ", backlog head txnId is:" + (m_backlog.isEmpty()? "empty" : TxnEgo.txnIdToString(m_backlog.getFirst().getTxnId()))
                      );
          }
  
          int offered = 0;
          // If the first entry of the backlog is a completed transaction, clear it so it no longer
          // blocks the backlog then iterate the backlog for more work.
          //
          // Note the kooky corner case where a multi-part transaction can actually have multiple outstanding
          // tasks. At first glance you would think that because the relationship is request response there
          // can be only one outstanding task for a given multi-part transaction.
          //
          // That isn't true.
          //
          // A rollback can cause there to be a fragment task as well as a rollback
          // task. The rollback is generated asynchronously by another partition.
          // If we don't flush all the associated tasks now then flush won't be called again because it is waiting
          // for the complete transaction task that is languishing in the queue to do the flush post multi-part.
          // It can't be called eagerly because that would destructively flush single parts as well.
          if (m_backlog.isEmpty() || !m_backlog.getFirst().getTransactionState().isDone()) {
              return offered;
          }
  
          // Add a guard to protect the scenario that backlog been flushed multiple times for same txnId
          if (m_backlog.getFirst().getTxnId() != txnId) {
              return offered;
          }
  
          m_backlog.removeFirst();
          Iterator<TransactionTask> iter = m_backlog.iterator();
          while (iter.hasNext()) {
              TransactionTask task = iter.next();
              long lastQueuedTxnId = task.getTxnId();
              if (task.needCoordination() && m_scoreboardEnabled) {
                  coordinatedTaskQueueOffer(task);
              } else {
                  taskQueueOffer(task);
              }
              ++offered;
              if (task.getTransactionState().isSinglePartition()) {
                  // single part can be immediately removed and offered
                  iter.remove();
                  continue;
              }
              else {
                  // leave the mp fragment at the head of the backlog but
                  // iterate and take care of the kooky case explained above.
                  while (iter.hasNext()) {
                      task = iter.next();
                      if (task.getTxnId() == lastQueuedTxnId) {
                          iter.remove();
                          if (task.needCoordination() && m_scoreboardEnabled) {
                              coordinatedTaskQueueOffer(task);
                          } else {
                              taskQueueOffer(task);
                          }
                          ++offered;
                      }
                  }
                  break;
              }
          }
          return offered;
      }