public static void atomicMoveWithFallback(Path source, Path target, boolean needFlushParentDir) throws IOException {
        try {
            Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
        } catch (IOException outer) {
            try {
                log.warn("Failed atomic move of {} to {} retrying with a non-atomic move", source, target, outer);
                Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
                log.debug("Non-atomic move of {} to {} succeeded after atomic move failed", source, target);
            } catch (IOException inner) {
                inner.addSuppressed(outer);
                throw inner;
            }
        } finally {
            if (needFlushParentDir) {
                flushDir(target.toAbsolutePath().normalize().getParent());
            }
        }
    }
Attempts to move source to target atomically and falls back to a non-atomic move if it fails.
This function allows callers to decide whether to flush the parent directory. This is needed
when a sequence of atomicMoveWithFallback is called for the same directory and we don't want
to repeatedly flush the same parent directory.

@throws IOException if both atomic and non-atomic moves fail,
or parent dir flush fails if needFlushParentDir is true.
clients/src/main/java/org/apache/kafka/common/utils/Utils.java