Repository: qdrant/qdrant
File: src/common/metrics.rs (Rust)

Code before:
                Ok(proc) => proc,
                // Ignore children that don't exist anymore.
                Err(ProcError::NotFound(_)) => continue,
                Err(err) => return Err(err),
            };

            min += stat.minflt + stat.cminflt;
            maj += stat.majflt + stat.cmajflt;
        }
        Ok((min, maj))
    }

    /// Returns a list of all children of a given process. This works recursively, including grandchildren, etc.
    fn child_processes_helper(pid: Pid) -> Result<AHashSet<Pid>, ProcError> {
        let mut pids = AHashSet::default();
        child_processes_recursive(pid, &mut pids)?;

        // Exclude parent Pid from results
        pids.remove(&pid);

        Ok(pids)
    }

    /// Recursively collects all children of a process, specified by `pid`.
    fn child_processes_recursive(pid: Pid, pids: &mut AHashSet<Pid>) -> Result<(), ProcError> {
        for child_pid in child_processes(pid)? {
            let is_new = pids.insert(child_pid);

            // If PID is new we recurse into it
            if is_new {
                child_processes_recursive(child_pid, pids)?;
            }
        }

        Ok(())
    }

    /// Returns all new child processes of a parent process, specified by `parent_pid`.
    fn child_processes(parent_pid: Pid) -> Result<AHashSet<Pid>, ProcError> {
        let process = match Process::new(parent_pid) {
            Ok(proc) => proc,
            // Ignore children that don't exist anymore.
            Err(ProcError::NotFound(_)) => return Ok(AHashSet::default()),
            Err(err) => return Err(err),
        };

        process
            .tasks()?
            .filter_map(|task| {
                let children = task.and_then(|task| task.children());
                match children {

Review comment (performance):
Very minor performance nit: `child_processes` allocates a vector that would be dropped right after iterating. Tho, I think it's far from being a bottleneck.

Code after:
        Ok((min, maj))
    }

    /// Returns a list of all children of a given process. This works recursively, including grandchildren, etc.
    fn child_processes_helper(pid: Pid) -> Result<AHashSet<Pid>, ProcError> {
        let mut pids = AHashSet::default();
        child_processes_recursive(pid, &mut pids)?;

        // We don't expect to include parent Pid
        debug_assert!(!pids.contains(&pid));

        Ok(pids)
    }

    /// Recursively collects all children of a process, specified by `pid`.
    fn child_processes_recursive(pid: Pid, pids: &mut AHashSet<Pid>) -> Result<(), ProcError> {
        for child_pid in child_processes(pid) {
            let child_pid = child_pid?;

            let is_new = pids.insert(child_pid);

            // If PID is new we recurse into it
            if is_new {
                child_processes_recursive(child_pid, pids)?;
            }
        }

        Ok(())
    }

    /// Returns all new child processes of a parent process, specified by `parent_pid`.
    fn child_processes(parent_pid: Pid) -> Box<dyn Iterator<Item = Result<Pid, ProcError>>> {
        let process = match Process::new(parent_pid) {
            Ok(proc) => proc,
            // Ignore children that don't exist anymore.
            Err(ProcError::NotFound(_)) => return Box::new(std::iter::empty()),
            Err(err) => return Box::new(std::iter::once(Err(err))),
        };

        let tasks = match process.tasks() {
            Ok(tasks) => tasks,
            Err(err) => return Box::new(std::iter::once(Err(err))),
        };

        let pids = tasks
            .filter_map(|task| {
                let children = task.and_then(|task| task.children());
                match children {
                    Ok(children) => Some(Ok(children)),
                    // Filter out tasks/children that might not exist anymore.
                    Err(ProcError::NotFound(_)) => None,