Search in sources :

Example 1 with AckedClusterStateUpdateTask

use of org.opensearch.cluster.AckedClusterStateUpdateTask in project OpenSearch by opensearch-project.

the class TransportClusterUpdateSettingsAction method masterOperation.

@Override
protected void masterOperation(final ClusterUpdateSettingsRequest request, final ClusterState state, final ActionListener<ClusterUpdateSettingsResponse> listener) {
    final SettingsUpdater updater = new SettingsUpdater(clusterSettings);
    clusterService.submitStateUpdateTask("cluster_update_settings", new AckedClusterStateUpdateTask<ClusterUpdateSettingsResponse>(Priority.IMMEDIATE, request, listener) {

        private volatile boolean changed = false;

        @Override
        protected ClusterUpdateSettingsResponse newResponse(boolean acknowledged) {
            return new ClusterUpdateSettingsResponse(acknowledged, updater.getTransientUpdates(), updater.getPersistentUpdate());
        }

        @Override
        public void onAllNodesAcked(@Nullable Exception e) {
            if (changed) {
                reroute(true);
            } else {
                super.onAllNodesAcked(e);
            }
        }

        @Override
        public void onAckTimeout() {
            if (changed) {
                reroute(false);
            } else {
                super.onAckTimeout();
            }
        }

        private void reroute(final boolean updateSettingsAcked) {
            // so we should *not* execute the reroute.
            if (!clusterService.state().nodes().isLocalNodeElectedMaster()) {
                logger.debug("Skipping reroute after cluster update settings, because node is no longer master");
                listener.onResponse(new ClusterUpdateSettingsResponse(updateSettingsAcked, updater.getTransientUpdates(), updater.getPersistentUpdate()));
                return;
            }
            // The reason the reroute needs to be send as separate update task, is that all the *cluster* settings are encapsulate
            // in the components (e.g. FilterAllocationDecider), so the changes made by the first call aren't visible
            // to the components until the ClusterStateListener instances have been invoked, but are visible after
            // the first update task has been completed.
            clusterService.submitStateUpdateTask("reroute_after_cluster_update_settings", new AckedClusterStateUpdateTask<ClusterUpdateSettingsResponse>(Priority.URGENT, request, listener) {

                @Override
                public boolean mustAck(DiscoveryNode discoveryNode) {
                    // we wait for the reroute ack only if the update settings was acknowledged
                    return updateSettingsAcked;
                }

                @Override
                protected // update settings was acknowledged
                ClusterUpdateSettingsResponse newResponse(boolean acknowledged) {
                    return new ClusterUpdateSettingsResponse(updateSettingsAcked && acknowledged, updater.getTransientUpdates(), updater.getPersistentUpdate());
                }

                @Override
                public void onNoLongerMaster(String source) {
                    logger.debug("failed to preform reroute after cluster settings were updated - current node is no longer a master");
                    listener.onResponse(new ClusterUpdateSettingsResponse(updateSettingsAcked, updater.getTransientUpdates(), updater.getPersistentUpdate()));
                }

                @Override
                public void onFailure(String source, Exception e) {
                    // if the reroute fails we only log
                    logger.debug(() -> new ParameterizedMessage("failed to perform [{}]", source), e);
                    listener.onFailure(new OpenSearchException("reroute after update settings failed", e));
                }

                @Override
                public ClusterState execute(final ClusterState currentState) {
                    // now, reroute in case things that require it changed (e.g. number of replicas)
                    return allocationService.reroute(currentState, "reroute after cluster update settings");
                }
            });
        }

        @Override
        public void onFailure(String source, Exception e) {
            logger.debug(() -> new ParameterizedMessage("failed to perform [{}]", source), e);
            super.onFailure(source, e);
        }

        @Override
        public ClusterState execute(final ClusterState currentState) {
            final ClusterState clusterState = updater.updateSettings(currentState, clusterSettings.upgradeSettings(request.transientSettings()), clusterSettings.upgradeSettings(request.persistentSettings()), logger);
            changed = clusterState != currentState;
            return clusterState;
        }
    });
}
Also used : ClusterState(org.opensearch.cluster.ClusterState) DiscoveryNode(org.opensearch.cluster.node.DiscoveryNode) AckedClusterStateUpdateTask(org.opensearch.cluster.AckedClusterStateUpdateTask) ParameterizedMessage(org.apache.logging.log4j.message.ParameterizedMessage) OpenSearchException(org.opensearch.OpenSearchException) ClusterBlockException(org.opensearch.cluster.block.ClusterBlockException) IOException(java.io.IOException) OpenSearchException(org.opensearch.OpenSearchException)

