public void deRegisterFragmentInstanceFromQueryMemoryMap(
      String queryId, String fragmentInstanceId, boolean forceDeregister) {
    Map<String, Map<String, Long>> queryRelatedMemory = queryMemoryReservations.get(queryId);
    if (queryRelatedMemory != null) {
      Map<String, Long> fragmentRelatedMemory = queryRelatedMemory.get(fragmentInstanceId);
      boolean hasPotentialMemoryLeak = false;
      // fragmentRelatedMemory could be null if the FI has not reserved any memory(For example,
      // next() of root operator returns no data)
      if (fragmentRelatedMemory != null) {
        hasPotentialMemoryLeak =
            fragmentRelatedMemory.values().stream().anyMatch(value -> value != 0L);
      }
      if (!forceDeregister && hasPotentialMemoryLeak) {
        // If hasPotentialMemoryLeak is true, it means that LocalSourceChannel/LocalSourceHandles
        // have not been closed.
        // We should wait for them to be closed. Make sure this method is called again with
        // forceDeregister == true, after all LocalSourceChannel/LocalSourceHandles are closed.
        return;
      }
      synchronized (queryMemoryReservations) {
        queryRelatedMemory.remove(fragmentInstanceId);
        if (queryRelatedMemory.isEmpty()) {
          queryMemoryReservations.remove(queryId);
        }
      }
      if (hasPotentialMemoryLeak) {
        // hasPotentialMemoryLeak means that fragmentRelatedMemory is not null
        List<Map.Entry<String, Long>> invalidEntryList =
            fragmentRelatedMemory.entrySet().stream()
                .filter(entry -> entry.getValue() != 0L)
                .collect(Collectors.toList());
        throw new MemoryLeakException(
            String.format(
                "PlanNode related memory is not zero when trying to deregister FI from query memory pool. QueryId is : %s, FragmentInstanceId is : %s, Non-zero PlanNode related memory is : %s.",
                queryId, fragmentInstanceId, invalidEntryList));
      }
    }
  }
If all fragmentInstanceIds related to one queryId have been registered, when the last fragment
instance is deregister, the queryId can be cleared.

<p>If some fragmentInstanceIds have not been registered when queryId is cleared, they will
register queryId again with lock, so there is no concurrency problem.

@throws MemoryLeakException throw {@link MemoryLeakException}
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/memory/MemoryPool.java