code: private void readUnread(CommitData cd, Consumer<Entry<Key, Value>> locksSeen) {
      // TODO make async
      // TODO need to keep track of ranges read (not ranges passed in, but actual data read... user
      // may not iterate over entire range
      Map<Bytes, Set<Column>> columnsToRead = new HashMap<>();
  
      for (Entry<Bytes, Set<Column>> entry : cd.getRejected().entrySet()) {
        Set<Column> rowColsRead = columnsRead.get(entry.getKey());
        if (rowColsRead == null) {
          columnsToRead.put(entry.getKey(), entry.getValue());
        } else {
          HashSet<Column> colsToRead = new HashSet<>(entry.getValue());
          colsToRead.removeAll(rowColsRead);
          if (!colsToRead.isEmpty()) {
            columnsToRead.put(entry.getKey(), colsToRead);
          }
        }
      }
  
      for (Entry<Bytes, Set<Column>> entry : columnsToRead.entrySet()) {
        getImpl(entry.getKey(), entry.getValue(), locksSeen);
      }
    }
docs: This function helps handle the following case
  
  <OL>
  <LI>TX1 locls r1 col1
  <LI>TX1 fails before unlocking
  <LI>TX2 attempts to write r1:col1 w/o reading it
  </OL>
  
  <p>
  In this case TX2 would not roll back TX1, because it never read the column. This function
  attempts to handle this case if TX2 fails. Only doing this in case of failures is cheaper than
  trying to always read unread columns.
  
  @param cd Commit data