@Override
    public int cleanUpOneFileCache(FileCache<FileData> fc, int leftToCheck, long endTime,
        Ref<Boolean> isPastEndTime) throws InterruptedException {
      FileData fd = fc.getCache();
      fd.rwLock.writeLock().lock();
      try {
        for (StripeData sd : fd.stripes) {
          for (int colIx = 0; colIx < sd.data.length; ++colIx) {
            LlapSerDeDataBuffer[][] colData = sd.data[colIx];
            if (colData == null) continue;
            boolean hasAllData = true;
            for (int j = 0; (j < colData.length) && hasAllData; ++j) {
              LlapSerDeDataBuffer[] streamData = colData[j];
              if (streamData == null) continue;
              for (int k = 0; k < streamData.length; ++k) {
                LlapSerDeDataBuffer buf = streamData[k];
                hasAllData = hasAllData && lockBuffer(buf, false);
                if (!hasAllData) break;
                unlockBuffer(buf, true);
              }
            }
            if (!hasAllData) {
              handleRemovedColumnData(colData);
              sd.data[colIx] = null;
            }
          }
        }
      } finally {
        fd.rwLock.writeLock().unlock();
      }
      return leftToCheck - 1;
    }
RW lock ensures we have a consistent view of the file data, which is important given that
we generate "stripe" boundaries arbitrarily. Reading buffer data itself doesn't require
that this lock is held; however, everything else in stripes list does.
TODO: make more granular? We only care that each one reader sees consistent boundaries.
      So, we could shallow-copy the stripes list, then have individual locks inside each.
llap-server/src/java/org/apache/hadoop/hive/llap/cache/SerDeLowLevelCacheImpl.java