@RestrictedApi(explanation = "Should only be called in tests", link = "",
      allowedOnPath = ".*/StoreFileListFile.java|.*/src/test/.*")
  static void write(FileSystem fs, Path file, StoreFileList storeFileList) throws IOException {
    byte[] data = storeFileList.toByteArray();
    CRC32 crc32 = new CRC32();
    crc32.update(data);
    int checksum = (int) crc32.getValue();
    // 4 bytes length at the beginning, plus 4 bytes checksum
    try (FSDataOutputStream out = fs.create(file, true)) {
      out.writeInt(data.length);
      out.write(data);
      out.writeInt(checksum);
    }
  }
To fully avoid listing, here we use two files for tracking. When loading, we will try to read
both the two files, if only one exists, we will trust this one, if both exist, we will compare
the timestamp to see which one is newer and trust that one. And we will record in memory that
which one is trusted by us, and when we need to update the store file list, we will write to the
other file.
<p/>
So in this way, we could avoid listing when we want to load the store file list file.
<p/>
To prevent loading partial file, we use the first 4 bytes as file length, and also append a 4
bytes crc32 checksum at the end. This is because the protobuf message parser sometimes can return
without error on partial bytes if you stop at some special points, but the return message will
have incorrect field value. We should try our best to prevent this happens because loading an
incorrect store file list file usually leads to data loss.
<p/>
To prevent failing silently while downgrading, where we may miss some newly introduced fields in
{@link StoreFileList} which are necessary, we introduce a 'version' field in
{@link StoreFileList}. If we find out that we are reading a {@link StoreFileList} with higher
version, we will fail immediately and tell users that you need extra steps while downgrading, to
prevent potential data loss.
hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileListFile.java