class TransportSwapRelationsAction extends TransportMasterNodeAction<SwapRelationsRequest, AcknowledgedResponse> {

    private final SwapRelationsOperation swapRelationsOperation;
    private final ActiveShardsObserver activeShardsObserver;

    @Inject
    public TransportSwapRelationsAction(Settings settings,
                                        TransportService transportService,
                                        ClusterService clusterService,
                                        ThreadPool threadPool,
                                        DDLClusterStateService ddlClusterStateService,
                                        AllocationService allocationService) {
        super(
            "internal:crate:sql/alter/cluster/indices",
            transportService,
            clusterService,
            threadPool,
            SwapRelationsRequest::new
        );
        this.activeShardsObserver = new ActiveShardsObserver(clusterService);
        this.swapRelationsOperation = new SwapRelationsOperation(
            allocationService, ddlClusterStateService);
    }

    @Override
    protected String executor() {
        return ThreadPool.Names.SAME;
    }

    @Override
    protected AcknowledgedResponse read(StreamInput in) throws IOException {
        return new AcknowledgedResponse(in);
    }

    @Override
    protected void masterOperation(SwapRelationsRequest request,
                                   ClusterState state,
                                   ActionListener<AcknowledgedResponse> listener) throws Exception {
        AtomicReference<String[]> indexNamesAfterRelationSwap = new AtomicReference<>(null);
        ActionListener<AcknowledgedResponse> waitForShardsListener = ActionListeners.waitForShards(
            listener,
            activeShardsObserver,
            request.ackTimeout(),
            () -> logger.info("Switched name of relations, but the operation timed out waiting for enough shards to be started"),
            indexNamesAfterRelationSwap::get
        );
        AckedClusterStateUpdateTask<AcknowledgedResponse> updateTask =
            new AckedClusterStateUpdateTask<AcknowledgedResponse>(Priority.HIGH, request, waitForShardsListener) {

                @Override
                public ClusterState execute(ClusterState currentState) throws Exception {
                    if (logger.isInfoEnabled()) {
                        Iterable<String> swapActions = request.swapActions().stream()
                            .map(x -> x.source().fqn() + " <-> " + x.target().fqn())
                            ::iterator;
                        logger.info("Swapping tables [{}]", String.join(", ", swapActions));
                    }
                    SwapRelationsOperation.UpdatedState newState = swapRelationsOperation.execute(currentState, request);
                    indexNamesAfterRelationSwap.set(newState.newIndices.toArray(new String[0]));
                    return newState.newState;
                }

                @Override
                protected AcknowledgedResponse newResponse(boolean acknowledged) {
                    return new AcknowledgedResponse(acknowledged);
                }
            };
        clusterService.submitStateUpdateTask("swap-relations", updateTask);
    }

    @Override
    protected ClusterBlockException checkBlock(SwapRelationsRequest request, ClusterState state) {<FILL_FUNCTION_BODY>}
}

        Set<String> affectedIndices = new HashSet<>();
        Metadata metadata = state.metadata();
        for (RelationNameSwap swapAction : request.swapActions()) {
            affectedIndices.addAll(Arrays.asList(IndexNameExpressionResolver.concreteIndexNames(
                metadata, IndicesOptions.LENIENT_EXPAND_OPEN, swapAction.source().indexNameOrAlias())));
            affectedIndices.addAll(Arrays.asList(IndexNameExpressionResolver.concreteIndexNames(
                metadata, IndicesOptions.LENIENT_EXPAND_OPEN, swapAction.target().indexNameOrAlias())));
        }
        for (RelationName dropRelation : request.dropRelations()) {
            affectedIndices.addAll(Arrays.asList(IndexNameExpressionResolver.concreteIndexNames(
                metadata, IndicesOptions.LENIENT_EXPAND_OPEN, dropRelation.indexNameOrAlias())));
        }
        return state.blocks().indicesBlockedException(ClusterBlockLevel.METADATA_READ, affectedIndices.toArray(new String[0]));
    
<methods><variables>protected final non-sealed org.elasticsearch.cluster.service.ClusterService clusterService,private final non-sealed java.lang.String executor,protected final non-sealed org.elasticsearch.threadpool.ThreadPool threadPool,protected final non-sealed org.elasticsearch.transport.TransportService transportService