All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opensearch.transport.RemoteClusterConnection Maven / Gradle / Ivy

There is a newer version: 2.18.0
Show newest version
/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * The OpenSearch Contributors require contributions made to
 * this file be licensed under the Apache-2.0 license or a
 * compatible open source license.
 */

/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you under
 * the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*
 * Modifications Copyright OpenSearch Contributors. See
 * GitHub history for details.
 */

package org.opensearch.transport;

import org.opensearch.action.admin.cluster.state.ClusterStateAction;
import org.opensearch.action.admin.cluster.state.ClusterStateRequest;
import org.opensearch.action.admin.cluster.state.ClusterStateResponse;
import org.opensearch.action.support.ContextPreservingActionListener;
import org.opensearch.cluster.node.DiscoveryNode;
import org.opensearch.cluster.node.DiscoveryNodes;
import org.opensearch.common.settings.Settings;
import org.opensearch.common.unit.TimeValue;
import org.opensearch.common.util.concurrent.ThreadContext;
import org.opensearch.common.util.io.IOUtils;
import org.opensearch.core.action.ActionListener;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.threadpool.ThreadPool;

import java.io.Closeable;
import java.io.IOException;
import java.util.function.Function;

/**
 * Represents a connection to a single remote cluster. In contrast to a local cluster a remote cluster is not joined such that the
 * current node is part of the cluster and it won't receive cluster state updates from the remote cluster. Remote clusters are also not
 * fully connected with the current node. From a connection perspective a local cluster forms a bi-directional star network while in the
 * remote case we only connect to a subset of the nodes in the cluster in an uni-directional fashion.
 * 

* This class also handles the discovery of nodes from the remote cluster. The initial list of seed nodes is only used to discover all nodes * in the remote cluster and connects to all eligible nodes, for details see {@link RemoteClusterService#REMOTE_NODE_ATTRIBUTE}. *

* In the case of a disconnection, this class will issue a re-connect task to establish at most * {@link SniffConnectionStrategy#REMOTE_CONNECTIONS_PER_CLUSTER} until either all eligible nodes are exhausted or the maximum number of * connections per cluster has been reached. * * @opensearch.internal */ final class RemoteClusterConnection implements Closeable { private final TransportService transportService; private final RemoteConnectionManager remoteConnectionManager; private final RemoteConnectionStrategy connectionStrategy; private final String clusterAlias; private final ThreadPool threadPool; private volatile boolean skipUnavailable; private final TimeValue initialConnectionTimeout; /** * Creates a new {@link RemoteClusterConnection} * @param settings the nodes settings object * @param clusterAlias the configured alias of the cluster to connect to * @param transportService the local nodes transport service */ RemoteClusterConnection(Settings settings, String clusterAlias, TransportService transportService) { this.transportService = transportService; this.clusterAlias = clusterAlias; ConnectionProfile profile = RemoteConnectionStrategy.buildConnectionProfile(clusterAlias, settings); this.remoteConnectionManager = new RemoteConnectionManager(clusterAlias, createConnectionManager(profile, transportService)); this.connectionStrategy = RemoteConnectionStrategy.buildStrategy(clusterAlias, transportService, remoteConnectionManager, settings); // we register the transport service here as a listener to make sure we notify handlers on disconnect etc. this.remoteConnectionManager.addListener(transportService); this.skipUnavailable = RemoteClusterService.REMOTE_CLUSTER_SKIP_UNAVAILABLE.getConcreteSettingForNamespace(clusterAlias) .get(settings); this.threadPool = transportService.threadPool; initialConnectionTimeout = RemoteClusterService.REMOTE_INITIAL_CONNECTION_TIMEOUT_SETTING.get(settings); } /** * Updates the skipUnavailable flag that can be dynamically set for each remote cluster */ void updateSkipUnavailable(boolean skipUnavailable) { this.skipUnavailable = skipUnavailable; } /** * Returns whether this cluster is configured to be skipped when unavailable */ boolean isSkipUnavailable() { return skipUnavailable; } /** * Ensures that this cluster is connected. If the cluster is connected this operation * will invoke the listener immediately. */ void ensureConnected(ActionListener listener) { if (remoteConnectionManager.size() == 0) { connectionStrategy.connect(listener); } else { listener.onResponse(null); } } /** * Collects all nodes on the connected cluster and returns / passes a nodeID to {@link DiscoveryNode} lookup function * that returns null if the node ID is not found. *

* The requests to get cluster state on the connected cluster are made in the system context because logically * they are equivalent to checking a single detail in the local cluster state and should not require that the * user who made the request that is using this method in its implementation is authorized to view the entire * cluster state. */ void collectNodes(ActionListener> listener) { Runnable runnable = () -> { final ThreadContext threadContext = threadPool.getThreadContext(); final ContextPreservingActionListener> contextPreservingActionListener = new ContextPreservingActionListener<>(threadContext.newRestorableContext(false), listener); try (ThreadContext.StoredContext ignore = threadContext.stashContext()) { // we stash any context here since this is an internal execution and should not leak any existing context information threadContext.markAsSystemContext(); final ClusterStateRequest request = new ClusterStateRequest(); request.clear(); request.nodes(true); request.local(true); // run this on the node that gets the request it's as good as any other Transport.Connection connection = remoteConnectionManager.getAnyRemoteConnection(); transportService.sendRequest( connection, ClusterStateAction.NAME, request, TransportRequestOptions.EMPTY, new TransportResponseHandler() { @Override public ClusterStateResponse read(StreamInput in) throws IOException { return new ClusterStateResponse(in); } @Override public void handleResponse(ClusterStateResponse response) { DiscoveryNodes nodes = response.getState().nodes(); contextPreservingActionListener.onResponse(nodes::get); } @Override public void handleException(TransportException exp) { contextPreservingActionListener.onFailure(exp); } @Override public String executor() { return ThreadPool.Names.SAME; } } ); } }; try { // just in case if we are not connected for some reason we try to connect and if we fail we have to notify the listener // this will cause some back pressure on the search end and eventually will cause rejections but that's fine // we can't proceed with a search on a cluster level. // in the future we might want to just skip the remote nodes in such a case but that can already be implemented on the // caller end since they provide the listener. ensureConnected(ActionListener.wrap((x) -> runnable.run(), listener::onFailure)); } catch (Exception ex) { listener.onFailure(ex); } } /** * Returns a connection to the remote cluster, preferably a direct connection to the provided {@link DiscoveryNode}. * If such node is not connected, the returned connection will be a proxy connection that redirects to it. */ Transport.Connection getConnection(DiscoveryNode remoteClusterNode) { return remoteConnectionManager.getConnection(remoteClusterNode); } Transport.Connection getConnection() { return remoteConnectionManager.getAnyRemoteConnection(); } @Override public void close() throws IOException { IOUtils.close(connectionStrategy, remoteConnectionManager); } public boolean isClosed() { return connectionStrategy.isClosed(); } // for testing only boolean assertNoRunningConnections() { return connectionStrategy.assertNoRunningConnections(); } boolean isNodeConnected(final DiscoveryNode node) { return remoteConnectionManager.nodeConnected(node); } /** * Get the information about remote nodes to be rendered on {@code _remote/info} requests. */ public RemoteConnectionInfo getConnectionInfo() { return new RemoteConnectionInfo(clusterAlias, connectionStrategy.getModeInfo(), initialConnectionTimeout, skipUnavailable); } int getNumNodesConnected() { return remoteConnectionManager.size(); } private static ConnectionManager createConnectionManager(ConnectionProfile connectionProfile, TransportService transportService) { return new ClusterConnectionManager(connectionProfile, transportService.transport); } ConnectionManager getConnectionManager() { return remoteConnectionManager; } boolean shouldRebuildConnection(Settings newSettings) { return connectionStrategy.shouldRebuildConnection(newSettings); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy