public List<Node> performOn(NodeList nodes, Predicate<Node> filter, BiFunction<Node, Mutex, Node> action) {
        List<Node> resultingNodes = new ArrayList<>();
        nodes.matching(filter).stream().collect(groupingBy(Nodes::applicationIdForLock))
             .forEach((applicationId, nodeList) -> { // Grouped only to reduce number of lock acquire/release cycles.
                 try (NodeMutexes locked = lockAndGetAll(nodeList, Optional.empty())) {
                     for (NodeMutex node : locked.nodes())
                         if (filter.test(node.node()))
                             resultingNodes.add(action.apply(node.node(), node));
                 }
             });
        return resultingNodes;
    }
Performs an operation requiring locking on all given nodes.

@param action the action to perform
@return the set of nodes on which the action was performed, as they became as a result of the operation
node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java