Example 2 with AckedClusterStateUpdateTask

use of org.opensearch.cluster.AckedClusterStateUpdateTask in project OpenSearch by opensearch-project.

the class RepositoriesService method registerRepository.

/**
 * Registers new repository in the cluster
 * <p>
 * This method can be only called on the master node. It tries to create a new repository on the master
 * and if it was successful it adds new repository to cluster metadata.
 *
 * @param request  register repository request
 * @param listener register repository listener
 */
public void registerRepository(final PutRepositoryRequest request, final ActionListener<ClusterStateUpdateResponse> listener) {
    assert lifecycle.started() : "Trying to register new repository but service is in state [" + lifecycle.state() + "]";
    final RepositoryMetadata newRepositoryMetadata = new RepositoryMetadata(request.name(), request.type(), request.settings());
    validate(request.name());
    final ActionListener<ClusterStateUpdateResponse> registrationListener;
    if (request.verify()) {
        registrationListener = ActionListener.delegateFailure(listener, (delegatedListener, clusterStateUpdateResponse) -> {
            if (clusterStateUpdateResponse.isAcknowledged()) {
                // The response was acknowledged - all nodes should know about the new repository, let's verify them
                verifyRepository(request.name(), ActionListener.delegateFailure(delegatedListener, (innerDelegatedListener, discoveryNodes) -> innerDelegatedListener.onResponse(clusterStateUpdateResponse)));
            } else {
                delegatedListener.onResponse(clusterStateUpdateResponse);
            }
        });
    } else {
        registrationListener = listener;
    }
    // Trying to create the new repository on master to make sure it works
    try {
        closeRepository(createRepository(newRepositoryMetadata, typesRegistry));
    } catch (Exception e) {
        registrationListener.onFailure(e);
        return;
    }
    clusterService.submitStateUpdateTask("put_repository [" + request.name() + "]", new AckedClusterStateUpdateTask<ClusterStateUpdateResponse>(request, registrationListener) {

        @Override
        protected ClusterStateUpdateResponse newResponse(boolean acknowledged) {
            return new ClusterStateUpdateResponse(acknowledged);
        }

        @Override
        public ClusterState execute(ClusterState currentState) {
            ensureRepositoryNotInUse(currentState, request.name());
            Metadata metadata = currentState.metadata();
            Metadata.Builder mdBuilder = Metadata.builder(currentState.metadata());
            RepositoriesMetadata repositories = metadata.custom(RepositoriesMetadata.TYPE);
            if (repositories == null) {
                logger.info("put repository [{}]", request.name());
                repositories = new RepositoriesMetadata(Collections.singletonList(new RepositoryMetadata(request.name(), request.type(), request.settings())));
            } else {
                boolean found = false;
                List<RepositoryMetadata> repositoriesMetadata = new ArrayList<>(repositories.repositories().size() + 1);
                for (RepositoryMetadata repositoryMetadata : repositories.repositories()) {
                    if (repositoryMetadata.name().equals(newRepositoryMetadata.name())) {
                        if (newRepositoryMetadata.equalsIgnoreGenerations(repositoryMetadata)) {
                            // Previous version is the same as this one no update is needed.
                            return currentState;
                        }
                        found = true;
                        repositoriesMetadata.add(newRepositoryMetadata);
                    } else {
                        repositoriesMetadata.add(repositoryMetadata);
                    }
                }
                if (!found) {
                    logger.info("put repository [{}]", request.name());
                    repositoriesMetadata.add(new RepositoryMetadata(request.name(), request.type(), request.settings()));
                } else {
                    logger.info("update repository [{}]", request.name());
                }
                repositories = new RepositoriesMetadata(repositoriesMetadata);
            }
            mdBuilder.putCustom(RepositoriesMetadata.TYPE, repositories);
            return ClusterState.builder(currentState).metadata(mdBuilder).build();
        }

        @Override
        public void onFailure(String source, Exception e) {
            logger.warn(() -> new ParameterizedMessage("failed to create repository [{}]", request.name()), e);
            super.onFailure(source, e);
        }

        @Override
        public boolean mustAck(DiscoveryNode discoveryNode) {
            // repository is created on both master and data nodes
            return discoveryNode.isMasterNode() || discoveryNode.isDataNode();
        }
    });
}
Also used : Metadata(org.opensearch.cluster.metadata.Metadata) ActionRunnable(org.opensearch.action.ActionRunnable) ThreadPool(org.opensearch.threadpool.ThreadPool) SnapshotsInProgress(org.opensearch.cluster.SnapshotsInProgress) HashMap(java.util.HashMap) SnapshotDeletionsInProgress(org.opensearch.cluster.SnapshotDeletionsInProgress) ParameterizedMessage(org.apache.logging.log4j.message.ParameterizedMessage) ClusterStateApplier(org.opensearch.cluster.ClusterStateApplier) Regex(org.opensearch.common.regex.Regex) Strings(org.opensearch.common.Strings) ArrayList(java.util.ArrayList) ConcurrentCollections(org.opensearch.common.util.concurrent.ConcurrentCollections) ClusterState(org.opensearch.cluster.ClusterState) DiscoveryNode(org.opensearch.cluster.node.DiscoveryNode) Map(java.util.Map) AckedClusterStateUpdateTask(org.opensearch.cluster.AckedClusterStateUpdateTask) ActionListener(org.opensearch.action.ActionListener) RepositoryCleanupInProgress(org.opensearch.cluster.RepositoryCleanupInProgress) Setting(org.opensearch.common.settings.Setting) TimeValue(org.opensearch.common.unit.TimeValue) DeleteRepositoryRequest(org.opensearch.action.admin.cluster.repositories.delete.DeleteRepositoryRequest) RepositoriesMetadata(org.opensearch.cluster.metadata.RepositoriesMetadata) Collection(java.util.Collection) RepositoryMetadata(org.opensearch.cluster.metadata.RepositoryMetadata) Set(java.util.Set) Settings(org.opensearch.common.settings.Settings) IOException(java.io.IOException) DiscoveryNodeRole(org.opensearch.cluster.node.DiscoveryNodeRole) TransportService(org.opensearch.transport.TransportService) Collectors(java.util.stream.Collectors) IOUtils(org.opensearch.core.internal.io.IOUtils) AbstractLifecycleComponent(org.opensearch.common.component.AbstractLifecycleComponent) List(java.util.List) Logger(org.apache.logging.log4j.Logger) Stream(java.util.stream.Stream) MeteredBlobStoreRepository(org.opensearch.repositories.blobstore.MeteredBlobStoreRepository) PutRepositoryRequest(org.opensearch.action.admin.cluster.repositories.put.PutRepositoryRequest) ClusterService(org.opensearch.cluster.service.ClusterService) RestoreInProgress(org.opensearch.cluster.RestoreInProgress) ClusterStateUpdateResponse(org.opensearch.cluster.ack.ClusterStateUpdateResponse) LogManager(org.apache.logging.log4j.LogManager) Collections(java.util.Collections) ClusterChangedEvent(org.opensearch.cluster.ClusterChangedEvent) ClusterState(org.opensearch.cluster.ClusterState) DiscoveryNode(org.opensearch.cluster.node.DiscoveryNode) Metadata(org.opensearch.cluster.metadata.Metadata) RepositoriesMetadata(org.opensearch.cluster.metadata.RepositoriesMetadata) RepositoryMetadata(org.opensearch.cluster.metadata.RepositoryMetadata) IOException(java.io.IOException) RepositoriesMetadata(org.opensearch.cluster.metadata.RepositoriesMetadata) RepositoryMetadata(org.opensearch.cluster.metadata.RepositoryMetadata) ArrayList(java.util.ArrayList) List(java.util.List) ParameterizedMessage(org.apache.logging.log4j.message.ParameterizedMessage) ClusterStateUpdateResponse(org.opensearch.cluster.ack.ClusterStateUpdateResponse)

