@SuppressWarnings("OptionalGetWithoutIsPresent") // iterating the index, must present
  private FileDirtyInfo selectFileBaseOnDirtyData(TsFileResource resource) throws IOException {

    Collection<ModEntry> modifications = resource.getAllModEntries();
    ITimeIndex timeIndex = resource.getTimeIndex();
    if (timeIndex instanceof FileTimeIndex) {
      timeIndex = CompactionUtils.buildDeviceTimeIndex(resource);
    }
    Set<IDeviceID> deletedDevices = new HashSet<>();
    boolean hasExpiredTooLong = false;
    long currentTime = CommonDateTimeUtils.currentTime();

    for (IDeviceID device : ((ArrayDeviceTimeIndex) timeIndex).getDevices()) {
      // check expired device by ttl
      // TODO: remove deviceId conversion

      long ttl;
      String tableName = device.getTableName();
      if (tableName.startsWith("root.")) {
        ttl = DataNodeTTLCache.getInstance().getTTLForTree(device);
      } else {
        ttl = DataNodeTTLCache.getInstance().getTTLForTable(storageGroupName, tableName);
      }
      boolean hasSetTTL = ttl != Long.MAX_VALUE;

      long endTime = timeIndex.getEndTime(device).get();
      boolean isDeleted =
          !timeIndex.isDeviceAlive(device, ttl)
              || isDeviceDeletedByMods(
                  modifications, device, timeIndex.getStartTime(device).get(), endTime);
      if (hasSetTTL) {
        if (!isDeleted) {
          // For devices with TTL set, all data must expire in order to meet the conditions for
          // being selected.
          return new FileDirtyInfo(DirtyStatus.NOT_SATISFIED);
        }

        if (currentTime > endTime) {
          long outdatedTimeDiff = currentTime - endTime;
          if (endTime < 0 && outdatedTimeDiff < currentTime) {
            // overflow, like 100 - Long.MIN
            outdatedTimeDiff = Long.MAX_VALUE;
          }
          long ttlThreshold = 3 * ttl > ttl ? ttl : Long.MAX_VALUE;
          hasExpiredTooLong =
              hasExpiredTooLong
                  || outdatedTimeDiff > Math.min(config.getMaxExpiredTime(), ttlThreshold);
        } // else hasExpiredTooLong unchanged
      }

      if (isDeleted) {
        deletedDevices.add(device);
      }
    }

    double deletedDeviceRatio =
        ((double) deletedDevices.size()) / ((ArrayDeviceTimeIndex) timeIndex).getDevices().size();
    if (deletedDeviceRatio == 1d) {
      // the whole file is completely dirty
      return new FileDirtyInfo(DirtyStatus.FULLY_DIRTY);
    }
    hasExpiredTooLong = config.getMaxExpiredTime() != Long.MAX_VALUE && hasExpiredTooLong;
    if (hasExpiredTooLong || deletedDeviceRatio >= config.getExpiredDataRatio()) {
      // evaluate dirty data size in the tsfile
      return new FileDirtyInfo(
          PARTIALLY_DIRTY, (long) (deletedDeviceRatio * resource.getTsFileSize()));
    }
    return new FileDirtyInfo(DirtyStatus.NOT_SATISFIED);
  }
Only when all devices with ttl are deleted may they be selected. On the basic of the previous,
only when the number of deleted devices exceeds the threshold or has expired for too long will
they be selected.

@return dirty status means the status of current resource.
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/selector/impl/SettleSelectorImpl.java