https://github.com/elastic/elasticsearch/pull/97304 removes several places that might do an expensive rebuild of the cluster state via `getStateForMasterService`. There's one other call we should be able to remove, in `JoinValidationService`, but it's a little tricky since we rely on seeing the last-accepted state for the cluster UUID validation, and it's not obviously ok to expose the last-accepted state without the no-master block.

This is a fairly minor issue because we do not do this join validation during elections, only if we're the elected master, and in that case `getStateForMasterService` is cheap. But that's not guaranteed, and we should strengthen this to avoid the that expensive step whenever possible.
server/src/main/java/org/elasticsearch/cluster/coordination/JoinValidationService.java
import org.elasticsearch.node.NodeClosedException;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.BytesTransportRequest;
import org.elasticsearch.transport.NodeNotConnectedException;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.transport.TransportRequestOptions;
import org.elasticsearch.transport.TransportResponse;
import org.elasticsearch.transport.TransportService;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
/**
 * Coordinates the join validation process.
 * <p>
 * When a node requests to join an existing cluster, the master first sends it a copy of a recent cluster state to ensure that the new node
 * can make sense of it (e.g. it has all the plugins it needs to even deserialize the state). Cluster states can be expensive to serialize:
 * they are large, so we compress them, but the compression takes extra CPU. Also there may be many nodes all joining at once (e.g. after a
 * full cluster restart or the healing of a large network partition). This component caches the serialized and compressed state that was
 * sent to one joining node and reuses it to validate other join requests that arrive within the cache timeout, avoiding the need to
 * allocate memory for each request and repeat all that serialization and compression work each time.
 */
public class JoinValidationService {