Example 3 with AckedClusterStateUpdateTask

use of org.opensearch.cluster.AckedClusterStateUpdateTask in project OpenSearch by opensearch-project.

the class MasterServiceTests method testAcking.

public void testAcking() throws InterruptedException {
    final DiscoveryNode node1 = new DiscoveryNode("node1", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT);
    final DiscoveryNode node2 = new DiscoveryNode("node2", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT);
    final DiscoveryNode node3 = new DiscoveryNode("node3", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT);
    try (MasterService masterService = new MasterService(Settings.builder().put(ClusterName.CLUSTER_NAME_SETTING.getKey(), MasterServiceTests.class.getSimpleName()).put(Node.NODE_NAME_SETTING.getKey(), "test_node").build(), new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), threadPool)) {
        final ClusterState initialClusterState = ClusterState.builder(new ClusterName(MasterServiceTests.class.getSimpleName())).nodes(DiscoveryNodes.builder().add(node1).add(node2).add(node3).localNodeId(node1.getId()).masterNodeId(node1.getId())).blocks(ClusterBlocks.EMPTY_CLUSTER_BLOCK).build();
        final AtomicReference<ClusterStatePublisher> publisherRef = new AtomicReference<>();
        masterService.setClusterStatePublisher((e, pl, al) -> publisherRef.get().publish(e, pl, al));
        masterService.setClusterStateSupplier(() -> initialClusterState);
        masterService.start();
        // check that we don't time out before even committing the cluster state
        {
            final CountDownLatch latch = new CountDownLatch(1);
            publisherRef.set((clusterChangedEvent, publishListener, ackListener) -> publishListener.onFailure(new FailedToCommitClusterStateException("mock exception")));
            masterService.submitStateUpdateTask("test2", new AckedClusterStateUpdateTask<Void>(null, null) {

                @Override
                public ClusterState execute(ClusterState currentState) {
                    return ClusterState.builder(currentState).build();
                }

                @Override
                public TimeValue ackTimeout() {
                    return TimeValue.ZERO;
                }

                @Override
                public TimeValue timeout() {
                    return null;
                }

                @Override
                public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
                    fail();
                }

                @Override
                protected Void newResponse(boolean acknowledged) {
                    fail();
                    return null;
                }

                @Override
                public void onFailure(String source, Exception e) {
                    latch.countDown();
                }

                @Override
                public void onAckTimeout() {
                    fail();
                }
            });
            latch.await();
        }
        // check that we timeout if commit took too long
        {
            final CountDownLatch latch = new CountDownLatch(2);
            final TimeValue ackTimeout = TimeValue.timeValueMillis(randomInt(100));
            publisherRef.set((clusterChangedEvent, publishListener, ackListener) -> {
                publishListener.onResponse(null);
                ackListener.onCommit(TimeValue.timeValueMillis(ackTimeout.millis() + randomInt(100)));
                ackListener.onNodeAck(node1, null);
                ackListener.onNodeAck(node2, null);
                ackListener.onNodeAck(node3, null);
            });
            masterService.submitStateUpdateTask("test2", new AckedClusterStateUpdateTask<Void>(null, null) {

                @Override
                public ClusterState execute(ClusterState currentState) {
                    return ClusterState.builder(currentState).build();
                }

                @Override
                public TimeValue ackTimeout() {
                    return ackTimeout;
                }

                @Override
                public TimeValue timeout() {
                    return null;
                }

                @Override
                public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
                    latch.countDown();
                }

                @Override
                protected Void newResponse(boolean acknowledged) {
                    fail();
                    return null;
                }

                @Override
                public void onFailure(String source, Exception e) {
                    fail();
                }

                @Override
                public void onAckTimeout() {
                    latch.countDown();
                }
            });
            latch.await();
        }
    }
}
Also used : TestThreadPool(org.opensearch.threadpool.TestThreadPool) Level(org.apache.logging.log4j.Level) Version(org.opensearch.Version) OpenSearchException(org.opensearch.OpenSearchException) ThreadContext(org.opensearch.common.util.concurrent.ThreadContext) Matchers.hasKey(org.hamcrest.Matchers.hasKey) DiscoveryNode(org.opensearch.cluster.node.DiscoveryNode) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Map(java.util.Map) ClusterStatePublisher(org.opensearch.cluster.coordination.ClusterStatePublisher) AfterClass(org.junit.AfterClass) CyclicBarrier(java.util.concurrent.CyclicBarrier) TimeValue(org.opensearch.common.unit.TimeValue) OpenSearchTestCase(org.opensearch.test.OpenSearchTestCase) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Set(java.util.Set) ClusterStateTaskExecutor(org.opensearch.cluster.ClusterStateTaskExecutor) Settings(org.opensearch.common.settings.Settings) Nullable(org.opensearch.common.Nullable) Tuple(org.opensearch.common.collect.Tuple) FailedToCommitClusterStateException(org.opensearch.cluster.coordination.FailedToCommitClusterStateException) CountDownLatch(java.util.concurrent.CountDownLatch) List(java.util.List) ClusterStateUpdateTask(org.opensearch.cluster.ClusterStateUpdateTask) Matchers.equalTo(org.hamcrest.Matchers.equalTo) Matchers.anyOf(org.hamcrest.Matchers.anyOf) Matchers.containsString(org.hamcrest.Matchers.containsString) ClusterStateTaskListener(org.opensearch.cluster.ClusterStateTaskListener) DiscoveryNodes(org.opensearch.cluster.node.DiscoveryNodes) MockLogAppender(org.opensearch.test.MockLogAppender) BeforeClass(org.junit.BeforeClass) ThreadPool(org.opensearch.threadpool.ThreadPool) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) Priority(org.opensearch.common.Priority) HashMap(java.util.HashMap) Node(org.opensearch.node.Node) AtomicReference(java.util.concurrent.atomic.AtomicReference) ArrayList(java.util.ArrayList) ConcurrentMap(java.util.concurrent.ConcurrentMap) HashSet(java.util.HashSet) ClusterState(org.opensearch.cluster.ClusterState) AckedClusterStateUpdateTask(org.opensearch.cluster.AckedClusterStateUpdateTask) ClusterStateTaskConfig(org.opensearch.cluster.ClusterStateTaskConfig) ClusterSettings(org.opensearch.common.settings.ClusterSettings) ClusterBlocks(org.opensearch.cluster.block.ClusterBlocks) Before(org.junit.Before) Collections.emptyMap(java.util.Collections.emptyMap) Collections.emptySet(java.util.Collections.emptySet) Semaphore(java.util.concurrent.Semaphore) BrokenBarrierException(java.util.concurrent.BrokenBarrierException) BaseFuture(org.opensearch.common.util.concurrent.BaseFuture) LocalClusterUpdateTask(org.opensearch.cluster.LocalClusterUpdateTask) TestLogging(org.opensearch.test.junit.annotations.TestLogging) TimeUnit(java.util.concurrent.TimeUnit) ClusterName(org.opensearch.cluster.ClusterName) LogManager(org.apache.logging.log4j.LogManager) Collections(java.util.Collections) ClusterChangedEvent(org.opensearch.cluster.ClusterChangedEvent) FailedToCommitClusterStateException(org.opensearch.cluster.coordination.FailedToCommitClusterStateException) ClusterState(org.opensearch.cluster.ClusterState) DiscoveryNode(org.opensearch.cluster.node.DiscoveryNode) ClusterSettings(org.opensearch.common.settings.ClusterSettings) AtomicReference(java.util.concurrent.atomic.AtomicReference) Matchers.containsString(org.hamcrest.Matchers.containsString) CountDownLatch(java.util.concurrent.CountDownLatch) OpenSearchException(org.opensearch.OpenSearchException) FailedToCommitClusterStateException(org.opensearch.cluster.coordination.FailedToCommitClusterStateException) BrokenBarrierException(java.util.concurrent.BrokenBarrierException) AckedClusterStateUpdateTask(org.opensearch.cluster.AckedClusterStateUpdateTask) ClusterName(org.opensearch.cluster.ClusterName) ClusterStatePublisher(org.opensearch.cluster.coordination.ClusterStatePublisher) TimeValue(org.opensearch.common.unit.TimeValue)

Example 4 with AckedClusterStateUpdateTask

use of org.opensearch.cluster.AckedClusterStateUpdateTask in project OpenSearch by opensearch-project.

the class MetadataUpdateSettingsService method updateSettings.

public void updateSettings(final UpdateSettingsClusterStateUpdateRequest request, final ActionListener<ClusterStateUpdateResponse> listener) {
    final Settings normalizedSettings = Settings.builder().put(request.settings()).normalizePrefix(IndexMetadata.INDEX_SETTING_PREFIX).build();
    Settings.Builder settingsForClosedIndices = Settings.builder();
    Settings.Builder settingsForOpenIndices = Settings.builder();
    final Set<String> skippedSettings = new HashSet<>();
    indexScopedSettings.validate(// don't validate wildcards
    normalizedSettings.filter(s -> Regex.isSimpleMatchPattern(s) == false), // don't validate dependencies here we check it below never allow to change the number of shards
    false, true);
    // validate internal or private index settings
    for (String key : normalizedSettings.keySet()) {
        Setting setting = indexScopedSettings.get(key);
        boolean isWildcard = setting == null && Regex.isSimpleMatchPattern(key);
        assert // we already validated the normalized settings
        setting != null || (isWildcard && normalizedSettings.hasValue(key) == false) : "unknown setting: " + key + " isWildcard: " + isWildcard + " hasValue: " + normalizedSettings.hasValue(key);
        settingsForClosedIndices.copy(key, normalizedSettings);
        if (isWildcard || setting.isDynamic()) {
            settingsForOpenIndices.copy(key, normalizedSettings);
        } else {
            skippedSettings.add(key);
        }
    }
    final Settings closedSettings = settingsForClosedIndices.build();
    final Settings openSettings = settingsForOpenIndices.build();
    final boolean preserveExisting = request.isPreserveExisting();
    clusterService.submitStateUpdateTask("update-settings " + Arrays.toString(request.indices()), new AckedClusterStateUpdateTask<ClusterStateUpdateResponse>(Priority.URGENT, request, wrapPreservingContext(listener, threadPool.getThreadContext())) {

        @Override
        protected ClusterStateUpdateResponse newResponse(boolean acknowledged) {
            return new ClusterStateUpdateResponse(acknowledged);
        }

        @Override
        public ClusterState execute(ClusterState currentState) {
            RoutingTable.Builder routingTableBuilder = RoutingTable.builder(currentState.routingTable());
            Metadata.Builder metadataBuilder = Metadata.builder(currentState.metadata());
            // allow to change any settings to a close index, and only allow dynamic settings to be changed
            // on an open index
            Set<Index> openIndices = new HashSet<>();
            Set<Index> closeIndices = new HashSet<>();
            final String[] actualIndices = new String[request.indices().length];
            for (int i = 0; i < request.indices().length; i++) {
                Index index = request.indices()[i];
                actualIndices[i] = index.getName();
                final IndexMetadata metadata = currentState.metadata().getIndexSafe(index);
                if (metadata.getState() == IndexMetadata.State.OPEN) {
                    openIndices.add(index);
                } else {
                    closeIndices.add(index);
                }
            }
            if (!skippedSettings.isEmpty() && !openIndices.isEmpty()) {
                throw new IllegalArgumentException(String.format(Locale.ROOT, "Can't update non dynamic settings [%s] for open indices %s", skippedSettings, openIndices));
            }
            if (IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.exists(openSettings)) {
                final int updatedNumberOfReplicas = IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.get(openSettings);
                if (preserveExisting == false) {
                    // Verify that this won't take us over the cluster shard limit.
                    int totalNewShards = Arrays.stream(request.indices()).mapToInt(i -> getTotalNewShards(i, currentState, updatedNumberOfReplicas)).sum();
                    Optional<String> error = shardLimitValidator.checkShardLimit(totalNewShards, currentState);
                    if (error.isPresent()) {
                        ValidationException ex = new ValidationException();
                        ex.addValidationError(error.get());
                        throw ex;
                    }
                    /*
                             * We do not update the in-sync allocation IDs as they will be removed upon the first index operation which makes
                             * these copies stale.
                             *
                             * TODO: should we update the in-sync allocation IDs once the data is deleted by the node?
                             */
                    routingTableBuilder.updateNumberOfReplicas(updatedNumberOfReplicas, actualIndices);
                    metadataBuilder.updateNumberOfReplicas(updatedNumberOfReplicas, actualIndices);
                    logger.info("updating number_of_replicas to [{}] for indices {}", updatedNumberOfReplicas, actualIndices);
                }
            }
            if (!openIndices.isEmpty()) {
                for (Index index : openIndices) {
                    IndexMetadata indexMetadata = metadataBuilder.getSafe(index);
                    Settings.Builder updates = Settings.builder();
                    Settings.Builder indexSettings = Settings.builder().put(indexMetadata.getSettings());
                    if (indexScopedSettings.updateDynamicSettings(openSettings, indexSettings, updates, index.getName())) {
                        if (preserveExisting) {
                            indexSettings.put(indexMetadata.getSettings());
                        }
                        /*
                                 * The setting index.number_of_replicas is special; we require that this setting has a value in the index. When
                                 * creating the index, we ensure this by explicitly providing a value for the setting to the default (one) if
                                 * there is a not value provided on the source of the index creation. A user can update this setting though,
                                 * including updating it to null, indicating that they want to use the default value. In this case, we again
                                 * have to provide an explicit value for the setting to the default (one).
                                 */
                        if (IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.exists(indexSettings) == false) {
                            indexSettings.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.get(Settings.EMPTY));
                        }
                        Settings finalSettings = indexSettings.build();
                        indexScopedSettings.validate(finalSettings.filter(k -> indexScopedSettings.isPrivateSetting(k) == false), true);
                        metadataBuilder.put(IndexMetadata.builder(indexMetadata).settings(finalSettings));
                    }
                }
            }
            if (!closeIndices.isEmpty()) {
                for (Index index : closeIndices) {
                    IndexMetadata indexMetadata = metadataBuilder.getSafe(index);
                    Settings.Builder updates = Settings.builder();
                    Settings.Builder indexSettings = Settings.builder().put(indexMetadata.getSettings());
                    if (indexScopedSettings.updateSettings(closedSettings, indexSettings, updates, index.getName())) {
                        if (preserveExisting) {
                            indexSettings.put(indexMetadata.getSettings());
                        }
                        /*
                                 * The setting index.number_of_replicas is special; we require that this setting has a value in the index. When
                                 * creating the index, we ensure this by explicitly providing a value for the setting to the default (one) if
                                 * there is a not value provided on the source of the index creation. A user can update this setting though,
                                 * including updating it to null, indicating that they want to use the default value. In this case, we again
                                 * have to provide an explicit value for the setting to the default (one).
                                 */
                        if (IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.exists(indexSettings) == false) {
                            indexSettings.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.get(Settings.EMPTY));
                        }
                        Settings finalSettings = indexSettings.build();
                        indexScopedSettings.validate(finalSettings.filter(k -> indexScopedSettings.isPrivateSetting(k) == false), true);
                        metadataBuilder.put(IndexMetadata.builder(indexMetadata).settings(finalSettings));
                    }
                }
            }
            if (IndexSettings.INDEX_TRANSLOG_RETENTION_AGE_SETTING.exists(normalizedSettings) || IndexSettings.INDEX_TRANSLOG_RETENTION_SIZE_SETTING.exists(normalizedSettings)) {
                Settings indexSettings;
                for (String index : actualIndices) {
                    indexSettings = metadataBuilder.get(index).getSettings();
                    MetadataCreateIndexService.validateTranslogRetentionSettings(indexSettings);
                    // validate storeType for deprecating index stores
                    MetadataCreateIndexService.validateStoreTypeSettings(indexSettings);
                }
            }
            boolean changed = false;
            // increment settings versions
            for (final String index : actualIndices) {
                if (same(currentState.metadata().index(index).getSettings(), metadataBuilder.get(index).getSettings()) == false) {
                    changed = true;
                    final IndexMetadata.Builder builder = IndexMetadata.builder(metadataBuilder.get(index));
                    builder.settingsVersion(1 + builder.settingsVersion());
                    metadataBuilder.put(builder);
                }
            }
            final ClusterBlocks.Builder blocks = ClusterBlocks.builder().blocks(currentState.blocks());
            for (IndexMetadata.APIBlock block : IndexMetadata.APIBlock.values()) {
                changed |= maybeUpdateClusterBlock(actualIndices, blocks, block.block, block.setting, openSettings);
            }
            if (changed == false) {
                return currentState;
            }
            ClusterState updatedState = ClusterState.builder(currentState).metadata(metadataBuilder).routingTable(routingTableBuilder.build()).blocks(blocks).build();
            // now, reroute in case things change that require it (like number of replicas)
            updatedState = allocationService.reroute(updatedState, "settings update");
            try {
                for (Index index : openIndices) {
                    final IndexMetadata currentMetadata = currentState.getMetadata().getIndexSafe(index);
                    final IndexMetadata updatedMetadata = updatedState.metadata().getIndexSafe(index);
                    indicesService.verifyIndexMetadata(currentMetadata, updatedMetadata);
                }
                for (Index index : closeIndices) {
                    final IndexMetadata currentMetadata = currentState.getMetadata().getIndexSafe(index);
                    final IndexMetadata updatedMetadata = updatedState.metadata().getIndexSafe(index);
                    // Verifies that the current index settings can be updated with the updated dynamic settings.
                    indicesService.verifyIndexMetadata(currentMetadata, updatedMetadata);
                    // Now check that we can create the index with the updated settings (dynamic and non-dynamic).
                    // This step is mandatory since we allow to update non-dynamic settings on closed indices.
                    indicesService.verifyIndexMetadata(updatedMetadata, updatedMetadata);
                }
            } catch (IOException ex) {
                throw ExceptionsHelper.convertToOpenSearchException(ex);
            }
            return updatedState;
        }
    });
}
Also used : Arrays(java.util.Arrays) IndexScopedSettings(org.opensearch.common.settings.IndexScopedSettings) ThreadPool(org.opensearch.threadpool.ThreadPool) AllocationService(org.opensearch.cluster.routing.allocation.AllocationService) Version(org.opensearch.Version) Priority(org.opensearch.common.Priority) Regex(org.opensearch.common.regex.Regex) HashSet(java.util.HashSet) ClusterState(org.opensearch.cluster.ClusterState) ClusterBlock(org.opensearch.cluster.block.ClusterBlock) Locale(java.util.Locale) Map(java.util.Map) AckedClusterStateUpdateTask(org.opensearch.cluster.AckedClusterStateUpdateTask) Inject(org.opensearch.common.inject.Inject) ActionListener(org.opensearch.action.ActionListener) ClusterBlocks(org.opensearch.cluster.block.ClusterBlocks) Setting(org.opensearch.common.settings.Setting) ShardLimitValidator(org.opensearch.indices.ShardLimitValidator) Index(org.opensearch.index.Index) IndicesService(org.opensearch.indices.IndicesService) ExceptionsHelper(org.opensearch.ExceptionsHelper) UpgradeSettingsClusterStateUpdateRequest(org.opensearch.action.admin.indices.upgrade.post.UpgradeSettingsClusterStateUpdateRequest) Set(java.util.Set) Settings(org.opensearch.common.settings.Settings) IOException(java.io.IOException) UpdateSettingsClusterStateUpdateRequest(org.opensearch.action.admin.indices.settings.put.UpdateSettingsClusterStateUpdateRequest) ValidationException(org.opensearch.common.ValidationException) Tuple(org.opensearch.common.collect.Tuple) Logger(org.apache.logging.log4j.Logger) ClusterService(org.opensearch.cluster.service.ClusterService) IndexSettings(org.opensearch.index.IndexSettings) RoutingTable(org.opensearch.cluster.routing.RoutingTable) Optional(java.util.Optional) IndexSettings.same(org.opensearch.index.IndexSettings.same) ClusterStateUpdateResponse(org.opensearch.cluster.ack.ClusterStateUpdateResponse) ContextPreservingActionListener.wrapPreservingContext(org.opensearch.action.support.ContextPreservingActionListener.wrapPreservingContext) LogManager(org.apache.logging.log4j.LogManager) ClusterState(org.opensearch.cluster.ClusterState) HashSet(java.util.HashSet) Set(java.util.Set) ValidationException(org.opensearch.common.ValidationException) Optional(java.util.Optional) Setting(org.opensearch.common.settings.Setting) Index(org.opensearch.index.Index) IOException(java.io.IOException) ClusterStateUpdateResponse(org.opensearch.cluster.ack.ClusterStateUpdateResponse) IndexScopedSettings(org.opensearch.common.settings.IndexScopedSettings) Settings(org.opensearch.common.settings.Settings) IndexSettings(org.opensearch.index.IndexSettings) HashSet(java.util.HashSet)

Aggregations

AckedClusterStateUpdateTask (org.opensearch.cluster.AckedClusterStateUpdateTask)4 ClusterState (org.opensearch.cluster.ClusterState)4 IOException (java.io.IOException)3 Map (java.util.Map)3 Set (java.util.Set)3 LogManager (org.apache.logging.log4j.LogManager)3 DiscoveryNode (org.opensearch.cluster.node.DiscoveryNode)3 Settings (org.opensearch.common.settings.Settings)3 ThreadPool (org.opensearch.threadpool.ThreadPool)3 ArrayList (java.util.ArrayList)2 Collections (java.util.Collections)2 HashMap (java.util.HashMap)2 HashSet (java.util.HashSet)2 List (java.util.List)2 Logger (org.apache.logging.log4j.Logger)2 ParameterizedMessage (org.apache.logging.log4j.message.ParameterizedMessage)2 OpenSearchException (org.opensearch.OpenSearchException)2 ClusterChangedEvent (org.opensearch.cluster.ClusterChangedEvent)2 TimeValue (org.opensearch.common.unit.TimeValue)2 Arrays (java.util.Arrays)1