mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-01 16:39:11 +00:00
Merge branch 'master' into feature-suggest-refactoring
This commit is contained in:
commit
daeffb149c
@ -31,6 +31,7 @@ import org.elasticsearch.discovery.DiscoveryStats;
|
|||||||
import org.elasticsearch.http.HttpStats;
|
import org.elasticsearch.http.HttpStats;
|
||||||
import org.elasticsearch.indices.NodeIndicesStats;
|
import org.elasticsearch.indices.NodeIndicesStats;
|
||||||
import org.elasticsearch.indices.breaker.AllCircuitBreakerStats;
|
import org.elasticsearch.indices.breaker.AllCircuitBreakerStats;
|
||||||
|
import org.elasticsearch.ingest.IngestStats;
|
||||||
import org.elasticsearch.monitor.fs.FsInfo;
|
import org.elasticsearch.monitor.fs.FsInfo;
|
||||||
import org.elasticsearch.monitor.jvm.JvmStats;
|
import org.elasticsearch.monitor.jvm.JvmStats;
|
||||||
import org.elasticsearch.monitor.os.OsStats;
|
import org.elasticsearch.monitor.os.OsStats;
|
||||||
@ -81,6 +82,9 @@ public class NodeStats extends BaseNodeResponse implements ToXContent {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private DiscoveryStats discoveryStats;
|
private DiscoveryStats discoveryStats;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private IngestStats ingestStats;
|
||||||
|
|
||||||
NodeStats() {
|
NodeStats() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +93,8 @@ public class NodeStats extends BaseNodeResponse implements ToXContent {
|
|||||||
@Nullable FsInfo fs, @Nullable TransportStats transport, @Nullable HttpStats http,
|
@Nullable FsInfo fs, @Nullable TransportStats transport, @Nullable HttpStats http,
|
||||||
@Nullable AllCircuitBreakerStats breaker,
|
@Nullable AllCircuitBreakerStats breaker,
|
||||||
@Nullable ScriptStats scriptStats,
|
@Nullable ScriptStats scriptStats,
|
||||||
@Nullable DiscoveryStats discoveryStats) {
|
@Nullable DiscoveryStats discoveryStats,
|
||||||
|
@Nullable IngestStats ingestStats) {
|
||||||
super(node);
|
super(node);
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.indices = indices;
|
this.indices = indices;
|
||||||
@ -103,6 +108,7 @@ public class NodeStats extends BaseNodeResponse implements ToXContent {
|
|||||||
this.breaker = breaker;
|
this.breaker = breaker;
|
||||||
this.scriptStats = scriptStats;
|
this.scriptStats = scriptStats;
|
||||||
this.discoveryStats = discoveryStats;
|
this.discoveryStats = discoveryStats;
|
||||||
|
this.ingestStats = ingestStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTimestamp() {
|
public long getTimestamp() {
|
||||||
@ -187,6 +193,11 @@ public class NodeStats extends BaseNodeResponse implements ToXContent {
|
|||||||
return this.discoveryStats;
|
return this.discoveryStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public IngestStats getIngestStats() {
|
||||||
|
return ingestStats;
|
||||||
|
}
|
||||||
|
|
||||||
public static NodeStats readNodeStats(StreamInput in) throws IOException {
|
public static NodeStats readNodeStats(StreamInput in) throws IOException {
|
||||||
NodeStats nodeInfo = new NodeStats();
|
NodeStats nodeInfo = new NodeStats();
|
||||||
nodeInfo.readFrom(in);
|
nodeInfo.readFrom(in);
|
||||||
@ -224,7 +235,7 @@ public class NodeStats extends BaseNodeResponse implements ToXContent {
|
|||||||
breaker = AllCircuitBreakerStats.readOptionalAllCircuitBreakerStats(in);
|
breaker = AllCircuitBreakerStats.readOptionalAllCircuitBreakerStats(in);
|
||||||
scriptStats = in.readOptionalStreamable(ScriptStats::new);
|
scriptStats = in.readOptionalStreamable(ScriptStats::new);
|
||||||
discoveryStats = in.readOptionalStreamable(() -> new DiscoveryStats(null));
|
discoveryStats = in.readOptionalStreamable(() -> new DiscoveryStats(null));
|
||||||
|
ingestStats = in.readOptionalWritable(IngestStats.PROTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -282,6 +293,7 @@ public class NodeStats extends BaseNodeResponse implements ToXContent {
|
|||||||
out.writeOptionalStreamable(breaker);
|
out.writeOptionalStreamable(breaker);
|
||||||
out.writeOptionalStreamable(scriptStats);
|
out.writeOptionalStreamable(scriptStats);
|
||||||
out.writeOptionalStreamable(discoveryStats);
|
out.writeOptionalStreamable(discoveryStats);
|
||||||
|
out.writeOptionalWriteable(ingestStats);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -337,6 +349,10 @@ public class NodeStats extends BaseNodeResponse implements ToXContent {
|
|||||||
getDiscoveryStats().toXContent(builder, params);
|
getDiscoveryStats().toXContent(builder, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getIngestStats() != null) {
|
||||||
|
getIngestStats().toXContent(builder, params);
|
||||||
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ public class NodesStatsRequest extends BaseNodesRequest<NodesStatsRequest> {
|
|||||||
private boolean breaker;
|
private boolean breaker;
|
||||||
private boolean script;
|
private boolean script;
|
||||||
private boolean discovery;
|
private boolean discovery;
|
||||||
|
private boolean ingest;
|
||||||
|
|
||||||
public NodesStatsRequest() {
|
public NodesStatsRequest() {
|
||||||
}
|
}
|
||||||
@ -69,6 +70,7 @@ public class NodesStatsRequest extends BaseNodesRequest<NodesStatsRequest> {
|
|||||||
this.breaker = true;
|
this.breaker = true;
|
||||||
this.script = true;
|
this.script = true;
|
||||||
this.discovery = true;
|
this.discovery = true;
|
||||||
|
this.ingest = true;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +89,7 @@ public class NodesStatsRequest extends BaseNodesRequest<NodesStatsRequest> {
|
|||||||
this.breaker = false;
|
this.breaker = false;
|
||||||
this.script = false;
|
this.script = false;
|
||||||
this.discovery = false;
|
this.discovery = false;
|
||||||
|
this.ingest = false;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,6 +253,17 @@ public class NodesStatsRequest extends BaseNodesRequest<NodesStatsRequest> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean ingest() {
|
||||||
|
return ingest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should ingest statistics be returned.
|
||||||
|
*/
|
||||||
|
public NodesStatsRequest ingest(boolean ingest) {
|
||||||
|
this.ingest = ingest;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readFrom(StreamInput in) throws IOException {
|
public void readFrom(StreamInput in) throws IOException {
|
||||||
@ -265,6 +279,7 @@ public class NodesStatsRequest extends BaseNodesRequest<NodesStatsRequest> {
|
|||||||
breaker = in.readBoolean();
|
breaker = in.readBoolean();
|
||||||
script = in.readBoolean();
|
script = in.readBoolean();
|
||||||
discovery = in.readBoolean();
|
discovery = in.readBoolean();
|
||||||
|
ingest = in.readBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -281,6 +296,7 @@ public class NodesStatsRequest extends BaseNodesRequest<NodesStatsRequest> {
|
|||||||
out.writeBoolean(breaker);
|
out.writeBoolean(breaker);
|
||||||
out.writeBoolean(script);
|
out.writeBoolean(script);
|
||||||
out.writeBoolean(discovery);
|
out.writeBoolean(discovery);
|
||||||
|
out.writeBoolean(ingest);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -137,4 +137,12 @@ public class NodesStatsRequestBuilder extends NodesOperationRequestBuilder<Nodes
|
|||||||
request.discovery(discovery);
|
request.discovery(discovery);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should ingest statistics be returned.
|
||||||
|
*/
|
||||||
|
public NodesStatsRequestBuilder ingest(boolean ingest) {
|
||||||
|
request.ingest(ingest);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,8 @@ public class TransportNodesStatsAction extends TransportNodesAction<NodesStatsRe
|
|||||||
protected NodeStats nodeOperation(NodeStatsRequest nodeStatsRequest) {
|
protected NodeStats nodeOperation(NodeStatsRequest nodeStatsRequest) {
|
||||||
NodesStatsRequest request = nodeStatsRequest.request;
|
NodesStatsRequest request = nodeStatsRequest.request;
|
||||||
return nodeService.stats(request.indices(), request.os(), request.process(), request.jvm(), request.threadPool(),
|
return nodeService.stats(request.indices(), request.os(), request.process(), request.jvm(), request.threadPool(),
|
||||||
request.fs(), request.transport(), request.http(), request.breaker(), request.script(), request.discovery());
|
request.fs(), request.transport(), request.http(), request.breaker(), request.script(), request.discovery(),
|
||||||
|
request.ingest());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -99,7 +99,7 @@ public class TransportClusterStatsAction extends TransportNodesAction<ClusterSta
|
|||||||
@Override
|
@Override
|
||||||
protected ClusterStatsNodeResponse nodeOperation(ClusterStatsNodeRequest nodeRequest) {
|
protected ClusterStatsNodeResponse nodeOperation(ClusterStatsNodeRequest nodeRequest) {
|
||||||
NodeInfo nodeInfo = nodeService.info(false, true, false, true, false, true, false, true, false);
|
NodeInfo nodeInfo = nodeService.info(false, true, false, true, false, true, false, true, false);
|
||||||
NodeStats nodeStats = nodeService.stats(CommonStatsFlags.NONE, false, true, true, false, true, false, false, false, false, false);
|
NodeStats nodeStats = nodeService.stats(CommonStatsFlags.NONE, false, true, true, false, true, false, false, false, false, false, false);
|
||||||
List<ShardStats> shardsStats = new ArrayList<>();
|
List<ShardStats> shardsStats = new ArrayList<>();
|
||||||
for (IndexService indexService : indicesService) {
|
for (IndexService indexService : indicesService) {
|
||||||
for (IndexShard indexShard : indexService) {
|
for (IndexShard indexShard : indexService) {
|
||||||
|
@ -112,7 +112,7 @@ public final class IngestActionFilter extends AbstractComponent implements Actio
|
|||||||
logger.error("failed to execute pipeline for a bulk request", throwable);
|
logger.error("failed to execute pipeline for a bulk request", throwable);
|
||||||
listener.onFailure(throwable);
|
listener.onFailure(throwable);
|
||||||
} else {
|
} else {
|
||||||
long ingestTookInMillis = TimeUnit.MILLISECONDS.convert(System.nanoTime() - ingestStartTimeInNanos, TimeUnit.NANOSECONDS);
|
long ingestTookInMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - ingestStartTimeInNanos);
|
||||||
BulkRequest bulkRequest = bulkRequestModifier.getBulkRequest();
|
BulkRequest bulkRequest = bulkRequestModifier.getBulkRequest();
|
||||||
ActionListener<BulkResponse> actionListener = bulkRequestModifier.wrapActionListenerIfNeeded(ingestTookInMillis, listener);
|
ActionListener<BulkResponse> actionListener = bulkRequestModifier.wrapActionListenerIfNeeded(ingestTookInMillis, listener);
|
||||||
if (bulkRequest.requests().isEmpty()) {
|
if (bulkRequest.requests().isEmpty()) {
|
||||||
|
@ -136,6 +136,7 @@ public class ClusterModule extends AbstractModule {
|
|||||||
bind(AllocationService.class).asEagerSingleton();
|
bind(AllocationService.class).asEagerSingleton();
|
||||||
bind(DiscoveryNodeService.class).asEagerSingleton();
|
bind(DiscoveryNodeService.class).asEagerSingleton();
|
||||||
bind(ClusterService.class).to(InternalClusterService.class).asEagerSingleton();
|
bind(ClusterService.class).to(InternalClusterService.class).asEagerSingleton();
|
||||||
|
bind(NodeConnectionsService.class).asEagerSingleton();
|
||||||
bind(OperationRouting.class).asEagerSingleton();
|
bind(OperationRouting.class).asEagerSingleton();
|
||||||
bind(MetaDataCreateIndexService.class).asEagerSingleton();
|
bind(MetaDataCreateIndexService.class).asEagerSingleton();
|
||||||
bind(MetaDataDeleteIndexService.class).asEagerSingleton();
|
bind(MetaDataDeleteIndexService.class).asEagerSingleton();
|
||||||
|
@ -26,7 +26,6 @@ import org.elasticsearch.cluster.service.PendingClusterTask;
|
|||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.component.LifecycleComponent;
|
import org.elasticsearch.common.component.LifecycleComponent;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.tasks.TaskManager;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -154,9 +153,4 @@ public interface ClusterService extends LifecycleComponent<ClusterService> {
|
|||||||
* @return A zero time value if the queue is empty, otherwise the time value oldest task waiting in the queue
|
* @return A zero time value if the queue is empty, otherwise the time value oldest task waiting in the queue
|
||||||
*/
|
*/
|
||||||
TimeValue getMaxTaskWaitTime();
|
TimeValue getMaxTaskWaitTime();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns task manager created in the cluster service
|
|
||||||
*/
|
|
||||||
TaskManager getTaskManager();
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.elasticsearch.cluster;
|
||||||
|
|
||||||
|
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||||
|
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||||
|
import org.elasticsearch.common.inject.Inject;
|
||||||
|
import org.elasticsearch.common.lease.Releasable;
|
||||||
|
import org.elasticsearch.common.settings.Setting;
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
|
||||||
|
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
|
||||||
|
import org.elasticsearch.common.util.concurrent.FutureUtils;
|
||||||
|
import org.elasticsearch.common.util.concurrent.KeyedLock;
|
||||||
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
|
import org.elasticsearch.transport.TransportService;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component is responsible for connecting to nodes once they are added to the cluster state, and disconnect when they are
|
||||||
|
* removed. Also, it periodically checks that all connections are still open and if needed restores them.
|
||||||
|
* Note that this component is *not* responsible for removing nodes from the cluster if they disconnect / do not respond
|
||||||
|
* to pings. This is done by {@link org.elasticsearch.discovery.zen.fd.NodesFaultDetection}. Master fault detection
|
||||||
|
* is done by {@link org.elasticsearch.discovery.zen.fd.MasterFaultDetection}.
|
||||||
|
*/
|
||||||
|
public class NodeConnectionsService extends AbstractLifecycleComponent<NodeConnectionsService> {
|
||||||
|
|
||||||
|
public static final Setting<TimeValue> CLUSTER_NODE_RECONNECT_INTERVAL_SETTING =
|
||||||
|
Setting.positiveTimeSetting("cluster.nodes.reconnect_interval", TimeValue.timeValueSeconds(10), false, Setting.Scope.CLUSTER);
|
||||||
|
private final ThreadPool threadPool;
|
||||||
|
private final TransportService transportService;
|
||||||
|
|
||||||
|
// map between current node and the number of failed connection attempts. 0 means successfully connected.
|
||||||
|
// if a node doesn't appear in this list it shouldn't be monitored
|
||||||
|
private ConcurrentMap<DiscoveryNode, Integer> nodes = ConcurrentCollections.newConcurrentMap();
|
||||||
|
|
||||||
|
final private KeyedLock<DiscoveryNode> nodeLocks = new KeyedLock<>();
|
||||||
|
|
||||||
|
private final TimeValue reconnectInterval;
|
||||||
|
|
||||||
|
private volatile ScheduledFuture<?> backgroundFuture = null;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public NodeConnectionsService(Settings settings, ThreadPool threadPool, TransportService transportService) {
|
||||||
|
super(settings);
|
||||||
|
this.threadPool = threadPool;
|
||||||
|
this.transportService = transportService;
|
||||||
|
this.reconnectInterval = NodeConnectionsService.CLUSTER_NODE_RECONNECT_INTERVAL_SETTING.get(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connectToAddedNodes(ClusterChangedEvent event) {
|
||||||
|
|
||||||
|
// TODO: do this in parallel (and wait)
|
||||||
|
for (final DiscoveryNode node : event.nodesDelta().addedNodes()) {
|
||||||
|
try (Releasable ignored = nodeLocks.acquire(node)) {
|
||||||
|
Integer current = nodes.put(node, 0);
|
||||||
|
assert current == null : "node " + node + " was added in event but already in internal nodes";
|
||||||
|
validateNodeConnected(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnectFromRemovedNodes(ClusterChangedEvent event) {
|
||||||
|
for (final DiscoveryNode node : event.nodesDelta().removedNodes()) {
|
||||||
|
try (Releasable ignored = nodeLocks.acquire(node)) {
|
||||||
|
Integer current = nodes.remove(node);
|
||||||
|
assert current != null : "node " + node + " was removed in event but not in internal nodes";
|
||||||
|
try {
|
||||||
|
transportService.disconnectFromNode(node);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
logger.warn("failed to disconnect to node [" + node + "]", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void validateNodeConnected(DiscoveryNode node) {
|
||||||
|
assert nodeLocks.isHeldByCurrentThread(node) : "validateNodeConnected must be called under lock";
|
||||||
|
if (lifecycle.stoppedOrClosed() ||
|
||||||
|
nodes.containsKey(node) == false) { // we double check existence of node since connectToNode might take time...
|
||||||
|
// nothing to do
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
// connecting to an already connected node is a noop
|
||||||
|
transportService.connectToNode(node);
|
||||||
|
nodes.put(node, 0);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Integer nodeFailureCount = nodes.get(node);
|
||||||
|
assert nodeFailureCount != null : node + " didn't have a counter in nodes map";
|
||||||
|
nodeFailureCount = nodeFailureCount + 1;
|
||||||
|
// log every 6th failure
|
||||||
|
if ((nodeFailureCount % 6) == 1) {
|
||||||
|
logger.warn("failed to connect to node {} (tried [{}] times)", e, node, nodeFailureCount);
|
||||||
|
}
|
||||||
|
nodes.put(node, nodeFailureCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConnectionChecker extends AbstractRunnable {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable t) {
|
||||||
|
logger.warn("unexpected error while checking for node reconnects", t);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void doRun() {
|
||||||
|
for (DiscoveryNode node : nodes.keySet()) {
|
||||||
|
try (Releasable ignored = nodeLocks.acquire(node)) {
|
||||||
|
validateNodeConnected(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAfter() {
|
||||||
|
if (lifecycle.started()) {
|
||||||
|
backgroundFuture = threadPool.schedule(reconnectInterval, ThreadPool.Names.GENERIC, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doStart() {
|
||||||
|
backgroundFuture = threadPool.schedule(reconnectInterval, ThreadPool.Names.GENERIC, new ConnectionChecker());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doStop() {
|
||||||
|
FutureUtils.cancel(backgroundFuture);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doClose() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -69,15 +69,17 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
|||||||
private final State state;
|
private final State state;
|
||||||
private final SnapshotId snapshotId;
|
private final SnapshotId snapshotId;
|
||||||
private final boolean includeGlobalState;
|
private final boolean includeGlobalState;
|
||||||
|
private final boolean partial;
|
||||||
private final ImmutableOpenMap<ShardId, ShardSnapshotStatus> shards;
|
private final ImmutableOpenMap<ShardId, ShardSnapshotStatus> shards;
|
||||||
private final List<String> indices;
|
private final List<String> indices;
|
||||||
private final ImmutableOpenMap<String, List<ShardId>> waitingIndices;
|
private final ImmutableOpenMap<String, List<ShardId>> waitingIndices;
|
||||||
private final long startTime;
|
private final long startTime;
|
||||||
|
|
||||||
public Entry(SnapshotId snapshotId, boolean includeGlobalState, State state, List<String> indices, long startTime, ImmutableOpenMap<ShardId, ShardSnapshotStatus> shards) {
|
public Entry(SnapshotId snapshotId, boolean includeGlobalState, boolean partial, State state, List<String> indices, long startTime, ImmutableOpenMap<ShardId, ShardSnapshotStatus> shards) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.snapshotId = snapshotId;
|
this.snapshotId = snapshotId;
|
||||||
this.includeGlobalState = includeGlobalState;
|
this.includeGlobalState = includeGlobalState;
|
||||||
|
this.partial = partial;
|
||||||
this.indices = indices;
|
this.indices = indices;
|
||||||
this.startTime = startTime;
|
this.startTime = startTime;
|
||||||
if (shards == null) {
|
if (shards == null) {
|
||||||
@ -90,7 +92,7 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Entry(Entry entry, State state, ImmutableOpenMap<ShardId, ShardSnapshotStatus> shards) {
|
public Entry(Entry entry, State state, ImmutableOpenMap<ShardId, ShardSnapshotStatus> shards) {
|
||||||
this(entry.snapshotId, entry.includeGlobalState, state, entry.indices, entry.startTime, shards);
|
this(entry.snapshotId, entry.includeGlobalState, entry.partial, state, entry.indices, entry.startTime, shards);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entry(Entry entry, ImmutableOpenMap<ShardId, ShardSnapshotStatus> shards) {
|
public Entry(Entry entry, ImmutableOpenMap<ShardId, ShardSnapshotStatus> shards) {
|
||||||
@ -121,6 +123,10 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
|||||||
return includeGlobalState;
|
return includeGlobalState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean partial() {
|
||||||
|
return partial;
|
||||||
|
}
|
||||||
|
|
||||||
public long startTime() {
|
public long startTime() {
|
||||||
return startTime;
|
return startTime;
|
||||||
}
|
}
|
||||||
@ -133,6 +139,7 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
|||||||
Entry entry = (Entry) o;
|
Entry entry = (Entry) o;
|
||||||
|
|
||||||
if (includeGlobalState != entry.includeGlobalState) return false;
|
if (includeGlobalState != entry.includeGlobalState) return false;
|
||||||
|
if (partial != entry.partial) return false;
|
||||||
if (startTime != entry.startTime) return false;
|
if (startTime != entry.startTime) return false;
|
||||||
if (!indices.equals(entry.indices)) return false;
|
if (!indices.equals(entry.indices)) return false;
|
||||||
if (!shards.equals(entry.shards)) return false;
|
if (!shards.equals(entry.shards)) return false;
|
||||||
@ -148,6 +155,7 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
|||||||
int result = state.hashCode();
|
int result = state.hashCode();
|
||||||
result = 31 * result + snapshotId.hashCode();
|
result = 31 * result + snapshotId.hashCode();
|
||||||
result = 31 * result + (includeGlobalState ? 1 : 0);
|
result = 31 * result + (includeGlobalState ? 1 : 0);
|
||||||
|
result = 31 * result + (partial ? 1 : 0);
|
||||||
result = 31 * result + shards.hashCode();
|
result = 31 * result + shards.hashCode();
|
||||||
result = 31 * result + indices.hashCode();
|
result = 31 * result + indices.hashCode();
|
||||||
result = 31 * result + waitingIndices.hashCode();
|
result = 31 * result + waitingIndices.hashCode();
|
||||||
@ -360,6 +368,7 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
|||||||
for (int i = 0; i < entries.length; i++) {
|
for (int i = 0; i < entries.length; i++) {
|
||||||
SnapshotId snapshotId = SnapshotId.readSnapshotId(in);
|
SnapshotId snapshotId = SnapshotId.readSnapshotId(in);
|
||||||
boolean includeGlobalState = in.readBoolean();
|
boolean includeGlobalState = in.readBoolean();
|
||||||
|
boolean partial = in.readBoolean();
|
||||||
State state = State.fromValue(in.readByte());
|
State state = State.fromValue(in.readByte());
|
||||||
int indices = in.readVInt();
|
int indices = in.readVInt();
|
||||||
List<String> indexBuilder = new ArrayList<>();
|
List<String> indexBuilder = new ArrayList<>();
|
||||||
@ -375,7 +384,7 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
|||||||
State shardState = State.fromValue(in.readByte());
|
State shardState = State.fromValue(in.readByte());
|
||||||
builder.put(shardId, new ShardSnapshotStatus(nodeId, shardState));
|
builder.put(shardId, new ShardSnapshotStatus(nodeId, shardState));
|
||||||
}
|
}
|
||||||
entries[i] = new Entry(snapshotId, includeGlobalState, state, Collections.unmodifiableList(indexBuilder), startTime, builder.build());
|
entries[i] = new Entry(snapshotId, includeGlobalState, partial, state, Collections.unmodifiableList(indexBuilder), startTime, builder.build());
|
||||||
}
|
}
|
||||||
return new SnapshotsInProgress(entries);
|
return new SnapshotsInProgress(entries);
|
||||||
}
|
}
|
||||||
@ -386,6 +395,7 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
|||||||
for (Entry entry : entries) {
|
for (Entry entry : entries) {
|
||||||
entry.snapshotId().writeTo(out);
|
entry.snapshotId().writeTo(out);
|
||||||
out.writeBoolean(entry.includeGlobalState());
|
out.writeBoolean(entry.includeGlobalState());
|
||||||
|
out.writeBoolean(entry.partial());
|
||||||
out.writeByte(entry.state().value());
|
out.writeByte(entry.state().value());
|
||||||
out.writeVInt(entry.indices().size());
|
out.writeVInt(entry.indices().size());
|
||||||
for (String index : entry.indices()) {
|
for (String index : entry.indices()) {
|
||||||
@ -406,6 +416,7 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
|||||||
static final XContentBuilderString SNAPSHOTS = new XContentBuilderString("snapshots");
|
static final XContentBuilderString SNAPSHOTS = new XContentBuilderString("snapshots");
|
||||||
static final XContentBuilderString SNAPSHOT = new XContentBuilderString("snapshot");
|
static final XContentBuilderString SNAPSHOT = new XContentBuilderString("snapshot");
|
||||||
static final XContentBuilderString INCLUDE_GLOBAL_STATE = new XContentBuilderString("include_global_state");
|
static final XContentBuilderString INCLUDE_GLOBAL_STATE = new XContentBuilderString("include_global_state");
|
||||||
|
static final XContentBuilderString PARTIAL = new XContentBuilderString("partial");
|
||||||
static final XContentBuilderString STATE = new XContentBuilderString("state");
|
static final XContentBuilderString STATE = new XContentBuilderString("state");
|
||||||
static final XContentBuilderString INDICES = new XContentBuilderString("indices");
|
static final XContentBuilderString INDICES = new XContentBuilderString("indices");
|
||||||
static final XContentBuilderString START_TIME_MILLIS = new XContentBuilderString("start_time_millis");
|
static final XContentBuilderString START_TIME_MILLIS = new XContentBuilderString("start_time_millis");
|
||||||
@ -431,6 +442,7 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
|||||||
builder.field(Fields.REPOSITORY, entry.snapshotId().getRepository());
|
builder.field(Fields.REPOSITORY, entry.snapshotId().getRepository());
|
||||||
builder.field(Fields.SNAPSHOT, entry.snapshotId().getSnapshot());
|
builder.field(Fields.SNAPSHOT, entry.snapshotId().getSnapshot());
|
||||||
builder.field(Fields.INCLUDE_GLOBAL_STATE, entry.includeGlobalState());
|
builder.field(Fields.INCLUDE_GLOBAL_STATE, entry.includeGlobalState());
|
||||||
|
builder.field(Fields.PARTIAL, entry.partial());
|
||||||
builder.field(Fields.STATE, entry.state());
|
builder.field(Fields.STATE, entry.state());
|
||||||
builder.startArray(Fields.INDICES);
|
builder.startArray(Fields.INDICES);
|
||||||
{
|
{
|
||||||
|
@ -34,11 +34,12 @@ import org.elasticsearch.common.inject.Inject;
|
|||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.util.concurrent.FutureUtils;
|
import org.elasticsearch.common.util.concurrent.FutureUtils;
|
||||||
|
import org.elasticsearch.common.util.set.Sets;
|
||||||
import org.elasticsearch.index.IndexNotFoundException;
|
import org.elasticsearch.index.IndexNotFoundException;
|
||||||
|
import org.elasticsearch.snapshots.SnapshotsService;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Set;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
@ -67,7 +68,7 @@ public class MetaDataDeleteIndexService extends AbstractComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void deleteIndices(final Request request, final Listener userListener) {
|
public void deleteIndices(final Request request, final Listener userListener) {
|
||||||
Collection<String> indices = Arrays.asList(request.indices);
|
Set<String> indices = Sets.newHashSet(request.indices);
|
||||||
final DeleteIndexListener listener = new DeleteIndexListener(userListener);
|
final DeleteIndexListener listener = new DeleteIndexListener(userListener);
|
||||||
|
|
||||||
clusterService.submitStateUpdateTask("delete-index " + indices, new ClusterStateUpdateTask(Priority.URGENT) {
|
clusterService.submitStateUpdateTask("delete-index " + indices, new ClusterStateUpdateTask(Priority.URGENT) {
|
||||||
@ -84,6 +85,9 @@ public class MetaDataDeleteIndexService extends AbstractComponent {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClusterState execute(final ClusterState currentState) {
|
public ClusterState execute(final ClusterState currentState) {
|
||||||
|
// Check if index deletion conflicts with any running snapshots
|
||||||
|
SnapshotsService.checkIndexDeletion(currentState, indices);
|
||||||
|
|
||||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder(currentState.routingTable());
|
RoutingTable.Builder routingTableBuilder = RoutingTable.builder(currentState.routingTable());
|
||||||
MetaData.Builder metaDataBuilder = MetaData.builder(currentState.metaData());
|
MetaData.Builder metaDataBuilder = MetaData.builder(currentState.metaData());
|
||||||
ClusterBlocks.Builder clusterBlocksBuilder = ClusterBlocks.builder().blocks(currentState.blocks());
|
ClusterBlocks.Builder clusterBlocksBuilder = ClusterBlocks.builder().blocks(currentState.blocks());
|
||||||
|
@ -19,14 +19,12 @@
|
|||||||
|
|
||||||
package org.elasticsearch.cluster.metadata;
|
package org.elasticsearch.cluster.metadata;
|
||||||
|
|
||||||
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
|
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.admin.indices.close.CloseIndexClusterStateUpdateRequest;
|
import org.elasticsearch.action.admin.indices.close.CloseIndexClusterStateUpdateRequest;
|
||||||
import org.elasticsearch.action.admin.indices.open.OpenIndexClusterStateUpdateRequest;
|
import org.elasticsearch.action.admin.indices.open.OpenIndexClusterStateUpdateRequest;
|
||||||
import org.elasticsearch.cluster.AckedClusterStateUpdateTask;
|
import org.elasticsearch.cluster.AckedClusterStateUpdateTask;
|
||||||
import org.elasticsearch.cluster.ClusterService;
|
import org.elasticsearch.cluster.ClusterService;
|
||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
import org.elasticsearch.cluster.RestoreInProgress;
|
|
||||||
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
|
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
|
||||||
import org.elasticsearch.cluster.block.ClusterBlock;
|
import org.elasticsearch.cluster.block.ClusterBlock;
|
||||||
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||||
@ -39,8 +37,9 @@ import org.elasticsearch.common.component.AbstractComponent;
|
|||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.index.IndexNotFoundException;
|
import org.elasticsearch.index.IndexNotFoundException;
|
||||||
import org.elasticsearch.index.shard.ShardId;
|
|
||||||
import org.elasticsearch.rest.RestStatus;
|
import org.elasticsearch.rest.RestStatus;
|
||||||
|
import org.elasticsearch.snapshots.RestoreService;
|
||||||
|
import org.elasticsearch.snapshots.SnapshotsService;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -99,27 +98,10 @@ public class MetaDataIndexStateService extends AbstractComponent {
|
|||||||
return currentState;
|
return currentState;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if any of the indices to be closed are currently being restored from a snapshot and fail closing if such an index
|
// Check if index closing conflicts with any running restores
|
||||||
// is found as closing an index that is being restored makes the index unusable (it cannot be recovered).
|
RestoreService.checkIndexClosing(currentState, indicesToClose);
|
||||||
RestoreInProgress restore = currentState.custom(RestoreInProgress.TYPE);
|
// Check if index closing conflicts with any running snapshots
|
||||||
if (restore != null) {
|
SnapshotsService.checkIndexClosing(currentState, indicesToClose);
|
||||||
Set<String> indicesToFail = null;
|
|
||||||
for (RestoreInProgress.Entry entry : restore.entries()) {
|
|
||||||
for (ObjectObjectCursor<ShardId, RestoreInProgress.ShardRestoreStatus> shard : entry.shards()) {
|
|
||||||
if (!shard.value.state().completed()) {
|
|
||||||
if (indicesToClose.contains(shard.key.getIndexName())) {
|
|
||||||
if (indicesToFail == null) {
|
|
||||||
indicesToFail = new HashSet<>();
|
|
||||||
}
|
|
||||||
indicesToFail.add(shard.key.getIndexName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (indicesToFail != null) {
|
|
||||||
throw new IllegalArgumentException("Cannot close indices that are being restored: " + indicesToFail);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("closing indices [{}]", indicesAsString);
|
logger.info("closing indices [{}]", indicesAsString);
|
||||||
|
|
||||||
|
@ -19,24 +19,40 @@
|
|||||||
|
|
||||||
package org.elasticsearch.cluster.node;
|
package org.elasticsearch.cluster.node;
|
||||||
|
|
||||||
|
import org.elasticsearch.Version;
|
||||||
|
import org.elasticsearch.common.Randomness;
|
||||||
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.component.AbstractComponent;
|
import org.elasticsearch.common.component.AbstractComponent;
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.transport.TransportAddress;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
public class DiscoveryNodeService extends AbstractComponent {
|
public class DiscoveryNodeService extends AbstractComponent {
|
||||||
|
|
||||||
|
public static final Setting<Long> NODE_ID_SEED_SETTING =
|
||||||
|
// don't use node.id.seed so it won't be seen as an attribute
|
||||||
|
Setting.longSetting("node_id.seed", 0L, Long.MIN_VALUE, false, Setting.Scope.CLUSTER);
|
||||||
private final List<CustomAttributesProvider> customAttributesProviders = new CopyOnWriteArrayList<>();
|
private final List<CustomAttributesProvider> customAttributesProviders = new CopyOnWriteArrayList<>();
|
||||||
|
private final Version version;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public DiscoveryNodeService(Settings settings) {
|
public DiscoveryNodeService(Settings settings, Version version) {
|
||||||
super(settings);
|
super(settings);
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String generateNodeId(Settings settings) {
|
||||||
|
Random random = Randomness.get(settings, NODE_ID_SEED_SETTING);
|
||||||
|
return Strings.randomBase64UUID(random);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiscoveryNodeService addCustomAttributeProvider(CustomAttributesProvider customAttributesProvider) {
|
public DiscoveryNodeService addCustomAttributeProvider(CustomAttributesProvider customAttributesProvider) {
|
||||||
@ -44,7 +60,7 @@ public class DiscoveryNodeService extends AbstractComponent {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, String> buildAttributes() {
|
public DiscoveryNode buildLocalNode(TransportAddress publishAddress) {
|
||||||
Map<String, String> attributes = new HashMap<>(settings.getByPrefix("node.").getAsMap());
|
Map<String, String> attributes = new HashMap<>(settings.getByPrefix("node.").getAsMap());
|
||||||
attributes.remove("name"); // name is extracted in other places
|
attributes.remove("name"); // name is extracted in other places
|
||||||
if (attributes.containsKey("client")) {
|
if (attributes.containsKey("client")) {
|
||||||
@ -76,10 +92,11 @@ public class DiscoveryNodeService extends AbstractComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return attributes;
|
final String nodeId = generateNodeId(settings);
|
||||||
|
return new DiscoveryNode(settings.get("node.name"), nodeId, publishAddress, attributes, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static interface CustomAttributesProvider {
|
public interface CustomAttributesProvider {
|
||||||
|
|
||||||
Map<String, String> buildAttributes();
|
Map<String, String> buildAttributes();
|
||||||
}
|
}
|
||||||
|
@ -597,6 +597,13 @@ public class RoutingNodes implements Iterable<RoutingNode> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of routing nodes
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return nodesToShards.size();
|
||||||
|
}
|
||||||
|
|
||||||
public static final class UnassignedShards implements Iterable<ShardRouting> {
|
public static final class UnassignedShards implements Iterable<ShardRouting> {
|
||||||
|
|
||||||
private final RoutingNodes nodes;
|
private final RoutingNodes nodes;
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
package org.elasticsearch.cluster.routing.allocation;
|
package org.elasticsearch.cluster.routing.allocation;
|
||||||
|
|
||||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||||
import org.apache.lucene.util.ArrayUtil;
|
|
||||||
import org.elasticsearch.cluster.ClusterInfoService;
|
import org.elasticsearch.cluster.ClusterInfoService;
|
||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
import org.elasticsearch.cluster.health.ClusterHealthStatus;
|
import org.elasticsearch.cluster.health.ClusterHealthStatus;
|
||||||
@ -36,13 +35,13 @@ import org.elasticsearch.cluster.routing.RoutingNodes;
|
|||||||
import org.elasticsearch.cluster.routing.RoutingTable;
|
import org.elasticsearch.cluster.routing.RoutingTable;
|
||||||
import org.elasticsearch.cluster.routing.ShardRouting;
|
import org.elasticsearch.cluster.routing.ShardRouting;
|
||||||
import org.elasticsearch.cluster.routing.UnassignedInfo;
|
import org.elasticsearch.cluster.routing.UnassignedInfo;
|
||||||
import org.elasticsearch.cluster.routing.allocation.allocator.ShardsAllocators;
|
import org.elasticsearch.cluster.routing.allocation.allocator.ShardsAllocator;
|
||||||
import org.elasticsearch.cluster.routing.allocation.command.AllocationCommands;
|
import org.elasticsearch.cluster.routing.allocation.command.AllocationCommands;
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders;
|
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders;
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.Decision;
|
|
||||||
import org.elasticsearch.common.component.AbstractComponent;
|
import org.elasticsearch.common.component.AbstractComponent;
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.gateway.GatewayAllocator;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -63,14 +62,17 @@ import java.util.stream.Collectors;
|
|||||||
public class AllocationService extends AbstractComponent {
|
public class AllocationService extends AbstractComponent {
|
||||||
|
|
||||||
private final AllocationDeciders allocationDeciders;
|
private final AllocationDeciders allocationDeciders;
|
||||||
|
private final GatewayAllocator gatewayAllocator;
|
||||||
|
private final ShardsAllocator shardsAllocator;
|
||||||
private final ClusterInfoService clusterInfoService;
|
private final ClusterInfoService clusterInfoService;
|
||||||
private final ShardsAllocators shardsAllocators;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public AllocationService(Settings settings, AllocationDeciders allocationDeciders, ShardsAllocators shardsAllocators, ClusterInfoService clusterInfoService) {
|
public AllocationService(Settings settings, AllocationDeciders allocationDeciders, GatewayAllocator gatewayAllocator,
|
||||||
|
ShardsAllocator shardsAllocator, ClusterInfoService clusterInfoService) {
|
||||||
super(settings);
|
super(settings);
|
||||||
this.allocationDeciders = allocationDeciders;
|
this.allocationDeciders = allocationDeciders;
|
||||||
this.shardsAllocators = shardsAllocators;
|
this.gatewayAllocator = gatewayAllocator;
|
||||||
|
this.shardsAllocator = shardsAllocator;
|
||||||
this.clusterInfoService = clusterInfoService;
|
this.clusterInfoService = clusterInfoService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +94,7 @@ public class AllocationService extends AbstractComponent {
|
|||||||
if (!changed) {
|
if (!changed) {
|
||||||
return new RoutingAllocation.Result(false, clusterState.routingTable(), clusterState.metaData());
|
return new RoutingAllocation.Result(false, clusterState.routingTable(), clusterState.metaData());
|
||||||
}
|
}
|
||||||
shardsAllocators.applyStartedShards(allocation);
|
gatewayAllocator.applyStartedShards(allocation);
|
||||||
if (withReroute) {
|
if (withReroute) {
|
||||||
reroute(allocation);
|
reroute(allocation);
|
||||||
}
|
}
|
||||||
@ -192,7 +194,7 @@ public class AllocationService extends AbstractComponent {
|
|||||||
if (!changed) {
|
if (!changed) {
|
||||||
return new RoutingAllocation.Result(false, clusterState.routingTable(), clusterState.metaData());
|
return new RoutingAllocation.Result(false, clusterState.routingTable(), clusterState.metaData());
|
||||||
}
|
}
|
||||||
shardsAllocators.applyFailedShards(allocation);
|
gatewayAllocator.applyFailedShards(allocation);
|
||||||
reroute(allocation);
|
reroute(allocation);
|
||||||
final RoutingAllocation.Result result = buildChangedResult(clusterState.metaData(), routingNodes);
|
final RoutingAllocation.Result result = buildChangedResult(clusterState.metaData(), routingNodes);
|
||||||
String failedShardsAsString = firstListElementsToCommaDelimitedString(failedShards, s -> s.shard.shardId().toString());
|
String failedShardsAsString = firstListElementsToCommaDelimitedString(failedShards, s -> s.shard.shardId().toString());
|
||||||
@ -306,14 +308,10 @@ public class AllocationService extends AbstractComponent {
|
|||||||
if (allocation.routingNodes().unassigned().size() > 0) {
|
if (allocation.routingNodes().unassigned().size() > 0) {
|
||||||
updateLeftDelayOfUnassignedShards(allocation, settings);
|
updateLeftDelayOfUnassignedShards(allocation, settings);
|
||||||
|
|
||||||
changed |= shardsAllocators.allocateUnassigned(allocation);
|
changed |= gatewayAllocator.allocateUnassigned(allocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
// move shards that no longer can be allocated
|
changed |= shardsAllocator.allocate(allocation);
|
||||||
changed |= shardsAllocators.moveShards(allocation);
|
|
||||||
|
|
||||||
// rebalance
|
|
||||||
changed |= shardsAllocators.rebalance(allocation);
|
|
||||||
assert RoutingNodes.assertShardStats(allocation.routingNodes());
|
assert RoutingNodes.assertShardStats(allocation.routingNodes());
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package org.elasticsearch.cluster.routing.allocation.allocator;
|
package org.elasticsearch.cluster.routing.allocation.allocator;
|
||||||
|
|
||||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
|
||||||
import org.apache.lucene.util.ArrayUtil;
|
import org.apache.lucene.util.ArrayUtil;
|
||||||
import org.apache.lucene.util.IntroSorter;
|
import org.apache.lucene.util.IntroSorter;
|
||||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||||
@ -28,9 +27,7 @@ import org.elasticsearch.cluster.routing.RoutingNode;
|
|||||||
import org.elasticsearch.cluster.routing.RoutingNodes;
|
import org.elasticsearch.cluster.routing.RoutingNodes;
|
||||||
import org.elasticsearch.cluster.routing.ShardRouting;
|
import org.elasticsearch.cluster.routing.ShardRouting;
|
||||||
import org.elasticsearch.cluster.routing.ShardRoutingState;
|
import org.elasticsearch.cluster.routing.ShardRoutingState;
|
||||||
import org.elasticsearch.cluster.routing.allocation.FailedRerouteAllocation;
|
|
||||||
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
|
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
|
||||||
import org.elasticsearch.cluster.routing.allocation.StartedRerouteAllocation;
|
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders;
|
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders;
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.Decision;
|
import org.elasticsearch.cluster.routing.allocation.decider.Decision;
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.Decision.Type;
|
import org.elasticsearch.cluster.routing.allocation.decider.Decision.Type;
|
||||||
@ -42,18 +39,14 @@ import org.elasticsearch.common.settings.Setting;
|
|||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.gateway.PriorityComparator;
|
import org.elasticsearch.gateway.PriorityComparator;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
import static org.elasticsearch.cluster.routing.ShardRoutingState.RELOCATING;
|
import static org.elasticsearch.cluster.routing.ShardRoutingState.RELOCATING;
|
||||||
|
|
||||||
@ -103,27 +96,16 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyStartedShards(StartedRerouteAllocation allocation) { /* ONLY FOR GATEWAYS */ }
|
public boolean allocate(RoutingAllocation allocation) {
|
||||||
|
if (allocation.routingNodes().size() == 0) {
|
||||||
@Override
|
/* with no nodes this is pointless */
|
||||||
public void applyFailedShards(FailedRerouteAllocation allocation) { /* ONLY FOR GATEWAYS */ }
|
return false;
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public boolean allocateUnassigned(RoutingAllocation allocation) {
|
|
||||||
final Balancer balancer = new Balancer(logger, allocation, weightFunction, threshold);
|
final Balancer balancer = new Balancer(logger, allocation, weightFunction, threshold);
|
||||||
return balancer.allocateUnassigned();
|
boolean changed = balancer.allocateUnassigned();
|
||||||
}
|
changed |= balancer.moveShards();
|
||||||
|
changed |= balancer.balance();
|
||||||
@Override
|
return changed;
|
||||||
public boolean rebalance(RoutingAllocation allocation) {
|
|
||||||
final Balancer balancer = new Balancer(logger, allocation, weightFunction, threshold);
|
|
||||||
return balancer.balance();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean moveShards(RoutingAllocation allocation) {
|
|
||||||
final Balancer balancer = new Balancer(logger, allocation, weightFunction, threshold);
|
|
||||||
return balancer.moveShards();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -203,8 +185,8 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
}
|
}
|
||||||
|
|
||||||
private float weight(Balancer balancer, ModelNode node, String index, int numAdditionalShards) {
|
private float weight(Balancer balancer, ModelNode node, String index, int numAdditionalShards) {
|
||||||
final float weightShard = (node.numShards() + numAdditionalShards - balancer.avgShardsPerNode());
|
final float weightShard = node.numShards() + numAdditionalShards - balancer.avgShardsPerNode();
|
||||||
final float weightIndex = (node.numShards(index) + numAdditionalShards - balancer.avgShardsPerNode(index));
|
final float weightIndex = node.numShards(index) + numAdditionalShards - balancer.avgShardsPerNode(index);
|
||||||
return theta0 * weightShard + theta1 * weightIndex;
|
return theta0 * weightShard + theta1 * weightIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +198,6 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
public static class Balancer {
|
public static class Balancer {
|
||||||
private final ESLogger logger;
|
private final ESLogger logger;
|
||||||
private final Map<String, ModelNode> nodes = new HashMap<>();
|
private final Map<String, ModelNode> nodes = new HashMap<>();
|
||||||
private final HashSet<String> indices = new HashSet<>();
|
|
||||||
private final RoutingAllocation allocation;
|
private final RoutingAllocation allocation;
|
||||||
private final RoutingNodes routingNodes;
|
private final RoutingNodes routingNodes;
|
||||||
private final WeightFunction weight;
|
private final WeightFunction weight;
|
||||||
@ -225,19 +206,15 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
private final MetaData metaData;
|
private final MetaData metaData;
|
||||||
private final float avgShardsPerNode;
|
private final float avgShardsPerNode;
|
||||||
|
|
||||||
private final Predicate<ShardRouting> assignedFilter = shard -> shard.assignedToNode();
|
|
||||||
|
|
||||||
public Balancer(ESLogger logger, RoutingAllocation allocation, WeightFunction weight, float threshold) {
|
public Balancer(ESLogger logger, RoutingAllocation allocation, WeightFunction weight, float threshold) {
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.allocation = allocation;
|
this.allocation = allocation;
|
||||||
this.weight = weight;
|
this.weight = weight;
|
||||||
this.threshold = threshold;
|
this.threshold = threshold;
|
||||||
this.routingNodes = allocation.routingNodes();
|
this.routingNodes = allocation.routingNodes();
|
||||||
for (RoutingNode node : routingNodes) {
|
|
||||||
nodes.put(node.nodeId(), new ModelNode(node.nodeId()));
|
|
||||||
}
|
|
||||||
metaData = routingNodes.metaData();
|
metaData = routingNodes.metaData();
|
||||||
avgShardsPerNode = ((float) metaData.totalNumberOfShards()) / nodes.size();
|
avgShardsPerNode = ((float) metaData.totalNumberOfShards()) / routingNodes.size();
|
||||||
|
buildModelFromAssigned();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -271,17 +248,6 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
return new NodeSorter(nodesArray(), weight, this);
|
return new NodeSorter(nodesArray(), weight, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean initialize(RoutingNodes routing, RoutingNodes.UnassignedShards unassigned) {
|
|
||||||
if (logger.isTraceEnabled()) {
|
|
||||||
logger.trace("Start distributing Shards");
|
|
||||||
}
|
|
||||||
for (ObjectCursor<String> index : allocation.routingTable().indicesRouting().keys()) {
|
|
||||||
indices.add(index.value);
|
|
||||||
}
|
|
||||||
buildModelFromAssigned(routing.shards(assignedFilter));
|
|
||||||
return allocateUnassigned(unassigned);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static float absDelta(float lower, float higher) {
|
private static float absDelta(float lower, float higher) {
|
||||||
assert higher >= lower : higher + " lt " + lower +" but was expected to be gte";
|
assert higher >= lower : higher + " lt " + lower +" but was expected to be gte";
|
||||||
return Math.abs(higher - lower);
|
return Math.abs(higher - lower);
|
||||||
@ -295,12 +261,36 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocates all possible unassigned shards
|
* Balances the nodes on the cluster model according to the weight function.
|
||||||
|
* The actual balancing is delegated to {@link #balanceByWeights()}
|
||||||
|
*
|
||||||
* @return <code>true</code> if the current configuration has been
|
* @return <code>true</code> if the current configuration has been
|
||||||
* changed, otherwise <code>false</code>
|
* changed, otherwise <code>false</code>
|
||||||
*/
|
*/
|
||||||
final boolean allocateUnassigned() {
|
private boolean balance() {
|
||||||
return balance(true);
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("Start balancing cluster");
|
||||||
|
}
|
||||||
|
if (allocation.hasPendingAsyncFetch()) {
|
||||||
|
/*
|
||||||
|
* see https://github.com/elastic/elasticsearch/issues/14387
|
||||||
|
* if we allow rebalance operations while we are still fetching shard store data
|
||||||
|
* we might end up with unnecessary rebalance operations which can be super confusion/frustrating
|
||||||
|
* since once the fetches come back we might just move all the shards back again.
|
||||||
|
* Therefore we only do a rebalance if we have fetched all information.
|
||||||
|
*/
|
||||||
|
logger.debug("skipping rebalance due to in-flight shard/store fetches");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (allocation.deciders().canRebalance(allocation).type() != Type.YES) {
|
||||||
|
logger.trace("skipping rebalance as it is disabled");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (nodes.size() < 2) { /* skip if we only have one node */
|
||||||
|
logger.trace("skipping rebalance as single node only");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return balanceByWeights();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -317,120 +307,100 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
* @return <code>true</code> if the current configuration has been
|
* @return <code>true</code> if the current configuration has been
|
||||||
* changed, otherwise <code>false</code>
|
* changed, otherwise <code>false</code>
|
||||||
*/
|
*/
|
||||||
public boolean balance() {
|
private boolean balanceByWeights() {
|
||||||
return balance(false);
|
boolean changed = false;
|
||||||
}
|
final NodeSorter sorter = newNodeSorter();
|
||||||
|
final AllocationDeciders deciders = allocation.deciders();
|
||||||
|
final ModelNode[] modelNodes = sorter.modelNodes;
|
||||||
|
final float[] weights = sorter.weights;
|
||||||
|
for (String index : buildWeightOrderedIndices(sorter)) {
|
||||||
|
IndexMetaData indexMetaData = metaData.index(index);
|
||||||
|
|
||||||
private boolean balance(boolean onlyAssign) {
|
// find nodes that have a shard of this index or where shards of this index are allowed to stay
|
||||||
if (this.nodes.isEmpty()) {
|
// move these nodes to the front of modelNodes so that we can only balance based on these nodes
|
||||||
/* with no nodes this is pointless */
|
int relevantNodes = 0;
|
||||||
return false;
|
for (int i = 0; i < modelNodes.length; i++) {
|
||||||
}
|
ModelNode modelNode = modelNodes[i];
|
||||||
if (logger.isTraceEnabled()) {
|
if (modelNode.getIndex(index) != null
|
||||||
if (onlyAssign) {
|
|| deciders.canAllocate(indexMetaData, modelNode.getRoutingNode(), allocation).type() != Type.NO) {
|
||||||
logger.trace("Start balancing cluster");
|
// swap nodes at position i and relevantNodes
|
||||||
} else {
|
modelNodes[i] = modelNodes[relevantNodes];
|
||||||
logger.trace("Start assigning unassigned shards");
|
modelNodes[relevantNodes] = modelNode;
|
||||||
|
relevantNodes++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
final RoutingNodes.UnassignedShards unassigned = routingNodes.unassigned();
|
|
||||||
boolean changed = initialize(routingNodes, unassigned);
|
|
||||||
if (onlyAssign == false && changed == false && allocation.deciders().canRebalance(allocation).type() == Type.YES) {
|
|
||||||
NodeSorter sorter = newNodeSorter();
|
|
||||||
if (nodes.size() > 1) { /* skip if we only have one node */
|
|
||||||
AllocationDeciders deciders = allocation.deciders();
|
|
||||||
final ModelNode[] modelNodes = sorter.modelNodes;
|
|
||||||
final float[] weights = sorter.weights;
|
|
||||||
for (String index : buildWeightOrderedIndices(sorter)) {
|
|
||||||
IndexMetaData indexMetaData = metaData.index(index);
|
|
||||||
|
|
||||||
// find nodes that have a shard of this index or where shards of this index are allowed to stay
|
if (relevantNodes < 2) {
|
||||||
// move these nodes to the front of modelNodes so that we can only balance based on these nodes
|
continue;
|
||||||
int relevantNodes = 0;
|
}
|
||||||
for (int i = 0; i < modelNodes.length; i++) {
|
|
||||||
ModelNode modelNode = modelNodes[i];
|
sorter.reset(index, 0, relevantNodes);
|
||||||
if (modelNode.getIndex(index) != null
|
int lowIdx = 0;
|
||||||
|| deciders.canAllocate(indexMetaData, modelNode.getRoutingNode(routingNodes), allocation).type() != Type.NO) {
|
int highIdx = relevantNodes - 1;
|
||||||
// swap nodes at position i and relevantNodes
|
while (true) {
|
||||||
modelNodes[i] = modelNodes[relevantNodes];
|
final ModelNode minNode = modelNodes[lowIdx];
|
||||||
modelNodes[relevantNodes] = modelNode;
|
final ModelNode maxNode = modelNodes[highIdx];
|
||||||
relevantNodes++;
|
advance_range:
|
||||||
|
if (maxNode.numShards(index) > 0) {
|
||||||
|
final float delta = absDelta(weights[lowIdx], weights[highIdx]);
|
||||||
|
if (lessThan(delta, threshold)) {
|
||||||
|
if (lowIdx > 0 && highIdx-1 > 0 // is there a chance for a higher delta?
|
||||||
|
&& (absDelta(weights[0], weights[highIdx-1]) > threshold) // check if we need to break at all
|
||||||
|
) {
|
||||||
|
/* This is a special case if allocations from the "heaviest" to the "lighter" nodes is not possible
|
||||||
|
* due to some allocation decider restrictions like zone awareness. if one zone has for instance
|
||||||
|
* less nodes than another zone. so one zone is horribly overloaded from a balanced perspective but we
|
||||||
|
* can't move to the "lighter" shards since otherwise the zone would go over capacity.
|
||||||
|
*
|
||||||
|
* This break jumps straight to the condition below were we start moving from the high index towards
|
||||||
|
* the low index to shrink the window we are considering for balance from the other direction.
|
||||||
|
* (check shrinking the window from MAX to MIN)
|
||||||
|
* See #3580
|
||||||
|
*/
|
||||||
|
break advance_range;
|
||||||
}
|
}
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("Stop balancing index [{}] min_node [{}] weight: [{}] max_node [{}] weight: [{}] delta: [{}]",
|
||||||
|
index, maxNode.getNodeId(), weights[highIdx], minNode.getNodeId(), weights[lowIdx], delta);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
if (relevantNodes < 2) {
|
logger.trace("Balancing from node [{}] weight: [{}] to node [{}] weight: [{}] delta: [{}]",
|
||||||
|
maxNode.getNodeId(), weights[highIdx], minNode.getNodeId(), weights[lowIdx], delta);
|
||||||
|
}
|
||||||
|
/* pass the delta to the replication function to prevent relocations that only swap the weights of the two nodes.
|
||||||
|
* a relocation must bring us closer to the balance if we only achieve the same delta the relocation is useless */
|
||||||
|
if (tryRelocateShard(minNode, maxNode, index, delta)) {
|
||||||
|
/*
|
||||||
|
* TODO we could be a bit smarter here, we don't need to fully sort necessarily
|
||||||
|
* we could just find the place to insert linearly but the win might be minor
|
||||||
|
* compared to the added complexity
|
||||||
|
*/
|
||||||
|
weights[lowIdx] = sorter.weight(modelNodes[lowIdx]);
|
||||||
|
weights[highIdx] = sorter.weight(modelNodes[highIdx]);
|
||||||
|
sorter.sort(0, relevantNodes);
|
||||||
|
lowIdx = 0;
|
||||||
|
highIdx = relevantNodes - 1;
|
||||||
|
changed = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
sorter.reset(index, 0, relevantNodes);
|
if (lowIdx < highIdx - 1) {
|
||||||
int lowIdx = 0;
|
/* Shrinking the window from MIN to MAX
|
||||||
int highIdx = relevantNodes - 1;
|
* we can't move from any shard from the min node lets move on to the next node
|
||||||
while (true) {
|
* and see if the threshold still holds. We either don't have any shard of this
|
||||||
final ModelNode minNode = modelNodes[lowIdx];
|
* index on this node of allocation deciders prevent any relocation.*/
|
||||||
final ModelNode maxNode = modelNodes[highIdx];
|
lowIdx++;
|
||||||
advance_range:
|
} else if (lowIdx > 0) {
|
||||||
if (maxNode.numShards(index) > 0) {
|
/* Shrinking the window from MAX to MIN
|
||||||
final float delta = absDelta(weights[lowIdx], weights[highIdx]);
|
* now we go max to min since obviously we can't move anything to the max node
|
||||||
if (lessThan(delta, threshold)) {
|
* lets pick the next highest */
|
||||||
if (lowIdx > 0 && highIdx-1 > 0 // is there a chance for a higher delta?
|
lowIdx = 0;
|
||||||
&& (absDelta(weights[0], weights[highIdx-1]) > threshold) // check if we need to break at all
|
highIdx--;
|
||||||
) {
|
} else {
|
||||||
/* This is a special case if allocations from the "heaviest" to the "lighter" nodes is not possible
|
/* we are done here, we either can't relocate anymore or we are balanced */
|
||||||
* due to some allocation decider restrictions like zone awareness. if one zone has for instance
|
break;
|
||||||
* less nodes than another zone. so one zone is horribly overloaded from a balanced perspective but we
|
|
||||||
* can't move to the "lighter" shards since otherwise the zone would go over capacity.
|
|
||||||
*
|
|
||||||
* This break jumps straight to the condition below were we start moving from the high index towards
|
|
||||||
* the low index to shrink the window we are considering for balance from the other direction.
|
|
||||||
* (check shrinking the window from MAX to MIN)
|
|
||||||
* See #3580
|
|
||||||
*/
|
|
||||||
break advance_range;
|
|
||||||
}
|
|
||||||
if (logger.isTraceEnabled()) {
|
|
||||||
logger.trace("Stop balancing index [{}] min_node [{}] weight: [{}] max_node [{}] weight: [{}] delta: [{}]",
|
|
||||||
index, maxNode.getNodeId(), weights[highIdx], minNode.getNodeId(), weights[lowIdx], delta);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (logger.isTraceEnabled()) {
|
|
||||||
logger.trace("Balancing from node [{}] weight: [{}] to node [{}] weight: [{}] delta: [{}]",
|
|
||||||
maxNode.getNodeId(), weights[highIdx], minNode.getNodeId(), weights[lowIdx], delta);
|
|
||||||
}
|
|
||||||
/* pass the delta to the replication function to prevent relocations that only swap the weights of the two nodes.
|
|
||||||
* a relocation must bring us closer to the balance if we only achieve the same delta the relocation is useless */
|
|
||||||
if (tryRelocateShard(minNode, maxNode, index, delta)) {
|
|
||||||
/*
|
|
||||||
* TODO we could be a bit smarter here, we don't need to fully sort necessarily
|
|
||||||
* we could just find the place to insert linearly but the win might be minor
|
|
||||||
* compared to the added complexity
|
|
||||||
*/
|
|
||||||
weights[lowIdx] = sorter.weight(modelNodes[lowIdx]);
|
|
||||||
weights[highIdx] = sorter.weight(modelNodes[highIdx]);
|
|
||||||
sorter.sort(0, relevantNodes);
|
|
||||||
lowIdx = 0;
|
|
||||||
highIdx = relevantNodes - 1;
|
|
||||||
changed = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (lowIdx < highIdx - 1) {
|
|
||||||
/* Shrinking the window from MIN to MAX
|
|
||||||
* we can't move from any shard from the min node lets move on to the next node
|
|
||||||
* and see if the threshold still holds. We either don't have any shard of this
|
|
||||||
* index on this node of allocation deciders prevent any relocation.*/
|
|
||||||
lowIdx++;
|
|
||||||
} else if (lowIdx > 0) {
|
|
||||||
/* Shrinking the window from MAX to MIN
|
|
||||||
* now we go max to min since obviously we can't move anything to the max node
|
|
||||||
* lets pick the next highest */
|
|
||||||
lowIdx = 0;
|
|
||||||
highIdx--;
|
|
||||||
} else {
|
|
||||||
/* we are done here, we either can't relocate anymore or we are balanced */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -451,7 +421,7 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
* to the nodes we relocated them from.
|
* to the nodes we relocated them from.
|
||||||
*/
|
*/
|
||||||
private String[] buildWeightOrderedIndices(NodeSorter sorter) {
|
private String[] buildWeightOrderedIndices(NodeSorter sorter) {
|
||||||
final String[] indices = this.indices.toArray(new String[this.indices.size()]);
|
final String[] indices = allocation.routingTable().indicesRouting().keys().toArray(String.class);
|
||||||
final float[] deltas = new float[indices.length];
|
final float[] deltas = new float[indices.length];
|
||||||
for (int i = 0; i < deltas.length; i++) {
|
for (int i = 0; i < deltas.length; i++) {
|
||||||
sorter.reset(indices[i]);
|
sorter.reset(indices[i]);
|
||||||
@ -503,20 +473,16 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
* @return <code>true</code> if the allocation has changed, otherwise <code>false</code>
|
* @return <code>true</code> if the allocation has changed, otherwise <code>false</code>
|
||||||
*/
|
*/
|
||||||
public boolean moveShards() {
|
public boolean moveShards() {
|
||||||
if (nodes.isEmpty()) {
|
// Iterate over the started shards interleaving between nodes, and check if they can remain. In the presence of throttling
|
||||||
/* with no nodes this is pointless */
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a copy of the started shards interleaving between nodes, and check if they can remain. In the presence of throttling
|
|
||||||
// shard movements, the goal of this iteration order is to achieve a fairer movement of shards from the nodes that are
|
// shard movements, the goal of this iteration order is to achieve a fairer movement of shards from the nodes that are
|
||||||
// offloading the shards.
|
// offloading the shards.
|
||||||
List<ShardRouting> shards = new ArrayList<>();
|
boolean changed = false;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
boolean found = true;
|
boolean found = true;
|
||||||
|
final NodeSorter sorter = newNodeSorter();
|
||||||
while (found) {
|
while (found) {
|
||||||
found = false;
|
found = false;
|
||||||
for (RoutingNode routingNode : routingNodes) {
|
for (RoutingNode routingNode : allocation.routingNodes()) {
|
||||||
if (index >= routingNode.size()) {
|
if (index >= routingNode.size()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -524,64 +490,52 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
ShardRouting shardRouting = routingNode.get(index);
|
ShardRouting shardRouting = routingNode.get(index);
|
||||||
// we can only move started shards...
|
// we can only move started shards...
|
||||||
if (shardRouting.started()) {
|
if (shardRouting.started()) {
|
||||||
shards.add(shardRouting);
|
final ModelNode sourceNode = nodes.get(shardRouting.currentNodeId());
|
||||||
|
assert sourceNode != null && sourceNode.containsShard(shardRouting);
|
||||||
|
Decision decision = allocation.deciders().canRemain(shardRouting, routingNode, allocation);
|
||||||
|
if (decision.type() == Decision.Type.NO) {
|
||||||
|
changed |= moveShard(sorter, shardRouting, sourceNode, routingNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
if (shards.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final RoutingNodes.UnassignedShards unassigned = routingNodes.unassigned();
|
return changed;
|
||||||
boolean changed = initialize(routingNodes, unassigned);
|
}
|
||||||
if (changed == false) {
|
|
||||||
final NodeSorter sorter = newNodeSorter();
|
/**
|
||||||
final ModelNode[] modelNodes = sorter.modelNodes;
|
* Move started shard to the minimal eligible node with respect to the weight function
|
||||||
for (ShardRouting shardRouting : shards) {
|
*
|
||||||
final ModelNode sourceNode = nodes.get(shardRouting.currentNodeId());
|
* @return <code>true</code> if the shard was moved successfully, otherwise <code>false</code>
|
||||||
assert sourceNode != null && sourceNode.containsShard(shardRouting);
|
*/
|
||||||
final RoutingNode routingNode = sourceNode.getRoutingNode(routingNodes);
|
private boolean moveShard(NodeSorter sorter, ShardRouting shardRouting, ModelNode sourceNode, RoutingNode routingNode) {
|
||||||
Decision decision = allocation.deciders().canRemain(shardRouting, routingNode, allocation);
|
logger.debug("[{}][{}] allocated on [{}], but can no longer be allocated on it, moving...", shardRouting.index(), shardRouting.id(), routingNode.node());
|
||||||
if (decision.type() == Decision.Type.NO) {
|
sorter.reset(shardRouting.getIndexName());
|
||||||
logger.debug("[{}][{}] allocated on [{}], but can no longer be allocated on it, moving...", shardRouting.index(), shardRouting.id(), routingNode.node());
|
/*
|
||||||
sorter.reset(shardRouting.getIndexName());
|
* the sorter holds the minimum weight node first for the shards index.
|
||||||
/*
|
* We now walk through the nodes until we find a node to allocate the shard.
|
||||||
* the sorter holds the minimum weight node first for the shards index.
|
* This is not guaranteed to be balanced after this operation we still try best effort to
|
||||||
* We now walk through the nodes until we find a node to allocate the shard.
|
* allocate on the minimal eligible node.
|
||||||
* This is not guaranteed to be balanced after this operation we still try best effort to
|
*/
|
||||||
* allocate on the minimal eligible node.
|
for (ModelNode currentNode : sorter.modelNodes) {
|
||||||
*/
|
if (currentNode != sourceNode) {
|
||||||
boolean moved = false;
|
RoutingNode target = currentNode.getRoutingNode();
|
||||||
for (ModelNode currentNode : modelNodes) {
|
Decision allocationDecision = allocation.deciders().canAllocate(shardRouting, target, allocation);
|
||||||
if (currentNode == sourceNode) {
|
Decision rebalanceDecision = allocation.deciders().canRebalance(shardRouting, allocation);
|
||||||
continue;
|
if (allocationDecision.type() == Type.YES && rebalanceDecision.type() == Type.YES) { // TODO maybe we can respect throttling here too?
|
||||||
}
|
sourceNode.removeShard(shardRouting);
|
||||||
RoutingNode target = currentNode.getRoutingNode(routingNodes);
|
ShardRouting targetRelocatingShard = routingNodes.relocate(shardRouting, target.nodeId(), allocation.clusterInfo().getShardSize(shardRouting, ShardRouting.UNAVAILABLE_EXPECTED_SHARD_SIZE));
|
||||||
Decision allocationDecision = allocation.deciders().canAllocate(shardRouting, target, allocation);
|
currentNode.addShard(targetRelocatingShard);
|
||||||
Decision rebalanceDecision = allocation.deciders().canRebalance(shardRouting, allocation);
|
if (logger.isTraceEnabled()) {
|
||||||
if (allocationDecision.type() == Type.YES && rebalanceDecision.type() == Type.YES) { // TODO maybe we can respect throttling here too?
|
logger.trace("Moved shard [{}] to node [{}]", shardRouting, routingNode.node());
|
||||||
Decision sourceDecision = sourceNode.removeShard(shardRouting);
|
|
||||||
ShardRouting targetRelocatingShard = routingNodes.relocate(shardRouting, target.nodeId(), allocation.clusterInfo().getShardSize(shardRouting, ShardRouting.UNAVAILABLE_EXPECTED_SHARD_SIZE));
|
|
||||||
// re-add (now relocating shard) to source node
|
|
||||||
sourceNode.addShard(shardRouting, sourceDecision);
|
|
||||||
Decision targetDecision = new Decision.Multi().add(allocationDecision).add(rebalanceDecision);
|
|
||||||
currentNode.addShard(targetRelocatingShard, targetDecision);
|
|
||||||
if (logger.isTraceEnabled()) {
|
|
||||||
logger.trace("Moved shard [{}] to node [{}]", shardRouting, routingNode.node());
|
|
||||||
}
|
|
||||||
moved = true;
|
|
||||||
changed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (moved == false) {
|
|
||||||
logger.debug("[{}][{}] can't move", shardRouting.index(), shardRouting.id());
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return changed;
|
logger.debug("[{}][{}] can't move", shardRouting.index(), shardRouting.id());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -593,18 +547,19 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
* on the target node which we respect during the allocation / balancing
|
* on the target node which we respect during the allocation / balancing
|
||||||
* process. In short, this method recreates the status-quo in the cluster.
|
* process. In short, this method recreates the status-quo in the cluster.
|
||||||
*/
|
*/
|
||||||
private void buildModelFromAssigned(Iterable<ShardRouting> shards) {
|
private void buildModelFromAssigned() {
|
||||||
for (ShardRouting shard : shards) {
|
for (RoutingNode rn : routingNodes) {
|
||||||
assert shard.assignedToNode();
|
ModelNode node = new ModelNode(rn);
|
||||||
/* we skip relocating shards here since we expect an initializing shard with the same id coming in */
|
nodes.put(rn.nodeId(), node);
|
||||||
if (shard.state() == RELOCATING) {
|
for (ShardRouting shard : rn) {
|
||||||
continue;
|
assert rn.nodeId().equals(shard.currentNodeId());
|
||||||
}
|
/* we skip relocating shards here since we expect an initializing shard with the same id coming in */
|
||||||
ModelNode node = nodes.get(shard.currentNodeId());
|
if (shard.state() != RELOCATING) {
|
||||||
assert node != null;
|
node.addShard(shard);
|
||||||
node.addShard(shard, Decision.single(Type.YES, "Already allocated on node", node.getNodeId()));
|
if (logger.isTraceEnabled()) {
|
||||||
if (logger.isTraceEnabled()) {
|
logger.trace("Assigned shard [{}] to node [{}]", shard, node.getNodeId());
|
||||||
logger.trace("Assigned shard [{}] to node [{}]", shard, node.getNodeId());
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -612,8 +567,11 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
/**
|
/**
|
||||||
* Allocates all given shards on the minimal eligible node for the shards index
|
* Allocates all given shards on the minimal eligible node for the shards index
|
||||||
* with respect to the weight function. All given shards must be unassigned.
|
* with respect to the weight function. All given shards must be unassigned.
|
||||||
|
* @return <code>true</code> if the current configuration has been
|
||||||
|
* changed, otherwise <code>false</code>
|
||||||
*/
|
*/
|
||||||
private boolean allocateUnassigned(RoutingNodes.UnassignedShards unassigned) {
|
private boolean allocateUnassigned() {
|
||||||
|
RoutingNodes.UnassignedShards unassigned = routingNodes.unassigned();
|
||||||
assert !nodes.isEmpty();
|
assert !nodes.isEmpty();
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Start allocating unassigned shards");
|
logger.trace("Start allocating unassigned shards");
|
||||||
@ -657,7 +615,7 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
int secondaryLength = 0;
|
int secondaryLength = 0;
|
||||||
int primaryLength = primary.length;
|
int primaryLength = primary.length;
|
||||||
ArrayUtil.timSort(primary, comparator);
|
ArrayUtil.timSort(primary, comparator);
|
||||||
final Set<ModelNode> throttledNodes = Collections.newSetFromMap(new IdentityHashMap<ModelNode, Boolean>());
|
final Set<ModelNode> throttledNodes = Collections.newSetFromMap(new IdentityHashMap<>());
|
||||||
do {
|
do {
|
||||||
for (int i = 0; i < primaryLength; i++) {
|
for (int i = 0; i < primaryLength; i++) {
|
||||||
ShardRouting shard = primary[i];
|
ShardRouting shard = primary[i];
|
||||||
@ -695,7 +653,7 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
* don't check deciders
|
* don't check deciders
|
||||||
*/
|
*/
|
||||||
if (currentWeight <= minWeight) {
|
if (currentWeight <= minWeight) {
|
||||||
Decision currentDecision = deciders.canAllocate(shard, node.getRoutingNode(routingNodes), allocation);
|
Decision currentDecision = deciders.canAllocate(shard, node.getRoutingNode(), allocation);
|
||||||
NOUPDATE:
|
NOUPDATE:
|
||||||
if (currentDecision.type() == Type.YES || currentDecision.type() == Type.THROTTLE) {
|
if (currentDecision.type() == Type.YES || currentDecision.type() == Type.THROTTLE) {
|
||||||
if (currentWeight == minWeight) {
|
if (currentWeight == minWeight) {
|
||||||
@ -736,7 +694,7 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
}
|
}
|
||||||
assert decision != null && minNode != null || decision == null && minNode == null;
|
assert decision != null && minNode != null || decision == null && minNode == null;
|
||||||
if (minNode != null) {
|
if (minNode != null) {
|
||||||
minNode.addShard(shard, decision);
|
minNode.addShard(shard);
|
||||||
if (decision.type() == Type.YES) {
|
if (decision.type() == Type.YES) {
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Assigned shard [{}] to [{}]", shard, minNode.getNodeId());
|
logger.trace("Assigned shard [{}] to [{}]", shard, minNode.getNodeId());
|
||||||
@ -745,7 +703,7 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
changed = true;
|
changed = true;
|
||||||
continue; // don't add to ignoreUnassigned
|
continue; // don't add to ignoreUnassigned
|
||||||
} else {
|
} else {
|
||||||
final RoutingNode node = minNode.getRoutingNode(routingNodes);
|
final RoutingNode node = minNode.getRoutingNode();
|
||||||
if (deciders.canAllocate(node, allocation).type() != Type.YES) {
|
if (deciders.canAllocate(node, allocation).type() != Type.YES) {
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Can not allocate on node [{}] remove from round decision [{}]", node, decision.type());
|
logger.trace("Can not allocate on node [{}] remove from round decision [{}]", node, decision.type());
|
||||||
@ -791,10 +749,10 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
}
|
}
|
||||||
ShardRouting candidate = null;
|
ShardRouting candidate = null;
|
||||||
final AllocationDeciders deciders = allocation.deciders();
|
final AllocationDeciders deciders = allocation.deciders();
|
||||||
for (ShardRouting shard : index.getAllShards()) {
|
for (ShardRouting shard : index) {
|
||||||
if (shard.started()) {
|
if (shard.started()) {
|
||||||
// skip initializing, unassigned and relocating shards we can't relocate them anyway
|
// skip initializing, unassigned and relocating shards we can't relocate them anyway
|
||||||
Decision allocationDecision = deciders.canAllocate(shard, minNode.getRoutingNode(routingNodes), allocation);
|
Decision allocationDecision = deciders.canAllocate(shard, minNode.getRoutingNode(), allocation);
|
||||||
Decision rebalanceDecision = deciders.canRebalance(shard, allocation);
|
Decision rebalanceDecision = deciders.canRebalance(shard, allocation);
|
||||||
if (((allocationDecision.type() == Type.YES) || (allocationDecision.type() == Type.THROTTLE))
|
if (((allocationDecision.type() == Type.YES) || (allocationDecision.type() == Type.THROTTLE))
|
||||||
&& ((rebalanceDecision.type() == Type.YES) || (rebalanceDecision.type() == Type.THROTTLE))) {
|
&& ((rebalanceDecision.type() == Type.YES) || (rebalanceDecision.type() == Type.THROTTLE))) {
|
||||||
@ -815,24 +773,17 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (candidate != null) {
|
if (candidate != null) {
|
||||||
|
|
||||||
/* allocate on the model even if not throttled */
|
/* allocate on the model even if not throttled */
|
||||||
maxNode.removeShard(candidate);
|
maxNode.removeShard(candidate);
|
||||||
minNode.addShard(candidate, decision);
|
minNode.addShard(candidate);
|
||||||
if (decision.type() == Type.YES) { /* only allocate on the cluster if we are not throttled */
|
if (decision.type() == Type.YES) { /* only allocate on the cluster if we are not throttled */
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Relocate shard [{}] from node [{}] to node [{}]", candidate, maxNode.getNodeId(),
|
logger.trace("Relocate shard [{}] from node [{}] to node [{}]", candidate, maxNode.getNodeId(),
|
||||||
minNode.getNodeId());
|
minNode.getNodeId());
|
||||||
}
|
}
|
||||||
/* now allocate on the cluster - if we are started we need to relocate the shard */
|
/* now allocate on the cluster */
|
||||||
if (candidate.started()) {
|
routingNodes.relocate(candidate, minNode.getNodeId(), allocation.clusterInfo().getShardSize(candidate, ShardRouting.UNAVAILABLE_EXPECTED_SHARD_SIZE));
|
||||||
routingNodes.relocate(candidate, minNode.getNodeId(), allocation.clusterInfo().getShardSize(candidate, ShardRouting.UNAVAILABLE_EXPECTED_SHARD_SIZE));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
routingNodes.initialize(candidate, minNode.getNodeId(), null, allocation.clusterInfo().getShardSize(candidate, ShardRouting.UNAVAILABLE_EXPECTED_SHARD_SIZE));
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -846,14 +797,12 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
}
|
}
|
||||||
|
|
||||||
static class ModelNode implements Iterable<ModelIndex> {
|
static class ModelNode implements Iterable<ModelIndex> {
|
||||||
private final String id;
|
|
||||||
private final Map<String, ModelIndex> indices = new HashMap<>();
|
private final Map<String, ModelIndex> indices = new HashMap<>();
|
||||||
private int numShards = 0;
|
private int numShards = 0;
|
||||||
// lazily calculated
|
private final RoutingNode routingNode;
|
||||||
private RoutingNode routingNode;
|
|
||||||
|
|
||||||
public ModelNode(String id) {
|
public ModelNode(RoutingNode routingNode) {
|
||||||
this.id = id;
|
this.routingNode = routingNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ModelIndex getIndex(String indexId) {
|
public ModelIndex getIndex(String indexId) {
|
||||||
@ -861,13 +810,10 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getNodeId() {
|
public String getNodeId() {
|
||||||
return id;
|
return routingNode.nodeId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public RoutingNode getRoutingNode(RoutingNodes routingNodes) {
|
public RoutingNode getRoutingNode() {
|
||||||
if (routingNode == null) {
|
|
||||||
routingNode = routingNodes.node(id);
|
|
||||||
}
|
|
||||||
return routingNode;
|
return routingNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -888,33 +834,31 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addShard(ShardRouting shard, Decision decision) {
|
public void addShard(ShardRouting shard) {
|
||||||
ModelIndex index = indices.get(shard.getIndexName());
|
ModelIndex index = indices.get(shard.getIndexName());
|
||||||
if (index == null) {
|
if (index == null) {
|
||||||
index = new ModelIndex(shard.getIndexName());
|
index = new ModelIndex(shard.getIndexName());
|
||||||
indices.put(index.getIndexId(), index);
|
indices.put(index.getIndexId(), index);
|
||||||
}
|
}
|
||||||
index.addShard(shard, decision);
|
index.addShard(shard);
|
||||||
numShards++;
|
numShards++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Decision removeShard(ShardRouting shard) {
|
public void removeShard(ShardRouting shard) {
|
||||||
ModelIndex index = indices.get(shard.getIndexName());
|
ModelIndex index = indices.get(shard.getIndexName());
|
||||||
Decision removed = null;
|
|
||||||
if (index != null) {
|
if (index != null) {
|
||||||
removed = index.removeShard(shard);
|
index.removeShard(shard);
|
||||||
if (removed != null && index.numShards() == 0) {
|
if (index.numShards() == 0) {
|
||||||
indices.remove(shard.getIndexName());
|
indices.remove(shard.getIndexName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
numShards--;
|
numShards--;
|
||||||
return removed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Node(").append(id).append(")");
|
sb.append("Node(").append(routingNode.nodeId()).append(")");
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -930,9 +874,9 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class ModelIndex {
|
static final class ModelIndex implements Iterable<ShardRouting> {
|
||||||
private final String id;
|
private final String id;
|
||||||
private final Map<ShardRouting, Decision> shards = new HashMap<>();
|
private final Set<ShardRouting> shards = new HashSet<>(4); // expect few shards of same index to be allocated on same node
|
||||||
private int highestPrimary = -1;
|
private int highestPrimary = -1;
|
||||||
|
|
||||||
public ModelIndex(String id) {
|
public ModelIndex(String id) {
|
||||||
@ -942,7 +886,7 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
public int highestPrimary() {
|
public int highestPrimary() {
|
||||||
if (highestPrimary == -1) {
|
if (highestPrimary == -1) {
|
||||||
int maxId = -1;
|
int maxId = -1;
|
||||||
for (ShardRouting shard : shards.keySet()) {
|
for (ShardRouting shard : shards) {
|
||||||
if (shard.primary()) {
|
if (shard.primary()) {
|
||||||
maxId = Math.max(maxId, shard.id());
|
maxId = Math.max(maxId, shard.id());
|
||||||
}
|
}
|
||||||
@ -960,24 +904,25 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
|
|||||||
return shards.size();
|
return shards.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<ShardRouting> getAllShards() {
|
@Override
|
||||||
return shards.keySet();
|
public Iterator<ShardRouting> iterator() {
|
||||||
|
return shards.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Decision removeShard(ShardRouting shard) {
|
public void removeShard(ShardRouting shard) {
|
||||||
highestPrimary = -1;
|
highestPrimary = -1;
|
||||||
return shards.remove(shard);
|
assert shards.contains(shard) : "Shard not allocated on current node: " + shard;
|
||||||
|
shards.remove(shard);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addShard(ShardRouting shard, Decision decision) {
|
public void addShard(ShardRouting shard) {
|
||||||
highestPrimary = -1;
|
highestPrimary = -1;
|
||||||
assert decision != null;
|
assert !shards.contains(shard) : "Shard already allocated on current node: " + shard;
|
||||||
assert !shards.containsKey(shard) : "Shard already allocated on current node: " + shards.get(shard) + " " + shard;
|
shards.add(shard);
|
||||||
shards.put(shard, decision);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean containsShard(ShardRouting shard) {
|
public boolean containsShard(ShardRouting shard) {
|
||||||
return shards.containsKey(shard);
|
return shards.contains(shard);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,56 +19,25 @@
|
|||||||
|
|
||||||
package org.elasticsearch.cluster.routing.allocation.allocator;
|
package org.elasticsearch.cluster.routing.allocation.allocator;
|
||||||
|
|
||||||
import org.elasticsearch.cluster.routing.ShardRouting;
|
|
||||||
import org.elasticsearch.cluster.routing.ShardRoutingState;
|
|
||||||
import org.elasticsearch.cluster.routing.allocation.FailedRerouteAllocation;
|
|
||||||
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
|
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
|
||||||
import org.elasticsearch.cluster.routing.allocation.StartedRerouteAllocation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* A {@link ShardsAllocator} is the main entry point for shard allocation on nodes in the cluster.
|
* A {@link ShardsAllocator} is the main entry point for shard allocation on nodes in the cluster.
|
||||||
* The allocator makes basic decision where a shard instance will be allocated, if already allocated instances
|
* The allocator makes basic decision where a shard instance will be allocated, if already allocated instances
|
||||||
* need relocate to other nodes due to node failures or due to rebalancing decisions.
|
* need to relocate to other nodes due to node failures or due to rebalancing decisions.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public interface ShardsAllocator {
|
public interface ShardsAllocator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies changes on started nodes based on the implemented algorithm. For example if a
|
* Allocates shards to nodes in the cluster. An implementation of this method should:
|
||||||
* shard has changed to {@link ShardRoutingState#STARTED} from {@link ShardRoutingState#RELOCATING}
|
* - assign unassigned shards
|
||||||
* this allocator might apply some cleanups on the node that used to hold the shard.
|
* - relocate shards that cannot stay on a node anymore
|
||||||
* @param allocation all started {@link ShardRouting shards}
|
* - relocate shards to find a good shard balance in the cluster
|
||||||
*/
|
|
||||||
void applyStartedShards(StartedRerouteAllocation allocation);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies changes on failed nodes based on the implemented algorithm.
|
|
||||||
* @param allocation all failed {@link ShardRouting shards}
|
|
||||||
*/
|
|
||||||
void applyFailedShards(FailedRerouteAllocation allocation);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assign all unassigned shards to nodes
|
|
||||||
*
|
*
|
||||||
* @param allocation current node allocation
|
* @param allocation current node allocation
|
||||||
* @return <code>true</code> if the allocation has changed, otherwise <code>false</code>
|
* @return <code>true</code> if the allocation has changed, otherwise <code>false</code>
|
||||||
*/
|
*/
|
||||||
boolean allocateUnassigned(RoutingAllocation allocation);
|
boolean allocate(RoutingAllocation allocation);
|
||||||
|
|
||||||
/**
|
|
||||||
* Rebalancing number of shards on all nodes
|
|
||||||
*
|
|
||||||
* @param allocation current node allocation
|
|
||||||
* @return <code>true</code> if the allocation has changed, otherwise <code>false</code>
|
|
||||||
*/
|
|
||||||
boolean rebalance(RoutingAllocation allocation);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Move started shards that can not be allocated to a node anymore
|
|
||||||
*
|
|
||||||
* @param allocation current node allocation
|
|
||||||
* @return <code>true</code> if the allocation has changed, otherwise <code>false</code>
|
|
||||||
*/
|
|
||||||
boolean moveShards(RoutingAllocation allocation);
|
|
||||||
}
|
}
|
||||||
|
@ -1,100 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.elasticsearch.cluster.routing.allocation.allocator;
|
|
||||||
|
|
||||||
import org.elasticsearch.cluster.routing.allocation.FailedRerouteAllocation;
|
|
||||||
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
|
|
||||||
import org.elasticsearch.cluster.routing.allocation.StartedRerouteAllocation;
|
|
||||||
import org.elasticsearch.common.component.AbstractComponent;
|
|
||||||
import org.elasticsearch.common.inject.Inject;
|
|
||||||
import org.elasticsearch.common.settings.Settings;
|
|
||||||
import org.elasticsearch.gateway.GatewayAllocator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The {@link ShardsAllocator} class offers methods for allocating shard within a cluster.
|
|
||||||
* These methods include moving shards and re-balancing the cluster. It also allows management
|
|
||||||
* of shards by their state.
|
|
||||||
*/
|
|
||||||
public class ShardsAllocators extends AbstractComponent implements ShardsAllocator {
|
|
||||||
|
|
||||||
private final GatewayAllocator gatewayAllocator;
|
|
||||||
private final ShardsAllocator allocator;
|
|
||||||
|
|
||||||
public ShardsAllocators(GatewayAllocator allocator) {
|
|
||||||
this(Settings.Builder.EMPTY_SETTINGS, allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShardsAllocators(Settings settings, GatewayAllocator allocator) {
|
|
||||||
this(settings, allocator, new BalancedShardsAllocator(settings));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public ShardsAllocators(Settings settings, GatewayAllocator gatewayAllocator, ShardsAllocator allocator) {
|
|
||||||
super(settings);
|
|
||||||
this.gatewayAllocator = gatewayAllocator;
|
|
||||||
this.allocator = allocator;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void applyStartedShards(StartedRerouteAllocation allocation) {
|
|
||||||
gatewayAllocator.applyStartedShards(allocation);
|
|
||||||
allocator.applyStartedShards(allocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void applyFailedShards(FailedRerouteAllocation allocation) {
|
|
||||||
gatewayAllocator.applyFailedShards(allocation);
|
|
||||||
allocator.applyFailedShards(allocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean allocateUnassigned(RoutingAllocation allocation) {
|
|
||||||
boolean changed = false;
|
|
||||||
changed |= gatewayAllocator.allocateUnassigned(allocation);
|
|
||||||
changed |= allocator.allocateUnassigned(allocation);
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected long nanoTime() {
|
|
||||||
return System.nanoTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean rebalance(RoutingAllocation allocation) {
|
|
||||||
if (allocation.hasPendingAsyncFetch() == false) {
|
|
||||||
/*
|
|
||||||
* see https://github.com/elastic/elasticsearch/issues/14387
|
|
||||||
* if we allow rebalance operations while we are still fetching shard store data
|
|
||||||
* we might end up with unnecessary rebalance operations which can be super confusion/frustrating
|
|
||||||
* since once the fetches come back we might just move all the shards back again.
|
|
||||||
* Therefore we only do a rebalance if we have fetched all information.
|
|
||||||
*/
|
|
||||||
return allocator.rebalance(allocation);
|
|
||||||
} else {
|
|
||||||
logger.debug("skipping rebalance due to in-flight shard/store fetches");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean moveShards(RoutingAllocation allocation) {
|
|
||||||
return allocator.moveShards(allocation);
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package org.elasticsearch.cluster.service;
|
package org.elasticsearch.cluster.service;
|
||||||
|
|
||||||
import org.elasticsearch.Version;
|
|
||||||
import org.elasticsearch.cluster.AckedClusterStateTaskListener;
|
import org.elasticsearch.cluster.AckedClusterStateTaskListener;
|
||||||
import org.elasticsearch.cluster.ClusterChangedEvent;
|
import org.elasticsearch.cluster.ClusterChangedEvent;
|
||||||
import org.elasticsearch.cluster.ClusterName;
|
import org.elasticsearch.cluster.ClusterName;
|
||||||
@ -32,19 +31,18 @@ import org.elasticsearch.cluster.ClusterStateTaskExecutor;
|
|||||||
import org.elasticsearch.cluster.ClusterStateTaskListener;
|
import org.elasticsearch.cluster.ClusterStateTaskListener;
|
||||||
import org.elasticsearch.cluster.ClusterStateUpdateTask;
|
import org.elasticsearch.cluster.ClusterStateUpdateTask;
|
||||||
import org.elasticsearch.cluster.LocalNodeMasterListener;
|
import org.elasticsearch.cluster.LocalNodeMasterListener;
|
||||||
|
import org.elasticsearch.cluster.NodeConnectionsService;
|
||||||
import org.elasticsearch.cluster.TimeoutClusterStateListener;
|
import org.elasticsearch.cluster.TimeoutClusterStateListener;
|
||||||
import org.elasticsearch.cluster.block.ClusterBlock;
|
import org.elasticsearch.cluster.block.ClusterBlock;
|
||||||
import org.elasticsearch.cluster.block.ClusterBlocks;
|
import org.elasticsearch.cluster.block.ClusterBlocks;
|
||||||
import org.elasticsearch.cluster.metadata.MetaData;
|
import org.elasticsearch.cluster.metadata.MetaData;
|
||||||
import org.elasticsearch.cluster.metadata.ProcessClusterEventTimeoutException;
|
import org.elasticsearch.cluster.metadata.ProcessClusterEventTimeoutException;
|
||||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||||
import org.elasticsearch.cluster.node.DiscoveryNodeService;
|
|
||||||
import org.elasticsearch.cluster.node.DiscoveryNodes;
|
import org.elasticsearch.cluster.node.DiscoveryNodes;
|
||||||
import org.elasticsearch.cluster.routing.OperationRouting;
|
import org.elasticsearch.cluster.routing.OperationRouting;
|
||||||
import org.elasticsearch.cluster.routing.RoutingTable;
|
import org.elasticsearch.cluster.routing.RoutingTable;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.Priority;
|
import org.elasticsearch.common.Priority;
|
||||||
import org.elasticsearch.common.Randomness;
|
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
@ -54,7 +52,6 @@ import org.elasticsearch.common.settings.ClusterSettings;
|
|||||||
import org.elasticsearch.common.settings.Setting;
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.text.Text;
|
import org.elasticsearch.common.text.Text;
|
||||||
import org.elasticsearch.common.transport.TransportAddress;
|
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
|
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
|
||||||
import org.elasticsearch.common.util.concurrent.CountDown;
|
import org.elasticsearch.common.util.concurrent.CountDown;
|
||||||
@ -65,9 +62,7 @@ import org.elasticsearch.common.util.concurrent.PrioritizedEsThreadPoolExecutor;
|
|||||||
import org.elasticsearch.common.util.concurrent.PrioritizedRunnable;
|
import org.elasticsearch.common.util.concurrent.PrioritizedRunnable;
|
||||||
import org.elasticsearch.common.util.iterable.Iterables;
|
import org.elasticsearch.common.util.iterable.Iterables;
|
||||||
import org.elasticsearch.discovery.Discovery;
|
import org.elasticsearch.discovery.Discovery;
|
||||||
import org.elasticsearch.tasks.TaskManager;
|
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
import org.elasticsearch.transport.TransportService;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -78,8 +73,6 @@ import java.util.Locale;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.Random;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
@ -97,25 +90,15 @@ import static org.elasticsearch.common.util.concurrent.EsExecutors.daemonThreadF
|
|||||||
public class InternalClusterService extends AbstractLifecycleComponent<ClusterService> implements ClusterService {
|
public class InternalClusterService extends AbstractLifecycleComponent<ClusterService> implements ClusterService {
|
||||||
|
|
||||||
public static final Setting<TimeValue> CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING = Setting.positiveTimeSetting("cluster.service.slow_task_logging_threshold", TimeValue.timeValueSeconds(30), true, Setting.Scope.CLUSTER);
|
public static final Setting<TimeValue> CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING = Setting.positiveTimeSetting("cluster.service.slow_task_logging_threshold", TimeValue.timeValueSeconds(30), true, Setting.Scope.CLUSTER);
|
||||||
public static final Setting<TimeValue> CLUSTER_SERVICE_RECONNECT_INTERVAL_SETTING = Setting.positiveTimeSetting("cluster.service.reconnect_interval", TimeValue.timeValueSeconds(10), false, Setting.Scope.CLUSTER);
|
|
||||||
|
|
||||||
public static final String UPDATE_THREAD_NAME = "clusterService#updateTask";
|
public static final String UPDATE_THREAD_NAME = "clusterService#updateTask";
|
||||||
public static final Setting<Long> NODE_ID_SEED_SETTING =
|
|
||||||
// don't use node.id.seed so it won't be seen as an attribute
|
|
||||||
Setting.longSetting("node_id.seed", 0L, Long.MIN_VALUE, false, Setting.Scope.CLUSTER);
|
|
||||||
private final ThreadPool threadPool;
|
private final ThreadPool threadPool;
|
||||||
|
|
||||||
private BiConsumer<ClusterChangedEvent, Discovery.AckListener> clusterStatePublisher;
|
private BiConsumer<ClusterChangedEvent, Discovery.AckListener> clusterStatePublisher;
|
||||||
|
|
||||||
private final OperationRouting operationRouting;
|
private final OperationRouting operationRouting;
|
||||||
|
|
||||||
private final TransportService transportService;
|
|
||||||
|
|
||||||
private final ClusterSettings clusterSettings;
|
private final ClusterSettings clusterSettings;
|
||||||
private final DiscoveryNodeService discoveryNodeService;
|
|
||||||
private final Version version;
|
|
||||||
|
|
||||||
private final TimeValue reconnectInterval;
|
|
||||||
|
|
||||||
private TimeValue slowTaskLoggingThreshold;
|
private TimeValue slowTaskLoggingThreshold;
|
||||||
|
|
||||||
@ -140,47 +123,49 @@ public class InternalClusterService extends AbstractLifecycleComponent<ClusterSe
|
|||||||
|
|
||||||
private final ClusterBlocks.Builder initialBlocks;
|
private final ClusterBlocks.Builder initialBlocks;
|
||||||
|
|
||||||
private final TaskManager taskManager;
|
private NodeConnectionsService nodeConnectionsService;
|
||||||
|
|
||||||
private volatile ScheduledFuture reconnectToNodes;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public InternalClusterService(Settings settings, OperationRouting operationRouting, TransportService transportService,
|
public InternalClusterService(Settings settings, OperationRouting operationRouting,
|
||||||
ClusterSettings clusterSettings, ThreadPool threadPool, ClusterName clusterName, DiscoveryNodeService discoveryNodeService, Version version) {
|
ClusterSettings clusterSettings, ThreadPool threadPool, ClusterName clusterName) {
|
||||||
super(settings);
|
super(settings);
|
||||||
this.operationRouting = operationRouting;
|
this.operationRouting = operationRouting;
|
||||||
this.transportService = transportService;
|
|
||||||
this.threadPool = threadPool;
|
this.threadPool = threadPool;
|
||||||
this.clusterSettings = clusterSettings;
|
this.clusterSettings = clusterSettings;
|
||||||
this.discoveryNodeService = discoveryNodeService;
|
|
||||||
this.version = version;
|
|
||||||
|
|
||||||
// will be replaced on doStart.
|
// will be replaced on doStart.
|
||||||
this.clusterState = ClusterState.builder(clusterName).build();
|
this.clusterState = ClusterState.builder(clusterName).build();
|
||||||
|
|
||||||
this.clusterSettings.addSettingsUpdateConsumer(CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING, this::setSlowTaskLoggingThreshold);
|
this.clusterSettings.addSettingsUpdateConsumer(CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING, this::setSlowTaskLoggingThreshold);
|
||||||
|
|
||||||
this.reconnectInterval = CLUSTER_SERVICE_RECONNECT_INTERVAL_SETTING.get(settings);
|
|
||||||
|
|
||||||
this.slowTaskLoggingThreshold = CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING.get(settings);
|
this.slowTaskLoggingThreshold = CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING.get(settings);
|
||||||
|
|
||||||
localNodeMasterListeners = new LocalNodeMasterListeners(threadPool);
|
localNodeMasterListeners = new LocalNodeMasterListeners(threadPool);
|
||||||
|
|
||||||
initialBlocks = ClusterBlocks.builder();
|
initialBlocks = ClusterBlocks.builder();
|
||||||
|
|
||||||
taskManager = transportService.getTaskManager();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setSlowTaskLoggingThreshold(TimeValue slowTaskLoggingThreshold) {
|
private void setSlowTaskLoggingThreshold(TimeValue slowTaskLoggingThreshold) {
|
||||||
this.slowTaskLoggingThreshold = slowTaskLoggingThreshold;
|
this.slowTaskLoggingThreshold = slowTaskLoggingThreshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setClusterStatePublisher(BiConsumer<ClusterChangedEvent, Discovery.AckListener> publisher) {
|
synchronized public void setClusterStatePublisher(BiConsumer<ClusterChangedEvent, Discovery.AckListener> publisher) {
|
||||||
clusterStatePublisher = publisher;
|
clusterStatePublisher = publisher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
synchronized public void setLocalNode(DiscoveryNode localNode) {
|
||||||
|
assert clusterState.nodes().localNodeId() == null : "local node is already set";
|
||||||
|
DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()).put(localNode).localNodeId(localNode.id());
|
||||||
|
this.clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized public void setNodeConnectionsService(NodeConnectionsService nodeConnectionsService) {
|
||||||
|
assert this.nodeConnectionsService == null : "nodeConnectionsService is already set";
|
||||||
|
this.nodeConnectionsService = nodeConnectionsService;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addInitialStateBlock(ClusterBlock block) throws IllegalStateException {
|
synchronized public void addInitialStateBlock(ClusterBlock block) throws IllegalStateException {
|
||||||
if (lifecycle.started()) {
|
if (lifecycle.started()) {
|
||||||
throw new IllegalStateException("can't set initial block when started");
|
throw new IllegalStateException("can't set initial block when started");
|
||||||
}
|
}
|
||||||
@ -188,12 +173,12 @@ public class InternalClusterService extends AbstractLifecycleComponent<ClusterSe
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeInitialStateBlock(ClusterBlock block) throws IllegalStateException {
|
synchronized public void removeInitialStateBlock(ClusterBlock block) throws IllegalStateException {
|
||||||
removeInitialStateBlock(block.id());
|
removeInitialStateBlock(block.id());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeInitialStateBlock(int blockId) throws IllegalStateException {
|
synchronized public void removeInitialStateBlock(int blockId) throws IllegalStateException {
|
||||||
if (lifecycle.started()) {
|
if (lifecycle.started()) {
|
||||||
throw new IllegalStateException("can't set initial block when started");
|
throw new IllegalStateException("can't set initial block when started");
|
||||||
}
|
}
|
||||||
@ -201,26 +186,18 @@ public class InternalClusterService extends AbstractLifecycleComponent<ClusterSe
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doStart() {
|
synchronized protected void doStart() {
|
||||||
Objects.requireNonNull(clusterStatePublisher, "please set a cluster state publisher before starting");
|
Objects.requireNonNull(clusterStatePublisher, "please set a cluster state publisher before starting");
|
||||||
|
Objects.requireNonNull(clusterState.nodes().localNode(), "please set the local node before starting");
|
||||||
|
Objects.requireNonNull(nodeConnectionsService, "please set the node connection service before starting");
|
||||||
add(localNodeMasterListeners);
|
add(localNodeMasterListeners);
|
||||||
add(taskManager);
|
|
||||||
this.clusterState = ClusterState.builder(clusterState).blocks(initialBlocks).build();
|
this.clusterState = ClusterState.builder(clusterState).blocks(initialBlocks).build();
|
||||||
this.updateTasksExecutor = EsExecutors.newSinglePrioritizing(UPDATE_THREAD_NAME, daemonThreadFactory(settings, UPDATE_THREAD_NAME), threadPool.getThreadContext());
|
this.updateTasksExecutor = EsExecutors.newSinglePrioritizing(UPDATE_THREAD_NAME, daemonThreadFactory(settings, UPDATE_THREAD_NAME), threadPool.getThreadContext());
|
||||||
this.reconnectToNodes = threadPool.schedule(reconnectInterval, ThreadPool.Names.GENERIC, new ReconnectToNodes());
|
this.clusterState = ClusterState.builder(clusterState).blocks(initialBlocks).build();
|
||||||
Map<String, String> nodeAttributes = discoveryNodeService.buildAttributes();
|
|
||||||
// note, we rely on the fact that its a new id each time we start, see FD and "kill -9" handling
|
|
||||||
final String nodeId = generateNodeId(settings);
|
|
||||||
final TransportAddress publishAddress = transportService.boundAddress().publishAddress();
|
|
||||||
DiscoveryNode localNode = new DiscoveryNode(settings.get("node.name"), nodeId, publishAddress, nodeAttributes, version);
|
|
||||||
DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder().put(localNode).localNodeId(localNode.id());
|
|
||||||
this.clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).blocks(initialBlocks).build();
|
|
||||||
this.transportService.setLocalNode(localNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doStop() {
|
synchronized protected void doStop() {
|
||||||
FutureUtils.cancel(this.reconnectToNodes);
|
|
||||||
for (NotifyTimeout onGoingTimeout : onGoingTimeouts) {
|
for (NotifyTimeout onGoingTimeout : onGoingTimeouts) {
|
||||||
onGoingTimeout.cancel();
|
onGoingTimeout.cancel();
|
||||||
onGoingTimeout.listener.onClose();
|
onGoingTimeout.listener.onClose();
|
||||||
@ -230,7 +207,7 @@ public class InternalClusterService extends AbstractLifecycleComponent<ClusterSe
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doClose() {
|
synchronized protected void doClose() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -400,11 +377,6 @@ public class InternalClusterService extends AbstractLifecycleComponent<ClusterSe
|
|||||||
return updateTasksExecutor.getMaxTaskWaitTime();
|
return updateTasksExecutor.getMaxTaskWaitTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public TaskManager getTaskManager() {
|
|
||||||
return taskManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** asserts that the current thread is the cluster state update thread */
|
/** asserts that the current thread is the cluster state update thread */
|
||||||
public boolean assertClusterStateThread() {
|
public boolean assertClusterStateThread() {
|
||||||
assert Thread.currentThread().getName().contains(InternalClusterService.UPDATE_THREAD_NAME) : "not called from the cluster state update thread";
|
assert Thread.currentThread().getName().contains(InternalClusterService.UPDATE_THREAD_NAME) : "not called from the cluster state update thread";
|
||||||
@ -457,14 +429,14 @@ public class InternalClusterService extends AbstractLifecycleComponent<ClusterSe
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ClusterStateTaskExecutor.BatchResult<T> batchResult;
|
ClusterStateTaskExecutor.BatchResult<T> batchResult;
|
||||||
long startTimeNS = System.nanoTime();
|
long startTimeNS = currentTimeInNanos();
|
||||||
try {
|
try {
|
||||||
List<T> inputs = toExecute.stream().map(tUpdateTask -> tUpdateTask.task).collect(Collectors.toList());
|
List<T> inputs = toExecute.stream().map(tUpdateTask -> tUpdateTask.task).collect(Collectors.toList());
|
||||||
batchResult = executor.execute(previousClusterState, inputs);
|
batchResult = executor.execute(previousClusterState, inputs);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(System.nanoTime() - startTimeNS)));
|
TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(currentTimeInNanos() - startTimeNS)));
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
StringBuilder sb = new StringBuilder("failed to execute cluster state update in ").append(executionTime).append(", state:\nversion [").append(previousClusterState.version()).append("], source [").append(source).append("]\n");
|
StringBuilder sb = new StringBuilder("failed to execute cluster state update in [").append(executionTime).append("], state:\nversion [").append(previousClusterState.version()).append("], source [").append(source).append("]\n");
|
||||||
sb.append(previousClusterState.nodes().prettyPrint());
|
sb.append(previousClusterState.nodes().prettyPrint());
|
||||||
sb.append(previousClusterState.routingTable().prettyPrint());
|
sb.append(previousClusterState.routingTable().prettyPrint());
|
||||||
sb.append(previousClusterState.getRoutingNodes().prettyPrint());
|
sb.append(previousClusterState.getRoutingNodes().prettyPrint());
|
||||||
@ -509,8 +481,8 @@ public class InternalClusterService extends AbstractLifecycleComponent<ClusterSe
|
|||||||
}
|
}
|
||||||
task.listener.clusterStateProcessed(task.source, previousClusterState, newClusterState);
|
task.listener.clusterStateProcessed(task.source, previousClusterState, newClusterState);
|
||||||
}
|
}
|
||||||
TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(System.nanoTime() - startTimeNS)));
|
TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(currentTimeInNanos() - startTimeNS)));
|
||||||
logger.debug("processing [{}]: took {} no change in cluster_state", source, executionTime);
|
logger.debug("processing [{}]: took [{}] no change in cluster_state", source, executionTime);
|
||||||
warnAboutSlowTaskIfNeeded(executionTime, source);
|
warnAboutSlowTaskIfNeeded(executionTime, source);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -568,15 +540,7 @@ public class InternalClusterService extends AbstractLifecycleComponent<ClusterSe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO, do this in parallel (and wait)
|
nodeConnectionsService.connectToAddedNodes(clusterChangedEvent);
|
||||||
for (DiscoveryNode node : nodesDelta.addedNodes()) {
|
|
||||||
try {
|
|
||||||
transportService.connectToNode(node);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
// the fault detection will detect it as failed as well
|
|
||||||
logger.warn("failed to connect to node [" + node + "]", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we are the master, publish the new state to all nodes
|
// if we are the master, publish the new state to all nodes
|
||||||
// we publish here before we send a notification to all the listeners, since if it fails
|
// we publish here before we send a notification to all the listeners, since if it fails
|
||||||
@ -612,13 +576,7 @@ public class InternalClusterService extends AbstractLifecycleComponent<ClusterSe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (DiscoveryNode node : nodesDelta.removedNodes()) {
|
nodeConnectionsService.disconnectFromRemovedNodes(clusterChangedEvent);
|
||||||
try {
|
|
||||||
transportService.disconnectFromNode(node);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
logger.warn("failed to disconnect to node [" + node + "]", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newClusterState.status(ClusterState.ClusterStateStatus.APPLIED);
|
newClusterState.status(ClusterState.ClusterStateStatus.APPLIED);
|
||||||
|
|
||||||
@ -649,11 +607,11 @@ public class InternalClusterService extends AbstractLifecycleComponent<ClusterSe
|
|||||||
logger.error("exception thrown while notifying executor of new cluster state publication [{}]", e, source);
|
logger.error("exception thrown while notifying executor of new cluster state publication [{}]", e, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(System.nanoTime() - startTimeNS)));
|
TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(currentTimeInNanos() - startTimeNS)));
|
||||||
logger.debug("processing [{}]: took {} done applying updated cluster_state (version: {}, uuid: {})", source, executionTime, newClusterState.version(), newClusterState.stateUUID());
|
logger.debug("processing [{}]: took [{}] done applying updated cluster_state (version: {}, uuid: {})", source, executionTime, newClusterState.version(), newClusterState.stateUUID());
|
||||||
warnAboutSlowTaskIfNeeded(executionTime, source);
|
warnAboutSlowTaskIfNeeded(executionTime, source);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(System.nanoTime() - startTimeNS)));
|
TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(currentTimeInNanos() - startTimeNS)));
|
||||||
StringBuilder sb = new StringBuilder("failed to apply updated cluster state in ").append(executionTime).append(":\nversion [").append(newClusterState.version()).append("], uuid [").append(newClusterState.stateUUID()).append("], source [").append(source).append("]\n");
|
StringBuilder sb = new StringBuilder("failed to apply updated cluster state in ").append(executionTime).append(":\nversion [").append(newClusterState.version()).append("], uuid [").append(newClusterState.stateUUID()).append("], source [").append(source).append("]\n");
|
||||||
sb.append(newClusterState.nodes().prettyPrint());
|
sb.append(newClusterState.nodes().prettyPrint());
|
||||||
sb.append(newClusterState.routingTable().prettyPrint());
|
sb.append(newClusterState.routingTable().prettyPrint());
|
||||||
@ -664,6 +622,9 @@ public class InternalClusterService extends AbstractLifecycleComponent<ClusterSe
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this one is overridden in tests so we can control time
|
||||||
|
protected long currentTimeInNanos() {return System.nanoTime();}
|
||||||
|
|
||||||
private static SafeClusterStateTaskListener safe(ClusterStateTaskListener listener, ESLogger logger) {
|
private static SafeClusterStateTaskListener safe(ClusterStateTaskListener listener, ESLogger logger) {
|
||||||
if (listener instanceof AckedClusterStateTaskListener) {
|
if (listener instanceof AckedClusterStateTaskListener) {
|
||||||
return new SafeAckedClusterStateTaskListener((AckedClusterStateTaskListener) listener, logger);
|
return new SafeAckedClusterStateTaskListener((AckedClusterStateTaskListener) listener, logger);
|
||||||
@ -777,7 +738,7 @@ public class InternalClusterService extends AbstractLifecycleComponent<ClusterSe
|
|||||||
|
|
||||||
private void warnAboutSlowTaskIfNeeded(TimeValue executionTime, String source) {
|
private void warnAboutSlowTaskIfNeeded(TimeValue executionTime, String source) {
|
||||||
if (executionTime.getMillis() > slowTaskLoggingThreshold.getMillis()) {
|
if (executionTime.getMillis() > slowTaskLoggingThreshold.getMillis()) {
|
||||||
logger.warn("cluster state update task [{}] took {} above the warn threshold of {}", source, executionTime, slowTaskLoggingThreshold);
|
logger.warn("cluster state update task [{}] took [{}] above the warn threshold of {}", source, executionTime, slowTaskLoggingThreshold);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -809,64 +770,6 @@ public class InternalClusterService extends AbstractLifecycleComponent<ClusterSe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ReconnectToNodes implements Runnable {
|
|
||||||
|
|
||||||
private ConcurrentMap<DiscoveryNode, Integer> failureCount = ConcurrentCollections.newConcurrentMap();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// master node will check against all nodes if its alive with certain discoveries implementations,
|
|
||||||
// but we can't rely on that, so we check on it as well
|
|
||||||
for (DiscoveryNode node : clusterState.nodes()) {
|
|
||||||
if (lifecycle.stoppedOrClosed()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (clusterState.nodes().nodeExists(node.id())) { // we double check existence of node since connectToNode might take time...
|
|
||||||
if (!transportService.nodeConnected(node)) {
|
|
||||||
try {
|
|
||||||
transportService.connectToNode(node);
|
|
||||||
} catch (Exception e) {
|
|
||||||
if (lifecycle.stoppedOrClosed()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (clusterState.nodes().nodeExists(node.id())) { // double check here as well, maybe its gone?
|
|
||||||
Integer nodeFailureCount = failureCount.get(node);
|
|
||||||
if (nodeFailureCount == null) {
|
|
||||||
nodeFailureCount = 1;
|
|
||||||
} else {
|
|
||||||
nodeFailureCount = nodeFailureCount + 1;
|
|
||||||
}
|
|
||||||
// log every 6th failure
|
|
||||||
if ((nodeFailureCount % 6) == 0) {
|
|
||||||
// reset the failure count...
|
|
||||||
nodeFailureCount = 0;
|
|
||||||
logger.warn("failed to reconnect to node {}", e, node);
|
|
||||||
}
|
|
||||||
failureCount.put(node, nodeFailureCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// go over and remove failed nodes that have been removed
|
|
||||||
DiscoveryNodes nodes = clusterState.nodes();
|
|
||||||
for (Iterator<DiscoveryNode> failedNodesIt = failureCount.keySet().iterator(); failedNodesIt.hasNext(); ) {
|
|
||||||
DiscoveryNode failedNode = failedNodesIt.next();
|
|
||||||
if (!nodes.nodeExists(failedNode.id())) {
|
|
||||||
failedNodesIt.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (lifecycle.started()) {
|
|
||||||
reconnectToNodes = threadPool.schedule(reconnectInterval, ThreadPool.Names.GENERIC, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String generateNodeId(Settings settings) {
|
|
||||||
Random random = Randomness.get(settings, NODE_ID_SEED_SETTING);
|
|
||||||
return Strings.randomBase64UUID(random);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class LocalNodeMasterListeners implements ClusterStateListener {
|
private static class LocalNodeMasterListeners implements ClusterStateListener {
|
||||||
|
|
||||||
private final List<LocalNodeMasterListener> listeners = new CopyOnWriteArrayList<>();
|
private final List<LocalNodeMasterListener> listeners = new CopyOnWriteArrayList<>();
|
||||||
|
@ -37,6 +37,7 @@ import org.elasticsearch.common.geo.builders.ShapeBuilder;
|
|||||||
import org.elasticsearch.common.text.Text;
|
import org.elasticsearch.common.text.Text;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
|
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
|
||||||
|
import org.elasticsearch.ingest.IngestStats;
|
||||||
import org.elasticsearch.search.rescore.RescoreBuilder;
|
import org.elasticsearch.search.rescore.RescoreBuilder;
|
||||||
import org.elasticsearch.search.suggest.SuggestionBuilder;
|
import org.elasticsearch.search.suggest.SuggestionBuilder;
|
||||||
import org.elasticsearch.search.suggest.completion.context.QueryContext;
|
import org.elasticsearch.search.suggest.completion.context.QueryContext;
|
||||||
@ -563,6 +564,14 @@ public abstract class StreamInput extends InputStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T extends Writeable> T readOptionalWritable(T prototype) throws IOException {
|
||||||
|
if (readBoolean()) {
|
||||||
|
return (T) prototype.readFrom(this);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public <T extends Throwable> T readThrowable() throws IOException {
|
public <T extends Throwable> T readThrowable() throws IOException {
|
||||||
if (readBoolean()) {
|
if (readBoolean()) {
|
||||||
int key = readVInt();
|
int key = readVInt();
|
||||||
|
@ -532,6 +532,15 @@ public abstract class StreamOutput extends OutputStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void writeOptionalWriteable(@Nullable Writeable writeable) throws IOException {
|
||||||
|
if (writeable != null) {
|
||||||
|
writeBoolean(true);
|
||||||
|
writeable.writeTo(this);
|
||||||
|
} else {
|
||||||
|
writeBoolean(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void writeThrowable(Throwable throwable) throws IOException {
|
public void writeThrowable(Throwable throwable) throws IOException {
|
||||||
if (throwable == null) {
|
if (throwable == null) {
|
||||||
writeBoolean(false);
|
writeBoolean(false);
|
||||||
|
@ -29,8 +29,10 @@ import org.elasticsearch.client.transport.TransportClientNodesService;
|
|||||||
import org.elasticsearch.cluster.ClusterModule;
|
import org.elasticsearch.cluster.ClusterModule;
|
||||||
import org.elasticsearch.cluster.ClusterName;
|
import org.elasticsearch.cluster.ClusterName;
|
||||||
import org.elasticsearch.cluster.InternalClusterInfoService;
|
import org.elasticsearch.cluster.InternalClusterInfoService;
|
||||||
|
import org.elasticsearch.cluster.NodeConnectionsService;
|
||||||
import org.elasticsearch.cluster.action.index.MappingUpdatedAction;
|
import org.elasticsearch.cluster.action.index.MappingUpdatedAction;
|
||||||
import org.elasticsearch.cluster.metadata.MetaData;
|
import org.elasticsearch.cluster.metadata.MetaData;
|
||||||
|
import org.elasticsearch.cluster.node.DiscoveryNodeService;
|
||||||
import org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator;
|
import org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator;
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.AwarenessAllocationDecider;
|
import org.elasticsearch.cluster.routing.allocation.decider.AwarenessAllocationDecider;
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.ClusterRebalanceAllocationDecider;
|
import org.elasticsearch.cluster.routing.allocation.decider.ClusterRebalanceAllocationDecider;
|
||||||
@ -259,7 +261,7 @@ public final class ClusterSettings extends AbstractScopedSettings {
|
|||||||
TransportService.TRACE_LOG_INCLUDE_SETTING,
|
TransportService.TRACE_LOG_INCLUDE_SETTING,
|
||||||
TransportCloseIndexAction.CLUSTER_INDICES_CLOSE_ENABLE_SETTING,
|
TransportCloseIndexAction.CLUSTER_INDICES_CLOSE_ENABLE_SETTING,
|
||||||
ShardsLimitAllocationDecider.CLUSTER_TOTAL_SHARDS_PER_NODE_SETTING,
|
ShardsLimitAllocationDecider.CLUSTER_TOTAL_SHARDS_PER_NODE_SETTING,
|
||||||
InternalClusterService.CLUSTER_SERVICE_RECONNECT_INTERVAL_SETTING,
|
NodeConnectionsService.CLUSTER_NODE_RECONNECT_INTERVAL_SETTING,
|
||||||
HierarchyCircuitBreakerService.FIELDDATA_CIRCUIT_BREAKER_TYPE_SETTING,
|
HierarchyCircuitBreakerService.FIELDDATA_CIRCUIT_BREAKER_TYPE_SETTING,
|
||||||
HierarchyCircuitBreakerService.REQUEST_CIRCUIT_BREAKER_TYPE_SETTING,
|
HierarchyCircuitBreakerService.REQUEST_CIRCUIT_BREAKER_TYPE_SETTING,
|
||||||
Transport.TRANSPORT_TCP_COMPRESS,
|
Transport.TRANSPORT_TCP_COMPRESS,
|
||||||
@ -326,7 +328,7 @@ public final class ClusterSettings extends AbstractScopedSettings {
|
|||||||
Environment.PATH_SCRIPTS_SETTING,
|
Environment.PATH_SCRIPTS_SETTING,
|
||||||
Environment.PATH_SHARED_DATA_SETTING,
|
Environment.PATH_SHARED_DATA_SETTING,
|
||||||
Environment.PIDFILE_SETTING,
|
Environment.PIDFILE_SETTING,
|
||||||
InternalClusterService.NODE_ID_SEED_SETTING,
|
DiscoveryNodeService.NODE_ID_SEED_SETTING,
|
||||||
DiscoverySettings.INITIAL_STATE_TIMEOUT_SETTING,
|
DiscoverySettings.INITIAL_STATE_TIMEOUT_SETTING,
|
||||||
DiscoveryModule.DISCOVERY_TYPE_SETTING,
|
DiscoveryModule.DISCOVERY_TYPE_SETTING,
|
||||||
DiscoveryModule.ZEN_MASTER_SERVICE_TYPE_SETTING,
|
DiscoveryModule.ZEN_MASTER_SERVICE_TYPE_SETTING,
|
||||||
|
@ -20,7 +20,10 @@
|
|||||||
package org.elasticsearch.common.util.concurrent;
|
package org.elasticsearch.common.util.concurrent;
|
||||||
|
|
||||||
|
|
||||||
|
import org.elasticsearch.common.lease.Releasable;
|
||||||
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
@ -30,7 +33,6 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
* lock. The latter is important to assure that the list of locks does not grow
|
* lock. The latter is important to assure that the list of locks does not grow
|
||||||
* infinitely.
|
* infinitely.
|
||||||
*
|
*
|
||||||
* A Thread can acquire a lock only once.
|
|
||||||
*
|
*
|
||||||
* */
|
* */
|
||||||
public class KeyedLock<T> {
|
public class KeyedLock<T> {
|
||||||
@ -50,48 +52,38 @@ public class KeyedLock<T> {
|
|||||||
|
|
||||||
private final ConcurrentMap<T, KeyLock> map = ConcurrentCollections.newConcurrentMap();
|
private final ConcurrentMap<T, KeyLock> map = ConcurrentCollections.newConcurrentMap();
|
||||||
|
|
||||||
protected final ThreadLocal<KeyLock> threadLocal = new ThreadLocal<>();
|
public Releasable acquire(T key) {
|
||||||
|
assert isHeldByCurrentThread(key) == false : "lock for " + key + " is already heald by this thread";
|
||||||
public void acquire(T key) {
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (threadLocal.get() != null) {
|
|
||||||
// if we are here, the thread already has the lock
|
|
||||||
throw new IllegalStateException("Lock already acquired in Thread" + Thread.currentThread().getId()
|
|
||||||
+ " for key " + key);
|
|
||||||
}
|
|
||||||
KeyLock perNodeLock = map.get(key);
|
KeyLock perNodeLock = map.get(key);
|
||||||
if (perNodeLock == null) {
|
if (perNodeLock == null) {
|
||||||
KeyLock newLock = new KeyLock(fair);
|
KeyLock newLock = new KeyLock(fair);
|
||||||
perNodeLock = map.putIfAbsent(key, newLock);
|
perNodeLock = map.putIfAbsent(key, newLock);
|
||||||
if (perNodeLock == null) {
|
if (perNodeLock == null) {
|
||||||
newLock.lock();
|
newLock.lock();
|
||||||
threadLocal.set(newLock);
|
return new ReleasableLock(key, newLock);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert perNodeLock != null;
|
assert perNodeLock != null;
|
||||||
int i = perNodeLock.count.get();
|
int i = perNodeLock.count.get();
|
||||||
if (i > 0 && perNodeLock.count.compareAndSet(i, i + 1)) {
|
if (i > 0 && perNodeLock.count.compareAndSet(i, i + 1)) {
|
||||||
perNodeLock.lock();
|
perNodeLock.lock();
|
||||||
threadLocal.set(perNodeLock);
|
return new ReleasableLock(key, perNodeLock);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void release(T key) {
|
public boolean isHeldByCurrentThread(T key) {
|
||||||
KeyLock lock = threadLocal.get();
|
KeyLock lock = map.get(key);
|
||||||
if (lock == null) {
|
if (lock == null) {
|
||||||
throw new IllegalStateException("Lock not acquired");
|
return false;
|
||||||
}
|
}
|
||||||
release(key, lock);
|
return lock.isHeldByCurrentThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
void release(T key, KeyLock lock) {
|
void release(T key, KeyLock lock) {
|
||||||
assert lock.isHeldByCurrentThread();
|
|
||||||
assert lock == map.get(key);
|
assert lock == map.get(key);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
threadLocal.set(null);
|
|
||||||
int decrementAndGet = lock.count.decrementAndGet();
|
int decrementAndGet = lock.count.decrementAndGet();
|
||||||
if (decrementAndGet == 0) {
|
if (decrementAndGet == 0) {
|
||||||
map.remove(key, lock);
|
map.remove(key, lock);
|
||||||
@ -99,6 +91,24 @@ public class KeyedLock<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private final class ReleasableLock implements Releasable {
|
||||||
|
final T key;
|
||||||
|
final KeyLock lock;
|
||||||
|
final AtomicBoolean closed = new AtomicBoolean();
|
||||||
|
|
||||||
|
private ReleasableLock(T key, KeyLock lock) {
|
||||||
|
this.key = key;
|
||||||
|
this.lock = lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (closed.compareAndSet(false, true)) {
|
||||||
|
release(key, lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
private final static class KeyLock extends ReentrantLock {
|
private final static class KeyLock extends ReentrantLock {
|
||||||
KeyLock(boolean fair) {
|
KeyLock(boolean fair) {
|
||||||
|
@ -19,8 +19,14 @@
|
|||||||
|
|
||||||
package org.elasticsearch.index.mapper;
|
package org.elasticsearch.index.mapper;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.lucene.document.Field;
|
import org.apache.lucene.document.Field;
|
||||||
import org.apache.lucene.index.IndexOptions;
|
|
||||||
import org.apache.lucene.index.IndexableField;
|
import org.apache.lucene.index.IndexableField;
|
||||||
import org.apache.lucene.util.CloseableThreadLocal;
|
import org.apache.lucene.util.CloseableThreadLocal;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
@ -48,15 +54,8 @@ import org.elasticsearch.index.mapper.object.ArrayValueMapperParser;
|
|||||||
import org.elasticsearch.index.mapper.object.ObjectMapper;
|
import org.elasticsearch.index.mapper.object.ObjectMapper;
|
||||||
import org.elasticsearch.index.mapper.object.RootObjectMapper;
|
import org.elasticsearch.index.mapper.object.RootObjectMapper;
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/** A parser for documents, given mappings from a DocumentMapper */
|
/** A parser for documents, given mappings from a DocumentMapper */
|
||||||
class DocumentParser implements Closeable {
|
final class DocumentParser implements Closeable {
|
||||||
|
|
||||||
private CloseableThreadLocal<ParseContext.InternalParseContext> cache = new CloseableThreadLocal<ParseContext.InternalParseContext>() {
|
private CloseableThreadLocal<ParseContext.InternalParseContext> cache = new CloseableThreadLocal<ParseContext.InternalParseContext>() {
|
||||||
@Override
|
@Override
|
||||||
@ -99,7 +98,7 @@ class DocumentParser implements Closeable {
|
|||||||
|
|
||||||
reverseOrder(context);
|
reverseOrder(context);
|
||||||
|
|
||||||
ParsedDocument doc = parsedDocument(source, context, update(context, mapping));
|
ParsedDocument doc = parsedDocument(source, context, createDynamicUpdate(mapping, docMapper, context.getDynamicMappers()));
|
||||||
// reset the context to free up memory
|
// reset the context to free up memory
|
||||||
context.reset(null, null, null);
|
context.reset(null, null, null);
|
||||||
return doc;
|
return doc;
|
||||||
@ -116,10 +115,7 @@ class DocumentParser implements Closeable {
|
|||||||
// entire type is disabled
|
// entire type is disabled
|
||||||
parser.skipChildren();
|
parser.skipChildren();
|
||||||
} else if (emptyDoc == false) {
|
} else if (emptyDoc == false) {
|
||||||
Mapper update = parseObject(context, mapping.root, true);
|
parseObjectOrNested(context, mapping.root, true);
|
||||||
if (update != null) {
|
|
||||||
context.addDynamicMappingsUpdate(update);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (MetadataFieldMapper metadataMapper : mapping.metadataMappers) {
|
for (MetadataFieldMapper metadataMapper : mapping.metadataMappers) {
|
||||||
@ -201,11 +197,6 @@ class DocumentParser implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static Mapping update(ParseContext.InternalParseContext context, Mapping mapping) {
|
|
||||||
Mapper rootDynamicUpdate = context.dynamicMappingsUpdate();
|
|
||||||
return rootDynamicUpdate != null ? mapping.mappingUpdate(rootDynamicUpdate) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static MapperParsingException wrapInMapperParsingException(SourceToParse source, Throwable e) {
|
private static MapperParsingException wrapInMapperParsingException(SourceToParse source, Throwable e) {
|
||||||
// if its already a mapper parsing exception, no need to wrap it...
|
// if its already a mapper parsing exception, no need to wrap it...
|
||||||
if (e instanceof MapperParsingException) {
|
if (e instanceof MapperParsingException) {
|
||||||
@ -220,10 +211,156 @@ class DocumentParser implements Closeable {
|
|||||||
return new MapperParsingException("failed to parse", e);
|
return new MapperParsingException("failed to parse", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ObjectMapper parseObject(ParseContext context, ObjectMapper mapper, boolean atRoot) throws IOException {
|
/** Creates a Mapping containing any dynamically added fields, or returns null if there were no dynamic mappings. */
|
||||||
|
static Mapping createDynamicUpdate(Mapping mapping, DocumentMapper docMapper, List<Mapper> dynamicMappers) {
|
||||||
|
if (dynamicMappers.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// We build a mapping by first sorting the mappers, so that all mappers containing a common prefix
|
||||||
|
// will be processed in a contiguous block. When the prefix is no longer seen, we pop the extra elements
|
||||||
|
// off the stack, merging them upwards into the existing mappers.
|
||||||
|
Collections.sort(dynamicMappers, (Mapper o1, Mapper o2) -> o1.name().compareTo(o2.name()));
|
||||||
|
Iterator<Mapper> dynamicMapperItr = dynamicMappers.iterator();
|
||||||
|
List<ObjectMapper> parentMappers = new ArrayList<>();
|
||||||
|
Mapper firstUpdate = dynamicMapperItr.next();
|
||||||
|
parentMappers.add(createUpdate(mapping.root(), firstUpdate.name().split("\\."), 0, firstUpdate));
|
||||||
|
Mapper previousMapper = null;
|
||||||
|
while (dynamicMapperItr.hasNext()) {
|
||||||
|
Mapper newMapper = dynamicMapperItr.next();
|
||||||
|
if (previousMapper != null && newMapper.name().equals(previousMapper.name())) {
|
||||||
|
// We can see the same mapper more than once, for example, if we had foo.bar and foo.baz, where
|
||||||
|
// foo did not yet exist. This will create 2 copies in dynamic mappings, which should be identical.
|
||||||
|
// Here we just skip over the duplicates, but we merge them to ensure there are no conflicts.
|
||||||
|
newMapper.merge(previousMapper, false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
previousMapper = newMapper;
|
||||||
|
String[] nameParts = newMapper.name().split("\\.");
|
||||||
|
|
||||||
|
// We first need the stack to only contain mappers in common with the previously processed mapper
|
||||||
|
// For example, if the first mapper processed was a.b.c, and we now have a.d, the stack will contain
|
||||||
|
// a.b, and we want to merge b back into the stack so it just contains a
|
||||||
|
int i = removeUncommonMappers(parentMappers, nameParts);
|
||||||
|
|
||||||
|
// Then we need to add back mappers that may already exist within the stack, but are not on it.
|
||||||
|
// For example, if we processed a.b, followed by an object mapper a.c.d, and now are adding a.c.d.e
|
||||||
|
// then the stack will only have a on it because we will have already merged a.c.d into the stack.
|
||||||
|
// So we need to pull a.c, followed by a.c.d, onto the stack so e can be added to the end.
|
||||||
|
i = expandCommonMappers(parentMappers, nameParts, i);
|
||||||
|
|
||||||
|
// If there are still parents of the new mapper which are not on the stack, we need to pull them
|
||||||
|
// from the existing mappings. In order to maintain the invariant that the stack only contains
|
||||||
|
// fields which are updated, we cannot simply add the existing mappers to the stack, since they
|
||||||
|
// may have other subfields which will not be updated. Instead, we pull the mapper from the existing
|
||||||
|
// mappings, and build an update with only the new mapper and its parents. This then becomes our
|
||||||
|
// "new mapper", and can be added to the stack.
|
||||||
|
if (i < nameParts.length - 1) {
|
||||||
|
newMapper = createExistingMapperUpdate(parentMappers, nameParts, i, docMapper, newMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newMapper instanceof ObjectMapper) {
|
||||||
|
parentMappers.add((ObjectMapper)newMapper);
|
||||||
|
} else {
|
||||||
|
addToLastMapper(parentMappers, newMapper, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
popMappers(parentMappers, 1, true);
|
||||||
|
assert parentMappers.size() == 1;
|
||||||
|
|
||||||
|
return mapping.mappingUpdate(parentMappers.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void popMappers(List<ObjectMapper> parentMappers, int keepBefore, boolean merge) {
|
||||||
|
assert keepBefore >= 1; // never remove the root mapper
|
||||||
|
// pop off parent mappers not needed by the current mapper,
|
||||||
|
// merging them backwards since they are immutable
|
||||||
|
for (int i = parentMappers.size() - 1; i >= keepBefore; --i) {
|
||||||
|
addToLastMapper(parentMappers, parentMappers.remove(i), merge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a mapper as an update into the last mapper. If merge is true, the new mapper
|
||||||
|
* will be merged in with other child mappers of the last parent, otherwise it will be a new update.
|
||||||
|
*/
|
||||||
|
private static void addToLastMapper(List<ObjectMapper> parentMappers, Mapper mapper, boolean merge) {
|
||||||
|
assert parentMappers.size() >= 1;
|
||||||
|
int lastIndex = parentMappers.size() - 1;
|
||||||
|
ObjectMapper withNewMapper = parentMappers.get(lastIndex).mappingUpdate(mapper);
|
||||||
|
if (merge) {
|
||||||
|
withNewMapper = parentMappers.get(lastIndex).merge(withNewMapper, false);
|
||||||
|
}
|
||||||
|
parentMappers.set(lastIndex, withNewMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes mappers that exist on the stack, but are not part of the path of the current nameParts,
|
||||||
|
* Returns the next unprocessed index from nameParts.
|
||||||
|
*/
|
||||||
|
private static int removeUncommonMappers(List<ObjectMapper> parentMappers, String[] nameParts) {
|
||||||
|
int keepBefore = 1;
|
||||||
|
while (keepBefore < parentMappers.size() &&
|
||||||
|
parentMappers.get(keepBefore).simpleName().equals(nameParts[keepBefore - 1])) {
|
||||||
|
++keepBefore;
|
||||||
|
}
|
||||||
|
popMappers(parentMappers, keepBefore, true);
|
||||||
|
return keepBefore - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds mappers from the end of the stack that exist as updates within those mappers.
|
||||||
|
* Returns the next unprocessed index from nameParts.
|
||||||
|
*/
|
||||||
|
private static int expandCommonMappers(List<ObjectMapper> parentMappers, String[] nameParts, int i) {
|
||||||
|
ObjectMapper last = parentMappers.get(parentMappers.size() - 1);
|
||||||
|
while (i < nameParts.length - 1 && last.getMapper(nameParts[i]) != null) {
|
||||||
|
Mapper newLast = last.getMapper(nameParts[i]);
|
||||||
|
assert newLast instanceof ObjectMapper;
|
||||||
|
last = (ObjectMapper) newLast;
|
||||||
|
parentMappers.add(last);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates an update for intermediate object mappers that are not on the stack, but parents of newMapper. */
|
||||||
|
private static ObjectMapper createExistingMapperUpdate(List<ObjectMapper> parentMappers, String[] nameParts, int i,
|
||||||
|
DocumentMapper docMapper, Mapper newMapper) {
|
||||||
|
String updateParentName = nameParts[i];
|
||||||
|
final ObjectMapper lastParent = parentMappers.get(parentMappers.size() - 1);
|
||||||
|
if (parentMappers.size() > 1) {
|
||||||
|
// only prefix with parent mapper if the parent mapper isn't the root (which has a fake name)
|
||||||
|
updateParentName = lastParent.name() + '.' + nameParts[i];
|
||||||
|
}
|
||||||
|
ObjectMapper updateParent = docMapper.objectMappers().get(updateParentName);
|
||||||
|
assert updateParent != null : updateParentName + " doesn't exist";
|
||||||
|
return createUpdate(updateParent, nameParts, i + 1, newMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Build an update for the parent which will contain the given mapper and any intermediate fields. */
|
||||||
|
private static ObjectMapper createUpdate(ObjectMapper parent, String[] nameParts, int i, Mapper mapper) {
|
||||||
|
List<ObjectMapper> parentMappers = new ArrayList<>();
|
||||||
|
ObjectMapper previousIntermediate = parent;
|
||||||
|
for (; i < nameParts.length - 1; ++i) {
|
||||||
|
Mapper intermediate = previousIntermediate.getMapper(nameParts[i]);
|
||||||
|
assert intermediate != null : "Field " + previousIntermediate.name() + " does not have a subfield " + nameParts[i];
|
||||||
|
assert intermediate instanceof ObjectMapper;
|
||||||
|
parentMappers.add((ObjectMapper)intermediate);
|
||||||
|
previousIntermediate = (ObjectMapper)intermediate;
|
||||||
|
}
|
||||||
|
if (parentMappers.isEmpty() == false) {
|
||||||
|
// add the new mapper to the stack, and pop down to the original parent level
|
||||||
|
addToLastMapper(parentMappers, mapper, false);
|
||||||
|
popMappers(parentMappers, 1, false);
|
||||||
|
mapper = parentMappers.get(0);
|
||||||
|
}
|
||||||
|
return parent.mappingUpdate(mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parseObjectOrNested(ParseContext context, ObjectMapper mapper, boolean atRoot) throws IOException {
|
||||||
if (mapper.isEnabled() == false) {
|
if (mapper.isEnabled() == false) {
|
||||||
context.parser().skipChildren();
|
context.parser().skipChildren();
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
XContentParser parser = context.parser();
|
XContentParser parser = context.parser();
|
||||||
|
|
||||||
@ -234,7 +371,7 @@ class DocumentParser implements Closeable {
|
|||||||
XContentParser.Token token = parser.currentToken();
|
XContentParser.Token token = parser.currentToken();
|
||||||
if (token == XContentParser.Token.VALUE_NULL) {
|
if (token == XContentParser.Token.VALUE_NULL) {
|
||||||
// the object is null ("obj1" : null), simply bail
|
// the object is null ("obj1" : null), simply bail
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token.isValue()) {
|
if (token.isValue()) {
|
||||||
@ -256,21 +393,19 @@ class DocumentParser implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ObjectMapper update = null;
|
ObjectMapper update = null;
|
||||||
update = innerParseObject(context, mapper, parser, currentFieldName, token, update);
|
innerParseObject(context, mapper, parser, currentFieldName, token);
|
||||||
// restore the enable path flag
|
// restore the enable path flag
|
||||||
if (nested.isNested()) {
|
if (nested.isNested()) {
|
||||||
nested(context, nested);
|
nested(context, nested);
|
||||||
}
|
}
|
||||||
return update;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ObjectMapper innerParseObject(ParseContext context, ObjectMapper mapper, XContentParser parser, String currentFieldName, XContentParser.Token token, ObjectMapper update) throws IOException {
|
private static void innerParseObject(ParseContext context, ObjectMapper mapper, XContentParser parser, String currentFieldName, XContentParser.Token token) throws IOException {
|
||||||
while (token != XContentParser.Token.END_OBJECT) {
|
while (token != XContentParser.Token.END_OBJECT) {
|
||||||
ObjectMapper newUpdate = null;
|
|
||||||
if (token == XContentParser.Token.START_OBJECT) {
|
if (token == XContentParser.Token.START_OBJECT) {
|
||||||
newUpdate = parseObject(context, mapper, currentFieldName);
|
parseObject(context, mapper, currentFieldName);
|
||||||
} else if (token == XContentParser.Token.START_ARRAY) {
|
} else if (token == XContentParser.Token.START_ARRAY) {
|
||||||
newUpdate = parseArray(context, mapper, currentFieldName);
|
parseArray(context, mapper, currentFieldName);
|
||||||
} else if (token == XContentParser.Token.FIELD_NAME) {
|
} else if (token == XContentParser.Token.FIELD_NAME) {
|
||||||
currentFieldName = parser.currentName();
|
currentFieldName = parser.currentName();
|
||||||
} else if (token == XContentParser.Token.VALUE_NULL) {
|
} else if (token == XContentParser.Token.VALUE_NULL) {
|
||||||
@ -278,18 +413,10 @@ class DocumentParser implements Closeable {
|
|||||||
} else if (token == null) {
|
} else if (token == null) {
|
||||||
throw new MapperParsingException("object mapping for [" + mapper.name() + "] tried to parse field [" + currentFieldName + "] as object, but got EOF, has a concrete value been provided to it?");
|
throw new MapperParsingException("object mapping for [" + mapper.name() + "] tried to parse field [" + currentFieldName + "] as object, but got EOF, has a concrete value been provided to it?");
|
||||||
} else if (token.isValue()) {
|
} else if (token.isValue()) {
|
||||||
newUpdate = parseValue(context, mapper, currentFieldName, token);
|
parseValue(context, mapper, currentFieldName, token);
|
||||||
}
|
}
|
||||||
token = parser.nextToken();
|
token = parser.nextToken();
|
||||||
if (newUpdate != null) {
|
|
||||||
if (update == null) {
|
|
||||||
update = newUpdate;
|
|
||||||
} else {
|
|
||||||
update = update.merge(newUpdate, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return update;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void nested(ParseContext context, ObjectMapper.Nested nested) {
|
private static void nested(ParseContext context, ObjectMapper.Nested nested) {
|
||||||
@ -335,33 +462,29 @@ class DocumentParser implements Closeable {
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Mapper parseObjectOrField(ParseContext context, Mapper mapper) throws IOException {
|
private static void parseObjectOrField(ParseContext context, Mapper mapper) throws IOException {
|
||||||
if (mapper instanceof ObjectMapper) {
|
if (mapper instanceof ObjectMapper) {
|
||||||
return parseObject(context, (ObjectMapper) mapper, false);
|
parseObjectOrNested(context, (ObjectMapper) mapper, false);
|
||||||
} else {
|
} else {
|
||||||
FieldMapper fieldMapper = (FieldMapper)mapper;
|
FieldMapper fieldMapper = (FieldMapper)mapper;
|
||||||
Mapper update = fieldMapper.parse(context);
|
Mapper update = fieldMapper.parse(context);
|
||||||
|
if (update != null) {
|
||||||
|
context.addDynamicMapper(update);
|
||||||
|
}
|
||||||
if (fieldMapper.copyTo() != null) {
|
if (fieldMapper.copyTo() != null) {
|
||||||
parseCopyFields(context, fieldMapper, fieldMapper.copyTo().copyToFields());
|
parseCopyFields(context, fieldMapper, fieldMapper.copyTo().copyToFields());
|
||||||
}
|
}
|
||||||
return update;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ObjectMapper parseObject(final ParseContext context, ObjectMapper mapper, String currentFieldName) throws IOException {
|
private static ObjectMapper parseObject(final ParseContext context, ObjectMapper mapper, String currentFieldName) throws IOException {
|
||||||
if (currentFieldName == null) {
|
assert currentFieldName != null;
|
||||||
throw new MapperParsingException("object mapping [" + mapper.name() + "] trying to serialize an object with no field associated with it, current value [" + context.parser().textOrNull() + "]");
|
|
||||||
}
|
|
||||||
context.path().add(currentFieldName);
|
context.path().add(currentFieldName);
|
||||||
|
|
||||||
ObjectMapper update = null;
|
ObjectMapper update = null;
|
||||||
Mapper objectMapper = mapper.getMapper(currentFieldName);
|
Mapper objectMapper = mapper.getMapper(currentFieldName);
|
||||||
if (objectMapper != null) {
|
if (objectMapper != null) {
|
||||||
final Mapper subUpdate = parseObjectOrField(context, objectMapper);
|
parseObjectOrField(context, objectMapper);
|
||||||
if (subUpdate != null) {
|
|
||||||
// propagate mapping update
|
|
||||||
update = mapper.mappingUpdate(subUpdate);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
ObjectMapper.Dynamic dynamic = mapper.dynamic();
|
ObjectMapper.Dynamic dynamic = mapper.dynamic();
|
||||||
if (dynamic == null) {
|
if (dynamic == null) {
|
||||||
@ -382,8 +505,9 @@ class DocumentParser implements Closeable {
|
|||||||
}
|
}
|
||||||
Mapper.BuilderContext builderContext = new Mapper.BuilderContext(context.indexSettings(), context.path());
|
Mapper.BuilderContext builderContext = new Mapper.BuilderContext(context.indexSettings(), context.path());
|
||||||
objectMapper = builder.build(builderContext);
|
objectMapper = builder.build(builderContext);
|
||||||
|
context.addDynamicMapper(objectMapper);
|
||||||
context.path().add(currentFieldName);
|
context.path().add(currentFieldName);
|
||||||
update = mapper.mappingUpdate(parseAndMergeUpdate(objectMapper, context));
|
parseObjectOrField(context, objectMapper);
|
||||||
} else {
|
} else {
|
||||||
// not dynamic, read everything up to end object
|
// not dynamic, read everything up to end object
|
||||||
context.parser().skipChildren();
|
context.parser().skipChildren();
|
||||||
@ -394,7 +518,7 @@ class DocumentParser implements Closeable {
|
|||||||
return update;
|
return update;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ObjectMapper parseArray(ParseContext context, ObjectMapper parentMapper, String lastFieldName) throws IOException {
|
private static void parseArray(ParseContext context, ObjectMapper parentMapper, String lastFieldName) throws IOException {
|
||||||
String arrayFieldName = lastFieldName;
|
String arrayFieldName = lastFieldName;
|
||||||
Mapper mapper = parentMapper.getMapper(lastFieldName);
|
Mapper mapper = parentMapper.getMapper(lastFieldName);
|
||||||
if (mapper != null) {
|
if (mapper != null) {
|
||||||
@ -402,15 +526,9 @@ class DocumentParser implements Closeable {
|
|||||||
// expects an array, if so we pass the context straight to the mapper and if not
|
// expects an array, if so we pass the context straight to the mapper and if not
|
||||||
// we serialize the array components
|
// we serialize the array components
|
||||||
if (mapper instanceof ArrayValueMapperParser) {
|
if (mapper instanceof ArrayValueMapperParser) {
|
||||||
final Mapper subUpdate = parseObjectOrField(context, mapper);
|
parseObjectOrField(context, mapper);
|
||||||
if (subUpdate != null) {
|
|
||||||
// propagate the mapping update
|
|
||||||
return parentMapper.mappingUpdate(subUpdate);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return parseNonDynamicArray(context, parentMapper, lastFieldName, arrayFieldName);
|
parseNonDynamicArray(context, parentMapper, lastFieldName, arrayFieldName);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@ -423,31 +541,34 @@ class DocumentParser implements Closeable {
|
|||||||
} else if (dynamic == ObjectMapper.Dynamic.TRUE) {
|
} else if (dynamic == ObjectMapper.Dynamic.TRUE) {
|
||||||
Mapper.Builder builder = context.root().findTemplateBuilder(context, arrayFieldName, "object");
|
Mapper.Builder builder = context.root().findTemplateBuilder(context, arrayFieldName, "object");
|
||||||
if (builder == null) {
|
if (builder == null) {
|
||||||
return parseNonDynamicArray(context, parentMapper, lastFieldName, arrayFieldName);
|
parseNonDynamicArray(context, parentMapper, lastFieldName, arrayFieldName);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
Mapper.BuilderContext builderContext = new Mapper.BuilderContext(context.indexSettings(), context.path());
|
Mapper.BuilderContext builderContext = new Mapper.BuilderContext(context.indexSettings(), context.path());
|
||||||
mapper = builder.build(builderContext);
|
mapper = builder.build(builderContext);
|
||||||
if (mapper != null && mapper instanceof ArrayValueMapperParser) {
|
assert mapper != null;
|
||||||
|
if (mapper instanceof ArrayValueMapperParser) {
|
||||||
|
context.addDynamicMapper(mapper);
|
||||||
context.path().add(arrayFieldName);
|
context.path().add(arrayFieldName);
|
||||||
mapper = parseAndMergeUpdate(mapper, context);
|
parseObjectOrField(context, mapper);
|
||||||
return parentMapper.mappingUpdate(mapper);
|
|
||||||
} else {
|
} else {
|
||||||
return parseNonDynamicArray(context, parentMapper, lastFieldName, arrayFieldName);
|
parseNonDynamicArray(context, parentMapper, lastFieldName, arrayFieldName);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return parseNonDynamicArray(context, parentMapper, lastFieldName, arrayFieldName);
|
// TODO: shouldn't this skip, not parse?
|
||||||
|
parseNonDynamicArray(context, parentMapper, lastFieldName, arrayFieldName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ObjectMapper parseNonDynamicArray(ParseContext context, ObjectMapper mapper, String lastFieldName, String arrayFieldName) throws IOException {
|
private static void parseNonDynamicArray(ParseContext context, ObjectMapper mapper, String lastFieldName, String arrayFieldName) throws IOException {
|
||||||
XContentParser parser = context.parser();
|
XContentParser parser = context.parser();
|
||||||
XContentParser.Token token;
|
XContentParser.Token token;
|
||||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||||
if (token == XContentParser.Token.START_OBJECT) {
|
if (token == XContentParser.Token.START_OBJECT) {
|
||||||
return parseObject(context, mapper, lastFieldName);
|
parseObject(context, mapper, lastFieldName);
|
||||||
} else if (token == XContentParser.Token.START_ARRAY) {
|
} else if (token == XContentParser.Token.START_ARRAY) {
|
||||||
return parseArray(context, mapper, lastFieldName);
|
parseArray(context, mapper, lastFieldName);
|
||||||
} else if (token == XContentParser.Token.FIELD_NAME) {
|
} else if (token == XContentParser.Token.FIELD_NAME) {
|
||||||
lastFieldName = parser.currentName();
|
lastFieldName = parser.currentName();
|
||||||
} else if (token == XContentParser.Token.VALUE_NULL) {
|
} else if (token == XContentParser.Token.VALUE_NULL) {
|
||||||
@ -455,25 +576,20 @@ class DocumentParser implements Closeable {
|
|||||||
} else if (token == null) {
|
} else if (token == null) {
|
||||||
throw new MapperParsingException("object mapping for [" + mapper.name() + "] with array for [" + arrayFieldName + "] tried to parse as array, but got EOF, is there a mismatch in types for the same field?");
|
throw new MapperParsingException("object mapping for [" + mapper.name() + "] with array for [" + arrayFieldName + "] tried to parse as array, but got EOF, is there a mismatch in types for the same field?");
|
||||||
} else {
|
} else {
|
||||||
return parseValue(context, mapper, lastFieldName, token);
|
parseValue(context, mapper, lastFieldName, token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ObjectMapper parseValue(final ParseContext context, ObjectMapper parentMapper, String currentFieldName, XContentParser.Token token) throws IOException {
|
private static void parseValue(final ParseContext context, ObjectMapper parentMapper, String currentFieldName, XContentParser.Token token) throws IOException {
|
||||||
if (currentFieldName == null) {
|
if (currentFieldName == null) {
|
||||||
throw new MapperParsingException("object mapping [" + parentMapper.name() + "] trying to serialize a value with no field associated with it, current value [" + context.parser().textOrNull() + "]");
|
throw new MapperParsingException("object mapping [" + parentMapper.name() + "] trying to serialize a value with no field associated with it, current value [" + context.parser().textOrNull() + "]");
|
||||||
}
|
}
|
||||||
Mapper mapper = parentMapper.getMapper(currentFieldName);
|
Mapper mapper = parentMapper.getMapper(currentFieldName);
|
||||||
if (mapper != null) {
|
if (mapper != null) {
|
||||||
Mapper subUpdate = parseObjectOrField(context, mapper);
|
parseObjectOrField(context, mapper);
|
||||||
if (subUpdate == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return parentMapper.mappingUpdate(subUpdate);
|
|
||||||
} else {
|
} else {
|
||||||
return parseDynamicValue(context, parentMapper, currentFieldName, token);
|
parseDynamicValue(context, parentMapper, currentFieldName, token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -641,7 +757,7 @@ class DocumentParser implements Closeable {
|
|||||||
throw new IllegalStateException("Can't handle serializing a dynamic type with content token [" + token + "] and field name [" + currentFieldName + "]");
|
throw new IllegalStateException("Can't handle serializing a dynamic type with content token [" + token + "] and field name [" + currentFieldName + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ObjectMapper parseDynamicValue(final ParseContext context, ObjectMapper parentMapper, String currentFieldName, XContentParser.Token token) throws IOException {
|
private static void parseDynamicValue(final ParseContext context, ObjectMapper parentMapper, String currentFieldName, XContentParser.Token token) throws IOException {
|
||||||
ObjectMapper.Dynamic dynamic = parentMapper.dynamic();
|
ObjectMapper.Dynamic dynamic = parentMapper.dynamic();
|
||||||
if (dynamic == null) {
|
if (dynamic == null) {
|
||||||
dynamic = dynamicOrDefault(context.root().dynamic());
|
dynamic = dynamicOrDefault(context.root().dynamic());
|
||||||
@ -650,7 +766,7 @@ class DocumentParser implements Closeable {
|
|||||||
throw new StrictDynamicMappingException(parentMapper.fullPath(), currentFieldName);
|
throw new StrictDynamicMappingException(parentMapper.fullPath(), currentFieldName);
|
||||||
}
|
}
|
||||||
if (dynamic == ObjectMapper.Dynamic.FALSE) {
|
if (dynamic == ObjectMapper.Dynamic.FALSE) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
final String path = context.path().pathAsText(currentFieldName);
|
final String path = context.path().pathAsText(currentFieldName);
|
||||||
final Mapper.BuilderContext builderContext = new Mapper.BuilderContext(context.indexSettings(), context.path());
|
final Mapper.BuilderContext builderContext = new Mapper.BuilderContext(context.indexSettings(), context.path());
|
||||||
@ -668,14 +784,9 @@ class DocumentParser implements Closeable {
|
|||||||
// try to not introduce a conflict
|
// try to not introduce a conflict
|
||||||
mapper = mapper.updateFieldType(Collections.singletonMap(path, existingFieldType));
|
mapper = mapper.updateFieldType(Collections.singletonMap(path, existingFieldType));
|
||||||
}
|
}
|
||||||
|
context.addDynamicMapper(mapper);
|
||||||
|
|
||||||
mapper = parseAndMergeUpdate(mapper, context);
|
parseObjectOrField(context, mapper);
|
||||||
|
|
||||||
ObjectMapper update = null;
|
|
||||||
if (mapper != null) {
|
|
||||||
update = parentMapper.mappingUpdate(mapper);
|
|
||||||
}
|
|
||||||
return update;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates instances of the fields that the current field should be copied to */
|
/** Creates instances of the fields that the current field should be copied to */
|
||||||
@ -713,8 +824,9 @@ class DocumentParser implements Closeable {
|
|||||||
// The path of the dest field might be completely different from the current one so we need to reset it
|
// The path of the dest field might be completely different from the current one so we need to reset it
|
||||||
context = context.overridePath(new ContentPath(0));
|
context = context.overridePath(new ContentPath(0));
|
||||||
|
|
||||||
String[] paths = Strings.splitStringToArray(field, '.');
|
// TODO: why Strings.splitStringToArray instead of String.split?
|
||||||
String fieldName = paths[paths.length-1];
|
final String[] paths = Strings.splitStringToArray(field, '.');
|
||||||
|
final String fieldName = paths[paths.length-1];
|
||||||
ObjectMapper mapper = context.root();
|
ObjectMapper mapper = context.root();
|
||||||
ObjectMapper[] mappers = new ObjectMapper[paths.length-1];
|
ObjectMapper[] mappers = new ObjectMapper[paths.length-1];
|
||||||
if (paths.length > 1) {
|
if (paths.length > 1) {
|
||||||
@ -745,6 +857,7 @@ class DocumentParser implements Closeable {
|
|||||||
if (mapper.nested() != ObjectMapper.Nested.NO) {
|
if (mapper.nested() != ObjectMapper.Nested.NO) {
|
||||||
throw new MapperParsingException("It is forbidden to create dynamic nested objects ([" + context.path().pathAsText(paths[i]) + "]) through `copy_to`");
|
throw new MapperParsingException("It is forbidden to create dynamic nested objects ([" + context.path().pathAsText(paths[i]) + "]) through `copy_to`");
|
||||||
}
|
}
|
||||||
|
context.addDynamicMapper(mapper);
|
||||||
break;
|
break;
|
||||||
case FALSE:
|
case FALSE:
|
||||||
// Maybe we should log something to tell the user that the copy_to is ignored in this case.
|
// Maybe we should log something to tell the user that the copy_to is ignored in this case.
|
||||||
@ -759,36 +872,10 @@ class DocumentParser implements Closeable {
|
|||||||
parent = mapper;
|
parent = mapper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ObjectMapper update = parseDynamicValue(context, mapper, fieldName, context.parser().currentToken());
|
parseDynamicValue(context, mapper, fieldName, context.parser().currentToken());
|
||||||
assert update != null; // we are parsing a dynamic value so we necessarily created a new mapping
|
|
||||||
|
|
||||||
if (paths.length > 1) {
|
|
||||||
for (int i = paths.length - 2; i >= 0; i--) {
|
|
||||||
ObjectMapper parent = context.root();
|
|
||||||
if (i > 0) {
|
|
||||||
parent = mappers[i-1];
|
|
||||||
}
|
|
||||||
assert parent != null;
|
|
||||||
update = parent.mappingUpdate(update);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
context.addDynamicMappingsUpdate(update);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse the given {@code context} with the given {@code mapper} and apply
|
|
||||||
* the potential mapping update in-place. This method is useful when
|
|
||||||
* composing mapping updates.
|
|
||||||
*/
|
|
||||||
private static <M extends Mapper> M parseAndMergeUpdate(M mapper, ParseContext context) throws IOException {
|
|
||||||
final Mapper update = parseObjectOrField(context, mapper);
|
|
||||||
if (update != null) {
|
|
||||||
mapper = (M) mapper.merge(update, false);
|
|
||||||
}
|
|
||||||
return mapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ObjectMapper.Dynamic dynamicOrDefault(ObjectMapper.Dynamic dynamic) {
|
private static ObjectMapper.Dynamic dynamicOrDefault(ObjectMapper.Dynamic dynamic) {
|
||||||
return dynamic == null ? ObjectMapper.Dynamic.TRUE : dynamic;
|
return dynamic == null ? ObjectMapper.Dynamic.TRUE : dynamic;
|
||||||
}
|
}
|
||||||
|
@ -374,7 +374,8 @@ public abstract class FieldMapper extends Mapper implements Cloneable {
|
|||||||
// this can happen if this mapper represents a mapping update
|
// this can happen if this mapper represents a mapping update
|
||||||
return this;
|
return this;
|
||||||
} else if (fieldType.getClass() != newFieldType.getClass()) {
|
} else if (fieldType.getClass() != newFieldType.getClass()) {
|
||||||
throw new IllegalStateException("Mixing up field types: " + fieldType.getClass() + " != " + newFieldType.getClass());
|
throw new IllegalStateException("Mixing up field types: " +
|
||||||
|
fieldType.getClass() + " != " + newFieldType.getClass() + " on field " + fieldType.name());
|
||||||
}
|
}
|
||||||
MultiFields updatedMultiFields = multiFields.updateFieldType(fullNameToFieldType);
|
MultiFields updatedMultiFields = multiFields.updateFieldType(fullNameToFieldType);
|
||||||
if (fieldType == newFieldType && multiFields == updatedMultiFields) {
|
if (fieldType == newFieldType && multiFields == updatedMultiFields) {
|
||||||
|
@ -76,6 +76,7 @@ public abstract class Mapper implements ToXContent, Iterable<Mapper> {
|
|||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns a newly built mapper. */
|
||||||
public abstract Y build(BuilderContext context);
|
public abstract Y build(BuilderContext context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,13 +331,13 @@ public abstract class ParseContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addDynamicMappingsUpdate(Mapper update) {
|
public void addDynamicMapper(Mapper update) {
|
||||||
in.addDynamicMappingsUpdate(update);
|
in.addDynamicMapper(update);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mapper dynamicMappingsUpdate() {
|
public List<Mapper> getDynamicMappers() {
|
||||||
return in.dynamicMappingsUpdate();
|
return in.getDynamicMappers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,7 +369,7 @@ public abstract class ParseContext {
|
|||||||
|
|
||||||
private AllEntries allEntries = new AllEntries();
|
private AllEntries allEntries = new AllEntries();
|
||||||
|
|
||||||
private Mapper dynamicMappingsUpdate = null;
|
private List<Mapper> dynamicMappers = new ArrayList<>();
|
||||||
|
|
||||||
public InternalParseContext(@Nullable Settings indexSettings, DocumentMapperParser docMapperParser, DocumentMapper docMapper, ContentPath path) {
|
public InternalParseContext(@Nullable Settings indexSettings, DocumentMapperParser docMapperParser, DocumentMapper docMapper, ContentPath path) {
|
||||||
this.indexSettings = indexSettings;
|
this.indexSettings = indexSettings;
|
||||||
@ -394,7 +394,7 @@ public abstract class ParseContext {
|
|||||||
this.source = source == null ? null : sourceToParse.source();
|
this.source = source == null ? null : sourceToParse.source();
|
||||||
this.path.reset();
|
this.path.reset();
|
||||||
this.allEntries = new AllEntries();
|
this.allEntries = new AllEntries();
|
||||||
this.dynamicMappingsUpdate = null;
|
this.dynamicMappers = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -536,18 +536,13 @@ public abstract class ParseContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addDynamicMappingsUpdate(Mapper mapper) {
|
public void addDynamicMapper(Mapper mapper) {
|
||||||
assert mapper instanceof RootObjectMapper : mapper;
|
dynamicMappers.add(mapper);
|
||||||
if (dynamicMappingsUpdate == null) {
|
|
||||||
dynamicMappingsUpdate = mapper;
|
|
||||||
} else {
|
|
||||||
dynamicMappingsUpdate = dynamicMappingsUpdate.merge(mapper, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mapper dynamicMappingsUpdate() {
|
public List<Mapper> getDynamicMappers() {
|
||||||
return dynamicMappingsUpdate;
|
return dynamicMappers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -747,12 +742,12 @@ public abstract class ParseContext {
|
|||||||
public abstract StringBuilder stringBuilder();
|
public abstract StringBuilder stringBuilder();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a dynamic update to the root object mapper.
|
* Add a new mapper dynamically created while parsing.
|
||||||
*/
|
*/
|
||||||
public abstract void addDynamicMappingsUpdate(Mapper update);
|
public abstract void addDynamicMapper(Mapper update);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get dynamic updates to the root object mapper.
|
* Get dynamic mappers created while parsing.
|
||||||
*/
|
*/
|
||||||
public abstract Mapper dynamicMappingsUpdate();
|
public abstract List<Mapper> getDynamicMappers();
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,9 @@ import org.apache.lucene.search.Query;
|
|||||||
import org.apache.lucene.util.BytesRef;
|
import org.apache.lucene.util.BytesRef;
|
||||||
import org.elasticsearch.Version;
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
|
import org.elasticsearch.common.logging.DeprecationLogger;
|
||||||
|
import org.elasticsearch.common.logging.ESLogger;
|
||||||
|
import org.elasticsearch.common.logging.Loggers;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
@ -39,9 +42,12 @@ import org.elasticsearch.index.mapper.ParseContext;
|
|||||||
import org.elasticsearch.index.mapper.internal.AllFieldMapper;
|
import org.elasticsearch.index.mapper.internal.AllFieldMapper;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.apache.lucene.index.IndexOptions.NONE;
|
import static org.apache.lucene.index.IndexOptions.NONE;
|
||||||
import static org.elasticsearch.index.mapper.core.TypeParsers.parseMultiField;
|
import static org.elasticsearch.index.mapper.core.TypeParsers.parseMultiField;
|
||||||
@ -52,6 +58,11 @@ public class StringFieldMapper extends FieldMapper implements AllFieldMapper.Inc
|
|||||||
public static final String CONTENT_TYPE = "string";
|
public static final String CONTENT_TYPE = "string";
|
||||||
private static final int POSITION_INCREMENT_GAP_USE_ANALYZER = -1;
|
private static final int POSITION_INCREMENT_GAP_USE_ANALYZER = -1;
|
||||||
|
|
||||||
|
private static final Set<String> SUPPORTED_PARAMETERS_FOR_AUTO_UPGRADE = new HashSet<>(Arrays.asList(
|
||||||
|
"type",
|
||||||
|
// most common parameters, for which the upgrade is straightforward
|
||||||
|
"index", "store", "doc_values", "omit_norms", "norms", "fields", "copy_to"));
|
||||||
|
|
||||||
public static class Defaults {
|
public static class Defaults {
|
||||||
public static final MappedFieldType FIELD_TYPE = new StringFieldType();
|
public static final MappedFieldType FIELD_TYPE = new StringFieldType();
|
||||||
|
|
||||||
@ -130,13 +141,33 @@ public class StringFieldMapper extends FieldMapper implements AllFieldMapper.Inc
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class TypeParser implements Mapper.TypeParser {
|
public static class TypeParser implements Mapper.TypeParser {
|
||||||
|
private final DeprecationLogger deprecationLogger;
|
||||||
|
|
||||||
|
public TypeParser() {
|
||||||
|
ESLogger logger = Loggers.getLogger(getClass());
|
||||||
|
this.deprecationLogger = new DeprecationLogger(logger);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mapper.Builder parse(String fieldName, Map<String, Object> node, ParserContext parserContext) throws MapperParsingException {
|
public Mapper.Builder parse(String fieldName, Map<String, Object> node, ParserContext parserContext) throws MapperParsingException {
|
||||||
// TODO: temporarily disabled to give Kibana time to upgrade to text/keyword mappings
|
if (parserContext.indexVersionCreated().onOrAfter(Version.V_5_0_0)) {
|
||||||
/*if (parserContext.indexVersionCreated().onOrAfter(Version.V_5_0_0)) {
|
// Automatically upgrade simple mappings for ease of upgrade, otherwise fail
|
||||||
|
if (SUPPORTED_PARAMETERS_FOR_AUTO_UPGRADE.containsAll(node.keySet())) {
|
||||||
|
deprecationLogger.deprecated("The [string] field is deprecated, please use [text] or [keyword] instead on [{}]",
|
||||||
|
fieldName);
|
||||||
|
final Object index = node.remove("index");
|
||||||
|
final boolean keyword = index != null && "analyzed".equals(index) == false;
|
||||||
|
// upgrade the index setting
|
||||||
|
node.put("index", "no".equals(index) == false);
|
||||||
|
if (keyword) {
|
||||||
|
return new KeywordFieldMapper.TypeParser().parse(fieldName, node, parserContext);
|
||||||
|
} else {
|
||||||
|
return new TextFieldMapper.TypeParser().parse(fieldName, node, parserContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
throw new IllegalArgumentException("The [string] type is removed in 5.0. You should now use either a [text] "
|
throw new IllegalArgumentException("The [string] type is removed in 5.0. You should now use either a [text] "
|
||||||
+ "or [keyword] field instead for field [" + fieldName + "]");
|
+ "or [keyword] field instead for field [" + fieldName + "]");
|
||||||
}*/
|
}
|
||||||
StringFieldMapper.Builder builder = new StringFieldMapper.Builder(fieldName);
|
StringFieldMapper.Builder builder = new StringFieldMapper.Builder(fieldName);
|
||||||
// hack for the fact that string can't just accept true/false for
|
// hack for the fact that string can't just accept true/false for
|
||||||
// the index property and still accepts no/not_analyzed/analyzed
|
// the index property and still accepts no/not_analyzed/analyzed
|
||||||
@ -241,11 +272,10 @@ public class StringFieldMapper extends FieldMapper implements AllFieldMapper.Inc
|
|||||||
int positionIncrementGap, int ignoreAbove,
|
int positionIncrementGap, int ignoreAbove,
|
||||||
Settings indexSettings, MultiFields multiFields, CopyTo copyTo) {
|
Settings indexSettings, MultiFields multiFields, CopyTo copyTo) {
|
||||||
super(simpleName, fieldType, defaultFieldType, indexSettings, multiFields, copyTo);
|
super(simpleName, fieldType, defaultFieldType, indexSettings, multiFields, copyTo);
|
||||||
// TODO: temporarily disabled to give Kibana time to upgrade to text/keyword mappings
|
if (Version.indexCreated(indexSettings).onOrAfter(Version.V_5_0_0)) {
|
||||||
/*if (Version.indexCreated(indexSettings).onOrAfter(Version.V_5_0_0)) {
|
|
||||||
throw new IllegalArgumentException("The [string] type is removed in 5.0. You should now use either a [text] "
|
throw new IllegalArgumentException("The [string] type is removed in 5.0. You should now use either a [text] "
|
||||||
+ "or [keyword] field instead for field [" + fieldType.name() + "]");
|
+ "or [keyword] field instead for field [" + fieldType.name() + "]");
|
||||||
}*/
|
}
|
||||||
if (fieldType.tokenized() && fieldType.indexOptions() != NONE && fieldType().hasDocValues()) {
|
if (fieldType.tokenized() && fieldType.indexOptions() != NONE && fieldType().hasDocValues()) {
|
||||||
throw new MapperParsingException("Field [" + fieldType.name() + "] cannot be analyzed and have doc values");
|
throw new MapperParsingException("Field [" + fieldType.name() + "] cannot be analyzed and have doc values");
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,9 @@ import org.elasticsearch.Version;
|
|||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||||
import org.elasticsearch.common.joda.Joda;
|
import org.elasticsearch.common.joda.Joda;
|
||||||
|
import org.elasticsearch.common.logging.DeprecationLogger;
|
||||||
import org.elasticsearch.common.logging.ESLoggerFactory;
|
import org.elasticsearch.common.logging.ESLoggerFactory;
|
||||||
|
import org.elasticsearch.common.logging.Loggers;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.settings.loader.SettingsLoader;
|
import org.elasticsearch.common.settings.loader.SettingsLoader;
|
||||||
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
||||||
@ -39,11 +41,14 @@ import org.elasticsearch.index.mapper.object.ObjectMapper;
|
|||||||
import org.elasticsearch.index.similarity.SimilarityProvider;
|
import org.elasticsearch.index.similarity.SimilarityProvider;
|
||||||
import org.elasticsearch.index.similarity.SimilarityService;
|
import org.elasticsearch.index.similarity.SimilarityService;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.elasticsearch.common.xcontent.support.XContentMapValues.isArray;
|
import static org.elasticsearch.common.xcontent.support.XContentMapValues.isArray;
|
||||||
import static org.elasticsearch.common.xcontent.support.XContentMapValues.lenientNodeBooleanValue;
|
import static org.elasticsearch.common.xcontent.support.XContentMapValues.lenientNodeBooleanValue;
|
||||||
@ -63,10 +68,18 @@ public class TypeParsers {
|
|||||||
public static final String INDEX_OPTIONS_POSITIONS = "positions";
|
public static final String INDEX_OPTIONS_POSITIONS = "positions";
|
||||||
public static final String INDEX_OPTIONS_OFFSETS = "offsets";
|
public static final String INDEX_OPTIONS_OFFSETS = "offsets";
|
||||||
|
|
||||||
private static boolean nodeBooleanValue(Object node, Mapper.TypeParser.ParserContext parserContext) {
|
private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(Loggers.getLogger(TypeParsers.class));
|
||||||
if (parserContext.indexVersionCreated().onOrAfter(Version.V_5_0_0)) {
|
private static final Set<String> BOOLEAN_STRINGS = new HashSet<>(Arrays.asList("true", "false"));
|
||||||
|
|
||||||
|
private static boolean nodeBooleanValue(String name, Object node, Mapper.TypeParser.ParserContext parserContext) {
|
||||||
|
// Hook onto ParseFieldMatcher so that parsing becomes strict when setting index.query.parse.strict
|
||||||
|
if (parserContext.parseFieldMatcher().isStrict()) {
|
||||||
return XContentMapValues.nodeBooleanValue(node);
|
return XContentMapValues.nodeBooleanValue(node);
|
||||||
} else {
|
} else {
|
||||||
|
// TODO: remove this leniency in 6.0
|
||||||
|
if (BOOLEAN_STRINGS.contains(node.toString()) == false) {
|
||||||
|
DEPRECATION_LOGGER.deprecated("Expected a boolean for property [{}] but got [{}]", name, node);
|
||||||
|
}
|
||||||
return XContentMapValues.lenientNodeBooleanValue(node);
|
return XContentMapValues.lenientNodeBooleanValue(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,13 +94,13 @@ public class TypeParsers {
|
|||||||
builder.precisionStep(nodeIntegerValue(propNode));
|
builder.precisionStep(nodeIntegerValue(propNode));
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
} else if (propName.equals("ignore_malformed")) {
|
} else if (propName.equals("ignore_malformed")) {
|
||||||
builder.ignoreMalformed(nodeBooleanValue(propNode, parserContext));
|
builder.ignoreMalformed(nodeBooleanValue("ignore_malformed", propNode, parserContext));
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
} else if (propName.equals("coerce")) {
|
} else if (propName.equals("coerce")) {
|
||||||
builder.coerce(nodeBooleanValue(propNode, parserContext));
|
builder.coerce(nodeBooleanValue("coerce", propNode, parserContext));
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
} else if (propName.equals("omit_norms")) {
|
} else if (propName.equals("omit_norms")) {
|
||||||
builder.omitNorms(nodeBooleanValue(propNode, parserContext));
|
builder.omitNorms(nodeBooleanValue("omit_norms", propNode, parserContext));
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
} else if (propName.equals("similarity")) {
|
} else if (propName.equals("similarity")) {
|
||||||
SimilarityProvider similarityProvider = resolveSimilarity(parserContext, name, propNode.toString());
|
SimilarityProvider similarityProvider = resolveSimilarity(parserContext, name, propNode.toString());
|
||||||
@ -112,16 +125,16 @@ public class TypeParsers {
|
|||||||
parseTermVector(name, propNode.toString(), builder);
|
parseTermVector(name, propNode.toString(), builder);
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
} else if (propName.equals("store_term_vectors")) {
|
} else if (propName.equals("store_term_vectors")) {
|
||||||
builder.storeTermVectors(nodeBooleanValue(propNode, parserContext));
|
builder.storeTermVectors(nodeBooleanValue("store_term_vectors", propNode, parserContext));
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
} else if (propName.equals("store_term_vector_offsets")) {
|
} else if (propName.equals("store_term_vector_offsets")) {
|
||||||
builder.storeTermVectorOffsets(nodeBooleanValue(propNode, parserContext));
|
builder.storeTermVectorOffsets(nodeBooleanValue("store_term_vector_offsets", propNode, parserContext));
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
} else if (propName.equals("store_term_vector_positions")) {
|
} else if (propName.equals("store_term_vector_positions")) {
|
||||||
builder.storeTermVectorPositions(nodeBooleanValue(propNode, parserContext));
|
builder.storeTermVectorPositions(nodeBooleanValue("store_term_vector_positions", propNode, parserContext));
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
} else if (propName.equals("store_term_vector_payloads")) {
|
} else if (propName.equals("store_term_vector_payloads")) {
|
||||||
builder.storeTermVectorPayloads(nodeBooleanValue(propNode, parserContext));
|
builder.storeTermVectorPayloads(nodeBooleanValue("store_term_vector_payloads", propNode, parserContext));
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
} else if (propName.equals("analyzer")) {
|
} else if (propName.equals("analyzer")) {
|
||||||
NamedAnalyzer analyzer = parserContext.analysisService().analyzer(propNode.toString());
|
NamedAnalyzer analyzer = parserContext.analysisService().analyzer(propNode.toString());
|
||||||
@ -199,13 +212,13 @@ public class TypeParsers {
|
|||||||
builder.index(parseIndex(name, propNode.toString(), parserContext));
|
builder.index(parseIndex(name, propNode.toString(), parserContext));
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
} else if (propName.equals(DOC_VALUES)) {
|
} else if (propName.equals(DOC_VALUES)) {
|
||||||
builder.docValues(nodeBooleanValue(propNode, parserContext));
|
builder.docValues(nodeBooleanValue(DOC_VALUES, propNode, parserContext));
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
} else if (propName.equals("boost")) {
|
} else if (propName.equals("boost")) {
|
||||||
builder.boost(nodeFloatValue(propNode));
|
builder.boost(nodeFloatValue(propNode));
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
} else if (propName.equals("omit_norms")) {
|
} else if (propName.equals("omit_norms")) {
|
||||||
builder.omitNorms(nodeBooleanValue(propNode, parserContext));
|
builder.omitNorms(nodeBooleanValue("omit_norms", propNode, parserContext));
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
} else if (propName.equals("norms")) {
|
} else if (propName.equals("norms")) {
|
||||||
final Map<String, Object> properties = nodeMapValue(propNode, "norms");
|
final Map<String, Object> properties = nodeMapValue(propNode, "norms");
|
||||||
@ -227,7 +240,7 @@ public class TypeParsers {
|
|||||||
builder.indexOptions(nodeIndexOptionValue(propNode));
|
builder.indexOptions(nodeIndexOptionValue(propNode));
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
} else if (propName.equals("include_in_all")) {
|
} else if (propName.equals("include_in_all")) {
|
||||||
builder.includeInAll(nodeBooleanValue(propNode, parserContext));
|
builder.includeInAll(nodeBooleanValue("include_in_all", propNode, parserContext));
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
} else if (propName.equals("similarity")) {
|
} else if (propName.equals("similarity")) {
|
||||||
SimilarityProvider similarityProvider = resolveSimilarity(parserContext, name, propNode.toString());
|
SimilarityProvider similarityProvider = resolveSimilarity(parserContext, name, propNode.toString());
|
||||||
@ -353,35 +366,32 @@ public class TypeParsers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean parseIndex(String fieldName, String index, Mapper.TypeParser.ParserContext parserContext) throws MapperParsingException {
|
public static boolean parseIndex(String fieldName, String index, Mapper.TypeParser.ParserContext parserContext) throws MapperParsingException {
|
||||||
if (parserContext.indexVersionCreated().onOrAfter(Version.V_5_0_0)) {
|
switch (index) {
|
||||||
switch (index) {
|
case "true":
|
||||||
case "true":
|
return true;
|
||||||
return true;
|
case "false":
|
||||||
case "false":
|
return false;
|
||||||
return false;
|
case "not_analyzed":
|
||||||
default:
|
case "analyzed":
|
||||||
|
case "no":
|
||||||
|
if (parserContext.parseFieldMatcher().isStrict() == false) {
|
||||||
|
DEPRECATION_LOGGER.deprecated("Expected a boolean for property [index] but got [{}]", index);
|
||||||
|
return "no".equals(index) == false;
|
||||||
|
} else {
|
||||||
throw new IllegalArgumentException("Can't parse [index] value [" + index + "] for field [" + fieldName + "], expected [true] or [false]");
|
throw new IllegalArgumentException("Can't parse [index] value [" + index + "] for field [" + fieldName + "], expected [true] or [false]");
|
||||||
}
|
}
|
||||||
} else {
|
default:
|
||||||
final String normalizedIndex = Strings.toUnderscoreCase(index);
|
throw new IllegalArgumentException("Can't parse [index] value [" + index + "] for field [" + fieldName + "], expected [true] or [false]");
|
||||||
switch (normalizedIndex) {
|
|
||||||
case "true":
|
|
||||||
case "not_analyzed":
|
|
||||||
case "analyzed":
|
|
||||||
return true;
|
|
||||||
case "false":
|
|
||||||
case "no":
|
|
||||||
return false;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Can't parse [index] value [" + index + "] for field [" + fieldName + "], expected [true], [false], [no], [not_analyzed] or [analyzed]");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean parseStore(String fieldName, String store, Mapper.TypeParser.ParserContext parserContext) throws MapperParsingException {
|
public static boolean parseStore(String fieldName, String store, Mapper.TypeParser.ParserContext parserContext) throws MapperParsingException {
|
||||||
if (parserContext.indexVersionCreated().onOrAfter(Version.V_5_0_0)) {
|
if (parserContext.parseFieldMatcher().isStrict()) {
|
||||||
return XContentMapValues.nodeBooleanValue(store);
|
return XContentMapValues.nodeBooleanValue(store);
|
||||||
} else {
|
} else {
|
||||||
|
if (BOOLEAN_STRINGS.contains(store) == false) {
|
||||||
|
DEPRECATION_LOGGER.deprecated("Expected a boolean for property [store] but got [{}]", store);
|
||||||
|
}
|
||||||
if ("no".equals(store)) {
|
if ("no".equals(store)) {
|
||||||
return false;
|
return false;
|
||||||
} else if ("yes".equals(store)) {
|
} else if ("yes".equals(store)) {
|
||||||
|
171
core/src/main/java/org/elasticsearch/ingest/IngestStats.java
Normal file
171
core/src/main/java/org/elasticsearch/ingest/IngestStats.java
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.ingest;
|
||||||
|
|
||||||
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
|
import org.elasticsearch.common.io.stream.Writeable;
|
||||||
|
import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class IngestStats implements Writeable<IngestStats>, ToXContent {
|
||||||
|
|
||||||
|
public final static IngestStats PROTO = new IngestStats(null, null);
|
||||||
|
|
||||||
|
private final Stats totalStats;
|
||||||
|
private final Map<String, Stats> statsPerPipeline;
|
||||||
|
|
||||||
|
public IngestStats(Stats totalStats, Map<String, Stats> statsPerPipeline) {
|
||||||
|
this.totalStats = totalStats;
|
||||||
|
this.statsPerPipeline = statsPerPipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The accumulated stats for all pipelines
|
||||||
|
*/
|
||||||
|
public Stats getTotalStats() {
|
||||||
|
return totalStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The stats on a per pipeline basis
|
||||||
|
*/
|
||||||
|
public Map<String, Stats> getStatsPerPipeline() {
|
||||||
|
return statsPerPipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IngestStats readFrom(StreamInput in) throws IOException {
|
||||||
|
Stats totalStats = Stats.PROTO.readFrom(in);
|
||||||
|
totalStats.readFrom(in);
|
||||||
|
int size = in.readVInt();
|
||||||
|
Map<String, Stats> statsPerPipeline = new HashMap<>(size);
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
Stats stats = Stats.PROTO.readFrom(in);
|
||||||
|
statsPerPipeline.put(in.readString(), stats);
|
||||||
|
stats.readFrom(in);
|
||||||
|
}
|
||||||
|
return new IngestStats(totalStats, statsPerPipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
|
totalStats.writeTo(out);
|
||||||
|
out.writeVLong(statsPerPipeline.size());
|
||||||
|
for (Map.Entry<String, Stats> entry : statsPerPipeline.entrySet()) {
|
||||||
|
out.writeString(entry.getKey());
|
||||||
|
entry.getValue().writeTo(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
|
builder.startObject("ingest");
|
||||||
|
builder.startObject("total");
|
||||||
|
totalStats.toXContent(builder, params);
|
||||||
|
builder.endObject();
|
||||||
|
builder.startObject("pipelines");
|
||||||
|
for (Map.Entry<String, Stats> entry : statsPerPipeline.entrySet()) {
|
||||||
|
builder.startObject(entry.getKey());
|
||||||
|
entry.getValue().toXContent(builder, params);
|
||||||
|
builder.endObject();
|
||||||
|
}
|
||||||
|
builder.endObject();
|
||||||
|
builder.endObject();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Stats implements Writeable<Stats>, ToXContent {
|
||||||
|
|
||||||
|
private final static Stats PROTO = new Stats(0, 0, 0, 0);
|
||||||
|
|
||||||
|
private final long ingestCount;
|
||||||
|
private final long ingestTimeInMillis;
|
||||||
|
private final long ingestCurrent;
|
||||||
|
private final long ingestFailedCount;
|
||||||
|
|
||||||
|
public Stats(long ingestCount, long ingestTimeInMillis, long ingestCurrent, long ingestFailedCount) {
|
||||||
|
this.ingestCount = ingestCount;
|
||||||
|
this.ingestTimeInMillis = ingestTimeInMillis;
|
||||||
|
this.ingestCurrent = ingestCurrent;
|
||||||
|
this.ingestFailedCount = ingestFailedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The total number of executed ingest preprocessing operations.
|
||||||
|
*/
|
||||||
|
public long getIngestCount() {
|
||||||
|
return ingestCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return The total time spent of ingest preprocessing in millis.
|
||||||
|
*/
|
||||||
|
public long getIngestTimeInMillis() {
|
||||||
|
return ingestTimeInMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The total number of ingest preprocessing operations currently executing.
|
||||||
|
*/
|
||||||
|
public long getIngestCurrent() {
|
||||||
|
return ingestCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The total number of ingest preprocessing operations that have failed.
|
||||||
|
*/
|
||||||
|
public long getIngestFailedCount() {
|
||||||
|
return ingestFailedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stats readFrom(StreamInput in) throws IOException {
|
||||||
|
long ingestCount = in.readVLong();
|
||||||
|
long ingestTimeInMillis = in.readVLong();
|
||||||
|
long ingestCurrent = in.readVLong();
|
||||||
|
long ingestFailedCount = in.readVLong();
|
||||||
|
return new Stats(ingestCount, ingestTimeInMillis, ingestCurrent, ingestFailedCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
|
out.writeVLong(ingestCount);
|
||||||
|
out.writeVLong(ingestTimeInMillis);
|
||||||
|
out.writeVLong(ingestCurrent);
|
||||||
|
out.writeVLong(ingestFailedCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
|
builder.field("count", ingestCount);
|
||||||
|
builder.timeValueField("time_in_millis", "time", ingestTimeInMillis, TimeUnit.MILLISECONDS);
|
||||||
|
builder.field("current", ingestCurrent);
|
||||||
|
builder.field("failed", ingestFailedCount);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -19,23 +19,36 @@
|
|||||||
|
|
||||||
package org.elasticsearch.ingest;
|
package org.elasticsearch.ingest;
|
||||||
|
|
||||||
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
import org.elasticsearch.action.ActionRequest;
|
import org.elasticsearch.action.ActionRequest;
|
||||||
import org.elasticsearch.action.index.IndexRequest;
|
import org.elasticsearch.action.index.IndexRequest;
|
||||||
|
import org.elasticsearch.cluster.ClusterChangedEvent;
|
||||||
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
|
import org.elasticsearch.cluster.ClusterStateListener;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
|
import org.elasticsearch.common.metrics.CounterMetric;
|
||||||
|
import org.elasticsearch.common.metrics.MeanMetric;
|
||||||
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
|
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
|
||||||
import org.elasticsearch.ingest.core.IngestDocument;
|
import org.elasticsearch.ingest.core.IngestDocument;
|
||||||
import org.elasticsearch.ingest.core.Pipeline;
|
import org.elasticsearch.ingest.core.Pipeline;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class PipelineExecutionService {
|
public class PipelineExecutionService implements ClusterStateListener {
|
||||||
|
|
||||||
private final PipelineStore store;
|
private final PipelineStore store;
|
||||||
private final ThreadPool threadPool;
|
private final ThreadPool threadPool;
|
||||||
|
|
||||||
|
private final StatsHolder totalStats = new StatsHolder();
|
||||||
|
private volatile Map<String, StatsHolder> statsHolderPerPipeline = Collections.emptyMap();
|
||||||
|
|
||||||
public PipelineExecutionService(PipelineStore store, ThreadPool threadPool) {
|
public PipelineExecutionService(PipelineStore store, ThreadPool threadPool) {
|
||||||
this.store = store;
|
this.store = store;
|
||||||
this.threadPool = threadPool;
|
this.threadPool = threadPool;
|
||||||
@ -89,29 +102,85 @@ public class PipelineExecutionService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void innerExecute(IndexRequest indexRequest, Pipeline pipeline) throws Exception {
|
public IngestStats stats() {
|
||||||
String index = indexRequest.index();
|
Map<String, StatsHolder> statsHolderPerPipeline = this.statsHolderPerPipeline;
|
||||||
String type = indexRequest.type();
|
|
||||||
String id = indexRequest.id();
|
|
||||||
String routing = indexRequest.routing();
|
|
||||||
String parent = indexRequest.parent();
|
|
||||||
String timestamp = indexRequest.timestamp();
|
|
||||||
String ttl = indexRequest.ttl() == null ? null : indexRequest.ttl().toString();
|
|
||||||
Map<String, Object> sourceAsMap = indexRequest.sourceAsMap();
|
|
||||||
IngestDocument ingestDocument = new IngestDocument(index, type, id, routing, parent, timestamp, ttl, sourceAsMap);
|
|
||||||
pipeline.execute(ingestDocument);
|
|
||||||
|
|
||||||
Map<IngestDocument.MetaData, String> metadataMap = ingestDocument.extractMetadata();
|
Map<String, IngestStats.Stats> statsPerPipeline = new HashMap<>(statsHolderPerPipeline.size());
|
||||||
//it's fine to set all metadata fields all the time, as ingest document holds their starting values
|
for (Map.Entry<String, StatsHolder> entry : statsHolderPerPipeline.entrySet()) {
|
||||||
//before ingestion, which might also get modified during ingestion.
|
statsPerPipeline.put(entry.getKey(), entry.getValue().createStats());
|
||||||
indexRequest.index(metadataMap.get(IngestDocument.MetaData.INDEX));
|
}
|
||||||
indexRequest.type(metadataMap.get(IngestDocument.MetaData.TYPE));
|
|
||||||
indexRequest.id(metadataMap.get(IngestDocument.MetaData.ID));
|
return new IngestStats(totalStats.createStats(), statsPerPipeline);
|
||||||
indexRequest.routing(metadataMap.get(IngestDocument.MetaData.ROUTING));
|
}
|
||||||
indexRequest.parent(metadataMap.get(IngestDocument.MetaData.PARENT));
|
|
||||||
indexRequest.timestamp(metadataMap.get(IngestDocument.MetaData.TIMESTAMP));
|
@Override
|
||||||
indexRequest.ttl(metadataMap.get(IngestDocument.MetaData.TTL));
|
public void clusterChanged(ClusterChangedEvent event) {
|
||||||
indexRequest.source(ingestDocument.getSourceAndMetadata());
|
IngestMetadata ingestMetadata = event.state().getMetaData().custom(IngestMetadata.TYPE);
|
||||||
|
if (ingestMetadata != null) {
|
||||||
|
updatePipelineStats(ingestMetadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updatePipelineStats(IngestMetadata ingestMetadata) {
|
||||||
|
boolean changed = false;
|
||||||
|
Map<String, StatsHolder> newStatsPerPipeline = new HashMap<>(statsHolderPerPipeline);
|
||||||
|
for (String pipeline : newStatsPerPipeline.keySet()) {
|
||||||
|
if (ingestMetadata.getPipelines().containsKey(pipeline) == false) {
|
||||||
|
newStatsPerPipeline.remove(pipeline);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String pipeline : ingestMetadata.getPipelines().keySet()) {
|
||||||
|
if (newStatsPerPipeline.containsKey(pipeline) == false) {
|
||||||
|
newStatsPerPipeline.put(pipeline, new StatsHolder());
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
statsHolderPerPipeline = Collections.unmodifiableMap(newStatsPerPipeline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void innerExecute(IndexRequest indexRequest, Pipeline pipeline) throws Exception {
|
||||||
|
long startTimeInNanos = System.nanoTime();
|
||||||
|
// the pipeline specific stat holder may not exist and that is fine:
|
||||||
|
// (e.g. the pipeline may have been removed while we're ingesting a document
|
||||||
|
Optional<StatsHolder> pipelineStats = Optional.ofNullable(statsHolderPerPipeline.get(pipeline.getId()));
|
||||||
|
try {
|
||||||
|
totalStats.preIngest();
|
||||||
|
pipelineStats.ifPresent(StatsHolder::preIngest);
|
||||||
|
String index = indexRequest.index();
|
||||||
|
String type = indexRequest.type();
|
||||||
|
String id = indexRequest.id();
|
||||||
|
String routing = indexRequest.routing();
|
||||||
|
String parent = indexRequest.parent();
|
||||||
|
String timestamp = indexRequest.timestamp();
|
||||||
|
String ttl = indexRequest.ttl() == null ? null : indexRequest.ttl().toString();
|
||||||
|
Map<String, Object> sourceAsMap = indexRequest.sourceAsMap();
|
||||||
|
IngestDocument ingestDocument = new IngestDocument(index, type, id, routing, parent, timestamp, ttl, sourceAsMap);
|
||||||
|
pipeline.execute(ingestDocument);
|
||||||
|
|
||||||
|
Map<IngestDocument.MetaData, String> metadataMap = ingestDocument.extractMetadata();
|
||||||
|
//it's fine to set all metadata fields all the time, as ingest document holds their starting values
|
||||||
|
//before ingestion, which might also get modified during ingestion.
|
||||||
|
indexRequest.index(metadataMap.get(IngestDocument.MetaData.INDEX));
|
||||||
|
indexRequest.type(metadataMap.get(IngestDocument.MetaData.TYPE));
|
||||||
|
indexRequest.id(metadataMap.get(IngestDocument.MetaData.ID));
|
||||||
|
indexRequest.routing(metadataMap.get(IngestDocument.MetaData.ROUTING));
|
||||||
|
indexRequest.parent(metadataMap.get(IngestDocument.MetaData.PARENT));
|
||||||
|
indexRequest.timestamp(metadataMap.get(IngestDocument.MetaData.TIMESTAMP));
|
||||||
|
indexRequest.ttl(metadataMap.get(IngestDocument.MetaData.TTL));
|
||||||
|
indexRequest.source(ingestDocument.getSourceAndMetadata());
|
||||||
|
} catch (Exception e) {
|
||||||
|
totalStats.ingestFailed();
|
||||||
|
pipelineStats.ifPresent(StatsHolder::ingestFailed);
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
long ingestTimeInMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTimeInNanos);
|
||||||
|
totalStats.postIngest(ingestTimeInMillis);
|
||||||
|
pipelineStats.ifPresent(statsHolder -> statsHolder.postIngest(ingestTimeInMillis));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pipeline getPipeline(String pipelineId) {
|
private Pipeline getPipeline(String pipelineId) {
|
||||||
@ -121,4 +190,30 @@ public class PipelineExecutionService {
|
|||||||
}
|
}
|
||||||
return pipeline;
|
return pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class StatsHolder {
|
||||||
|
|
||||||
|
private final MeanMetric ingestMetric = new MeanMetric();
|
||||||
|
private final CounterMetric ingestCurrent = new CounterMetric();
|
||||||
|
private final CounterMetric ingestFailed = new CounterMetric();
|
||||||
|
|
||||||
|
void preIngest() {
|
||||||
|
ingestCurrent.inc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void postIngest(long ingestTimeInMillis) {
|
||||||
|
ingestCurrent.dec();
|
||||||
|
ingestMetric.inc(ingestTimeInMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ingestFailed() {
|
||||||
|
ingestFailed.inc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IngestStats.Stats createStats() {
|
||||||
|
return new IngestStats.Stats(ingestMetric.count(), ingestMetric.sum(), ingestCurrent.count(), ingestFailed.count());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,10 @@ import org.elasticsearch.cluster.ClusterService;
|
|||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
import org.elasticsearch.cluster.ClusterStateObserver;
|
import org.elasticsearch.cluster.ClusterStateObserver;
|
||||||
import org.elasticsearch.cluster.MasterNodeChangePredicate;
|
import org.elasticsearch.cluster.MasterNodeChangePredicate;
|
||||||
|
import org.elasticsearch.cluster.NodeConnectionsService;
|
||||||
import org.elasticsearch.cluster.action.index.MappingUpdatedAction;
|
import org.elasticsearch.cluster.action.index.MappingUpdatedAction;
|
||||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||||
|
import org.elasticsearch.cluster.node.DiscoveryNodeService;
|
||||||
import org.elasticsearch.cluster.routing.RoutingService;
|
import org.elasticsearch.cluster.routing.RoutingService;
|
||||||
import org.elasticsearch.cluster.service.InternalClusterService;
|
import org.elasticsearch.cluster.service.InternalClusterService;
|
||||||
import org.elasticsearch.common.StopWatch;
|
import org.elasticsearch.common.StopWatch;
|
||||||
@ -294,6 +296,10 @@ public class Node implements Closeable {
|
|||||||
"node cluster service implementation must inherit from InternalClusterService";
|
"node cluster service implementation must inherit from InternalClusterService";
|
||||||
final InternalClusterService clusterService = (InternalClusterService) injector.getInstance(ClusterService.class);
|
final InternalClusterService clusterService = (InternalClusterService) injector.getInstance(ClusterService.class);
|
||||||
|
|
||||||
|
final NodeConnectionsService nodeConnectionsService = injector.getInstance(NodeConnectionsService.class);
|
||||||
|
nodeConnectionsService.start();
|
||||||
|
clusterService.setNodeConnectionsService(nodeConnectionsService);
|
||||||
|
|
||||||
// TODO hack around circular dependencies problems
|
// TODO hack around circular dependencies problems
|
||||||
injector.getInstance(GatewayAllocator.class).setReallocation(clusterService, injector.getInstance(RoutingService.class));
|
injector.getInstance(GatewayAllocator.class).setReallocation(clusterService, injector.getInstance(RoutingService.class));
|
||||||
|
|
||||||
@ -311,6 +317,15 @@ public class Node implements Closeable {
|
|||||||
// Start the transport service now so the publish address will be added to the local disco node in ClusterService
|
// Start the transport service now so the publish address will be added to the local disco node in ClusterService
|
||||||
TransportService transportService = injector.getInstance(TransportService.class);
|
TransportService transportService = injector.getInstance(TransportService.class);
|
||||||
transportService.start();
|
transportService.start();
|
||||||
|
DiscoveryNode localNode = injector.getInstance(DiscoveryNodeService.class)
|
||||||
|
.buildLocalNode(transportService.boundAddress().publishAddress());
|
||||||
|
|
||||||
|
// TODO: need to find a cleaner way to start/construct a service with some initial parameters,
|
||||||
|
// playing nice with the life cycle interfaces
|
||||||
|
clusterService.setLocalNode(localNode);
|
||||||
|
transportService.setLocalNode(localNode);
|
||||||
|
clusterService.add(transportService.getTaskManager());
|
||||||
|
|
||||||
clusterService.start();
|
clusterService.start();
|
||||||
|
|
||||||
// start after cluster service so the local disco is known
|
// start after cluster service so the local disco is known
|
||||||
@ -392,6 +407,7 @@ public class Node implements Closeable {
|
|||||||
injector.getInstance(RoutingService.class).stop();
|
injector.getInstance(RoutingService.class).stop();
|
||||||
injector.getInstance(ClusterService.class).stop();
|
injector.getInstance(ClusterService.class).stop();
|
||||||
injector.getInstance(Discovery.class).stop();
|
injector.getInstance(Discovery.class).stop();
|
||||||
|
injector.getInstance(NodeConnectionsService.class).stop();
|
||||||
injector.getInstance(MonitorService.class).stop();
|
injector.getInstance(MonitorService.class).stop();
|
||||||
injector.getInstance(GatewayService.class).stop();
|
injector.getInstance(GatewayService.class).stop();
|
||||||
injector.getInstance(SearchService.class).stop();
|
injector.getInstance(SearchService.class).stop();
|
||||||
@ -449,6 +465,8 @@ public class Node implements Closeable {
|
|||||||
toClose.add(injector.getInstance(RoutingService.class));
|
toClose.add(injector.getInstance(RoutingService.class));
|
||||||
toClose.add(() -> stopWatch.stop().start("cluster"));
|
toClose.add(() -> stopWatch.stop().start("cluster"));
|
||||||
toClose.add(injector.getInstance(ClusterService.class));
|
toClose.add(injector.getInstance(ClusterService.class));
|
||||||
|
toClose.add(() -> stopWatch.stop().start("node_connections_service"));
|
||||||
|
toClose.add(injector.getInstance(NodeConnectionsService.class));
|
||||||
toClose.add(() -> stopWatch.stop().start("discovery"));
|
toClose.add(() -> stopWatch.stop().start("discovery"));
|
||||||
toClose.add(injector.getInstance(Discovery.class));
|
toClose.add(injector.getInstance(Discovery.class));
|
||||||
toClose.add(() -> stopWatch.stop().start("monitor"));
|
toClose.add(() -> stopWatch.stop().start("monitor"));
|
||||||
|
@ -90,6 +90,7 @@ public class NodeService extends AbstractComponent implements Closeable {
|
|||||||
this.ingestService = new IngestService(settings, threadPool, processorsRegistryBuilder);
|
this.ingestService = new IngestService(settings, threadPool, processorsRegistryBuilder);
|
||||||
this.settingsFilter = settingsFilter;
|
this.settingsFilter = settingsFilter;
|
||||||
clusterService.add(ingestService.getPipelineStore());
|
clusterService.add(ingestService.getPipelineStore());
|
||||||
|
clusterService.add(ingestService.getPipelineExecutionService());
|
||||||
}
|
}
|
||||||
|
|
||||||
// can not use constructor injection or there will be a circular dependency
|
// can not use constructor injection or there will be a circular dependency
|
||||||
@ -165,13 +166,14 @@ public class NodeService extends AbstractComponent implements Closeable {
|
|||||||
httpServer == null ? null : httpServer.stats(),
|
httpServer == null ? null : httpServer.stats(),
|
||||||
circuitBreakerService.stats(),
|
circuitBreakerService.stats(),
|
||||||
scriptService.stats(),
|
scriptService.stats(),
|
||||||
discovery.stats()
|
discovery.stats(),
|
||||||
|
ingestService.getPipelineExecutionService().stats()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NodeStats stats(CommonStatsFlags indices, boolean os, boolean process, boolean jvm, boolean threadPool,
|
public NodeStats stats(CommonStatsFlags indices, boolean os, boolean process, boolean jvm, boolean threadPool,
|
||||||
boolean fs, boolean transport, boolean http, boolean circuitBreaker,
|
boolean fs, boolean transport, boolean http, boolean circuitBreaker,
|
||||||
boolean script, boolean discoveryStats) {
|
boolean script, boolean discoveryStats, boolean ingest) {
|
||||||
// for indices stats we want to include previous allocated shards stats as well (it will
|
// for indices stats we want to include previous allocated shards stats as well (it will
|
||||||
// only be applied to the sensible ones to use, like refresh/merge/flush/indexing stats)
|
// only be applied to the sensible ones to use, like refresh/merge/flush/indexing stats)
|
||||||
return new NodeStats(discovery.localNode(), System.currentTimeMillis(),
|
return new NodeStats(discovery.localNode(), System.currentTimeMillis(),
|
||||||
@ -185,7 +187,8 @@ public class NodeService extends AbstractComponent implements Closeable {
|
|||||||
http ? (httpServer == null ? null : httpServer.stats()) : null,
|
http ? (httpServer == null ? null : httpServer.stats()) : null,
|
||||||
circuitBreaker ? circuitBreakerService.stats() : null,
|
circuitBreaker ? circuitBreakerService.stats() : null,
|
||||||
script ? scriptService.stats() : null,
|
script ? scriptService.stats() : null,
|
||||||
discoveryStats ? discovery.stats() : null
|
discoveryStats ? discovery.stats() : null,
|
||||||
|
ingest ? ingestService.getPipelineExecutionService().stats() : null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +81,7 @@ public class RestNodesStatsAction extends BaseRestHandler {
|
|||||||
nodesStatsRequest.breaker(metrics.contains("breaker"));
|
nodesStatsRequest.breaker(metrics.contains("breaker"));
|
||||||
nodesStatsRequest.script(metrics.contains("script"));
|
nodesStatsRequest.script(metrics.contains("script"));
|
||||||
nodesStatsRequest.discovery(metrics.contains("discovery"));
|
nodesStatsRequest.discovery(metrics.contains("discovery"));
|
||||||
|
nodesStatsRequest.ingest(metrics.contains("ingest"));
|
||||||
|
|
||||||
// check for index specific metrics
|
// check for index specific metrics
|
||||||
if (metrics.contains("indices")) {
|
if (metrics.contains("indices")) {
|
||||||
@ -113,6 +114,6 @@ public class RestNodesStatsAction extends BaseRestHandler {
|
|||||||
nodesStatsRequest.indices().includeSegmentFileSizes(true);
|
nodesStatsRequest.indices().includeSegmentFileSizes(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
client.admin().cluster().nodesStats(nodesStatsRequest, new RestToXContentListener<NodesStatsResponse>(channel));
|
client.admin().cluster().nodesStats(nodesStatsRequest, new RestToXContentListener<>(channel));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ public class DerivativePipelineAggregator extends PipelineAggregator {
|
|||||||
for (InternalHistogram.Bucket bucket : buckets) {
|
for (InternalHistogram.Bucket bucket : buckets) {
|
||||||
Long thisBucketKey = resolveBucketKeyAsLong(bucket);
|
Long thisBucketKey = resolveBucketKeyAsLong(bucket);
|
||||||
Double thisBucketValue = resolveBucketValue(histo, bucket, bucketsPaths()[0], gapPolicy);
|
Double thisBucketValue = resolveBucketValue(histo, bucket, bucketsPaths()[0], gapPolicy);
|
||||||
if (lastBucketValue != null) {
|
if (lastBucketValue != null && thisBucketValue != null) {
|
||||||
double gradient = thisBucketValue - lastBucketValue;
|
double gradient = thisBucketValue - lastBucketValue;
|
||||||
double xDiff = -1;
|
double xDiff = -1;
|
||||||
if (xAxisUnits != null) {
|
if (xAxisUnits != null) {
|
||||||
|
@ -774,6 +774,32 @@ public class RestoreService extends AbstractComponent implements ClusterStateLis
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if any of the indices to be closed are currently being restored from a snapshot and fail closing if such an index
|
||||||
|
* is found as closing an index that is being restored makes the index unusable (it cannot be recovered).
|
||||||
|
*/
|
||||||
|
public static void checkIndexClosing(ClusterState currentState, Set<String> indices) {
|
||||||
|
RestoreInProgress restore = currentState.custom(RestoreInProgress.TYPE);
|
||||||
|
if (restore != null) {
|
||||||
|
Set<String> indicesToFail = null;
|
||||||
|
for (RestoreInProgress.Entry entry : restore.entries()) {
|
||||||
|
for (ObjectObjectCursor<ShardId, RestoreInProgress.ShardRestoreStatus> shard : entry.shards()) {
|
||||||
|
if (!shard.value.state().completed()) {
|
||||||
|
if (indices.contains(shard.key.getIndexName())) {
|
||||||
|
if (indicesToFail == null) {
|
||||||
|
indicesToFail = new HashSet<>();
|
||||||
|
}
|
||||||
|
indicesToFail.add(shard.key.getIndexName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (indicesToFail != null) {
|
||||||
|
throw new IllegalArgumentException("Cannot close indices that are being restored: " + indicesToFail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds restore completion listener
|
* Adds restore completion listener
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -206,7 +206,7 @@ public class SnapshotsService extends AbstractLifecycleComponent<SnapshotsServic
|
|||||||
// Store newSnapshot here to be processed in clusterStateProcessed
|
// Store newSnapshot here to be processed in clusterStateProcessed
|
||||||
List<String> indices = Arrays.asList(indexNameExpressionResolver.concreteIndices(currentState, request.indicesOptions(), request.indices()));
|
List<String> indices = Arrays.asList(indexNameExpressionResolver.concreteIndices(currentState, request.indicesOptions(), request.indices()));
|
||||||
logger.trace("[{}][{}] creating snapshot for indices [{}]", request.repository(), request.name(), indices);
|
logger.trace("[{}][{}] creating snapshot for indices [{}]", request.repository(), request.name(), indices);
|
||||||
newSnapshot = new SnapshotsInProgress.Entry(snapshotId, request.includeGlobalState(), State.INIT, indices, System.currentTimeMillis(), null);
|
newSnapshot = new SnapshotsInProgress.Entry(snapshotId, request.includeGlobalState(), request.partial(), State.INIT, indices, System.currentTimeMillis(), null);
|
||||||
snapshots = new SnapshotsInProgress(newSnapshot);
|
snapshots = new SnapshotsInProgress(newSnapshot);
|
||||||
} else {
|
} else {
|
||||||
// TODO: What should we do if a snapshot is already running?
|
// TODO: What should we do if a snapshot is already running?
|
||||||
@ -228,7 +228,7 @@ public class SnapshotsService extends AbstractLifecycleComponent<SnapshotsServic
|
|||||||
threadPool.executor(ThreadPool.Names.SNAPSHOT).execute(new Runnable() {
|
threadPool.executor(ThreadPool.Names.SNAPSHOT).execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
beginSnapshot(newState, newSnapshot, request.partial, listener);
|
beginSnapshot(newState, newSnapshot, request.partial(), listener);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1061,6 +1061,63 @@ public class SnapshotsService extends AbstractLifecycleComponent<SnapshotsServic
|
|||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if any of the indices to be deleted are currently being snapshotted. Fail as deleting an index that is being
|
||||||
|
* snapshotted (with partial == false) makes the snapshot fail.
|
||||||
|
*/
|
||||||
|
public static void checkIndexDeletion(ClusterState currentState, Set<String> indices) {
|
||||||
|
Set<String> indicesToFail = indicesToFailForCloseOrDeletion(currentState, indices);
|
||||||
|
if (indicesToFail != null) {
|
||||||
|
throw new IllegalArgumentException("Cannot delete indices that are being snapshotted: " + indicesToFail +
|
||||||
|
". Try again after snapshot finishes or cancel the currently running snapshot.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if any of the indices to be closed are currently being snapshotted. Fail as closing an index that is being
|
||||||
|
* snapshotted (with partial == false) makes the snapshot fail.
|
||||||
|
*/
|
||||||
|
public static void checkIndexClosing(ClusterState currentState, Set<String> indices) {
|
||||||
|
Set<String> indicesToFail = indicesToFailForCloseOrDeletion(currentState, indices);
|
||||||
|
if (indicesToFail != null) {
|
||||||
|
throw new IllegalArgumentException("Cannot close indices that are being snapshotted: " + indicesToFail +
|
||||||
|
". Try again after snapshot finishes or cancel the currently running snapshot.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<String> indicesToFailForCloseOrDeletion(ClusterState currentState, Set<String> indices) {
|
||||||
|
SnapshotsInProgress snapshots = currentState.custom(SnapshotsInProgress.TYPE);
|
||||||
|
Set<String> indicesToFail = null;
|
||||||
|
if (snapshots != null) {
|
||||||
|
for (final SnapshotsInProgress.Entry entry : snapshots.entries()) {
|
||||||
|
if (entry.partial() == false) {
|
||||||
|
if (entry.state() == State.INIT) {
|
||||||
|
for (String index : entry.indices()) {
|
||||||
|
if (indices.contains(index)) {
|
||||||
|
if (indicesToFail == null) {
|
||||||
|
indicesToFail = new HashSet<>();
|
||||||
|
}
|
||||||
|
indicesToFail.add(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (ObjectObjectCursor<ShardId, SnapshotsInProgress.ShardSnapshotStatus> shard : entry.shards()) {
|
||||||
|
if (!shard.value.state().completed()) {
|
||||||
|
if (indices.contains(shard.key.getIndexName())) {
|
||||||
|
if (indicesToFail == null) {
|
||||||
|
indicesToFail = new HashSet<>();
|
||||||
|
}
|
||||||
|
indicesToFail.add(shard.key.getIndexName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return indicesToFail;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds snapshot completion listener
|
* Adds snapshot completion listener
|
||||||
*
|
*
|
||||||
@ -1302,6 +1359,15 @@ public class SnapshotsService extends AbstractLifecycleComponent<SnapshotsServic
|
|||||||
return includeGlobalState;
|
return includeGlobalState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if partial snapshot should be allowed
|
||||||
|
*
|
||||||
|
* @return true if partial snapshot should be allowed
|
||||||
|
*/
|
||||||
|
public boolean partial() {
|
||||||
|
return partial;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns master node timeout
|
* Returns master node timeout
|
||||||
*
|
*
|
||||||
|
@ -33,6 +33,7 @@ import org.elasticsearch.common.inject.Inject;
|
|||||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||||
import org.elasticsearch.common.io.stream.ReleasableBytesStreamOutput;
|
import org.elasticsearch.common.io.stream.ReleasableBytesStreamOutput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
|
import org.elasticsearch.common.lease.Releasable;
|
||||||
import org.elasticsearch.common.lease.Releasables;
|
import org.elasticsearch.common.lease.Releasables;
|
||||||
import org.elasticsearch.common.math.MathUtils;
|
import org.elasticsearch.common.math.MathUtils;
|
||||||
import org.elasticsearch.common.metrics.CounterMetric;
|
import org.elasticsearch.common.metrics.CounterMetric;
|
||||||
@ -943,8 +944,8 @@ public class NettyTransport extends AbstractLifecycleComponent<Transport> implem
|
|||||||
}
|
}
|
||||||
globalLock.readLock().lock();
|
globalLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
connectionLock.acquire(node.id());
|
|
||||||
try {
|
try (Releasable ignored = connectionLock.acquire(node.id())) {
|
||||||
if (!lifecycle.started()) {
|
if (!lifecycle.started()) {
|
||||||
throw new IllegalStateException("can't add nodes to a stopped transport");
|
throw new IllegalStateException("can't add nodes to a stopped transport");
|
||||||
}
|
}
|
||||||
@ -979,8 +980,6 @@ public class NettyTransport extends AbstractLifecycleComponent<Transport> implem
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ConnectTransportException(node, "general node connection failure", e);
|
throw new ConnectTransportException(node, "general node connection failure", e);
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
connectionLock.release(node.id());
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
globalLock.readLock().unlock();
|
globalLock.readLock().unlock();
|
||||||
@ -1103,8 +1102,8 @@ public class NettyTransport extends AbstractLifecycleComponent<Transport> implem
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disconnectFromNode(DiscoveryNode node) {
|
public void disconnectFromNode(DiscoveryNode node) {
|
||||||
connectionLock.acquire(node.id());
|
|
||||||
try {
|
try (Releasable ignored = connectionLock.acquire(node.id())) {
|
||||||
NodeChannels nodeChannels = connectedNodes.remove(node);
|
NodeChannels nodeChannels = connectedNodes.remove(node);
|
||||||
if (nodeChannels != null) {
|
if (nodeChannels != null) {
|
||||||
try {
|
try {
|
||||||
@ -1115,8 +1114,6 @@ public class NettyTransport extends AbstractLifecycleComponent<Transport> implem
|
|||||||
transportServiceAdapter.raiseNodeDisconnected(node);
|
transportServiceAdapter.raiseNodeDisconnected(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
connectionLock.release(node.id());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1128,8 +1125,7 @@ public class NettyTransport extends AbstractLifecycleComponent<Transport> implem
|
|||||||
// check outside of the lock
|
// check outside of the lock
|
||||||
NodeChannels nodeChannels = connectedNodes.get(node);
|
NodeChannels nodeChannels = connectedNodes.get(node);
|
||||||
if (nodeChannels != null && nodeChannels.hasChannel(channel)) {
|
if (nodeChannels != null && nodeChannels.hasChannel(channel)) {
|
||||||
connectionLock.acquire(node.id());
|
try (Releasable ignored = connectionLock.acquire(node.id())) {
|
||||||
try {
|
|
||||||
nodeChannels = connectedNodes.get(node);
|
nodeChannels = connectedNodes.get(node);
|
||||||
// check again within the connection lock, if its still applicable to remove it
|
// check again within the connection lock, if its still applicable to remove it
|
||||||
if (nodeChannels != null && nodeChannels.hasChannel(channel)) {
|
if (nodeChannels != null && nodeChannels.hasChannel(channel)) {
|
||||||
@ -1143,8 +1139,6 @@ public class NettyTransport extends AbstractLifecycleComponent<Transport> implem
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
connectionLock.release(node.id());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -194,7 +194,7 @@ public abstract class TaskManagerTestCase extends ESTestCase {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
transportService.start();
|
transportService.start();
|
||||||
clusterService = new TestClusterService(threadPool, transportService);
|
clusterService = new TestClusterService(threadPool);
|
||||||
clusterService.add(transportService.getTaskManager());
|
clusterService.add(transportService.getTaskManager());
|
||||||
discoveryNode = new DiscoveryNode(name, transportService.boundAddress().publishAddress(), Version.CURRENT);
|
discoveryNode = new DiscoveryNode(name, transportService.boundAddress().publishAddress(), Version.CURRENT);
|
||||||
IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver(settings);
|
IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver(settings);
|
||||||
@ -238,7 +238,7 @@ public abstract class TaskManagerTestCase extends ESTestCase {
|
|||||||
RecordingTaskManagerListener[] listeners = new RecordingTaskManagerListener[nodes.length];
|
RecordingTaskManagerListener[] listeners = new RecordingTaskManagerListener[nodes.length];
|
||||||
for (int i = 0; i < nodes.length; i++) {
|
for (int i = 0; i < nodes.length; i++) {
|
||||||
listeners[i] = new RecordingTaskManagerListener(nodes[i].discoveryNode, actionMasks);
|
listeners[i] = new RecordingTaskManagerListener(nodes[i].discoveryNode, actionMasks);
|
||||||
((MockTaskManager) (nodes[i].clusterService.getTaskManager())).addListener(listeners[i]);
|
((MockTaskManager) (nodes[i].transportService.getTaskManager())).addListener(listeners[i]);
|
||||||
}
|
}
|
||||||
return listeners;
|
return listeners;
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ import org.elasticsearch.test.tasks.MockTaskManager;
|
|||||||
import org.elasticsearch.test.tasks.MockTaskManagerListener;
|
import org.elasticsearch.test.tasks.MockTaskManagerListener;
|
||||||
import org.elasticsearch.test.transport.MockTransportService;
|
import org.elasticsearch.test.transport.MockTransportService;
|
||||||
import org.elasticsearch.transport.ReceiveTimeoutTransportException;
|
import org.elasticsearch.transport.ReceiveTimeoutTransportException;
|
||||||
|
import org.elasticsearch.transport.TransportService;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -263,8 +264,8 @@ public class TasksIT extends ESIntegTestCase {
|
|||||||
ReentrantLock taskFinishLock = new ReentrantLock();
|
ReentrantLock taskFinishLock = new ReentrantLock();
|
||||||
taskFinishLock.lock();
|
taskFinishLock.lock();
|
||||||
CountDownLatch taskRegistered = new CountDownLatch(1);
|
CountDownLatch taskRegistered = new CountDownLatch(1);
|
||||||
for (ClusterService clusterService : internalCluster().getInstances(ClusterService.class)) {
|
for (TransportService transportService : internalCluster().getInstances(TransportService.class)) {
|
||||||
((MockTaskManager)clusterService.getTaskManager()).addListener(new MockTaskManagerListener() {
|
((MockTaskManager) transportService.getTaskManager()).addListener(new MockTaskManagerListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onTaskRegistered(Task task) {
|
public void onTaskRegistered(Task task) {
|
||||||
if (task.getAction().startsWith(IndexAction.NAME)) {
|
if (task.getAction().startsWith(IndexAction.NAME)) {
|
||||||
@ -408,7 +409,7 @@ public class TasksIT extends ESIntegTestCase {
|
|||||||
@Override
|
@Override
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
for (Map.Entry<Tuple<String, String>, RecordingTaskManagerListener> entry : listeners.entrySet()) {
|
for (Map.Entry<Tuple<String, String>, RecordingTaskManagerListener> entry : listeners.entrySet()) {
|
||||||
((MockTaskManager)internalCluster().getInstance(ClusterService.class, entry.getKey().v1()).getTaskManager()).removeListener(entry.getValue());
|
((MockTaskManager) internalCluster().getInstance(TransportService.class, entry.getKey().v1()).getTaskManager()).removeListener(entry.getValue());
|
||||||
}
|
}
|
||||||
listeners.clear();
|
listeners.clear();
|
||||||
super.tearDown();
|
super.tearDown();
|
||||||
@ -418,10 +419,10 @@ public class TasksIT extends ESIntegTestCase {
|
|||||||
* Registers recording task event listeners with the given action mask on all nodes
|
* Registers recording task event listeners with the given action mask on all nodes
|
||||||
*/
|
*/
|
||||||
private void registerTaskManageListeners(String actionMasks) {
|
private void registerTaskManageListeners(String actionMasks) {
|
||||||
for (ClusterService clusterService : internalCluster().getInstances(ClusterService.class)) {
|
for (String nodeName : internalCluster().getNodeNames()) {
|
||||||
DiscoveryNode node = clusterService.localNode();
|
DiscoveryNode node = internalCluster().getInstance(ClusterService.class, nodeName).localNode();
|
||||||
RecordingTaskManagerListener listener = new RecordingTaskManagerListener(node, Strings.splitStringToArray(actionMasks, ','));
|
RecordingTaskManagerListener listener = new RecordingTaskManagerListener(node, Strings.splitStringToArray(actionMasks, ','));
|
||||||
((MockTaskManager)clusterService.getTaskManager()).addListener(listener);
|
((MockTaskManager) internalCluster().getInstance(TransportService.class, nodeName).getTaskManager()).addListener(listener);
|
||||||
RecordingTaskManagerListener oldListener = listeners.put(new Tuple<>(node.name(), actionMasks), listener);
|
RecordingTaskManagerListener oldListener = listeners.put(new Tuple<>(node.name(), actionMasks), listener);
|
||||||
assertNull(oldListener);
|
assertNull(oldListener);
|
||||||
}
|
}
|
||||||
|
@ -48,19 +48,7 @@ public class ClusterModuleTests extends ModuleTestCase {
|
|||||||
|
|
||||||
static class FakeShardsAllocator implements ShardsAllocator {
|
static class FakeShardsAllocator implements ShardsAllocator {
|
||||||
@Override
|
@Override
|
||||||
public void applyStartedShards(StartedRerouteAllocation allocation) {}
|
public boolean allocate(RoutingAllocation allocation) {
|
||||||
@Override
|
|
||||||
public void applyFailedShards(FailedRerouteAllocation allocation) {}
|
|
||||||
@Override
|
|
||||||
public boolean allocateUnassigned(RoutingAllocation allocation) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public boolean rebalance(RoutingAllocation allocation) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public boolean moveShards(RoutingAllocation allocation) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,16 +18,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.elasticsearch.cluster;
|
package org.elasticsearch.cluster;
|
||||||
|
|
||||||
import org.apache.log4j.Level;
|
|
||||||
import org.apache.log4j.Logger;
|
|
||||||
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
|
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
|
||||||
import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksResponse;
|
import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksResponse;
|
||||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||||
import org.elasticsearch.cluster.service.InternalClusterService;
|
|
||||||
import org.elasticsearch.cluster.service.PendingClusterTask;
|
import org.elasticsearch.cluster.service.PendingClusterTask;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.Priority;
|
import org.elasticsearch.common.Priority;
|
||||||
import org.elasticsearch.common.collect.Tuple;
|
|
||||||
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||||
import org.elasticsearch.common.component.LifecycleComponent;
|
import org.elasticsearch.common.component.LifecycleComponent;
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
@ -35,38 +31,24 @@ import org.elasticsearch.common.inject.Singleton;
|
|||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.discovery.zen.ZenDiscovery;
|
import org.elasticsearch.discovery.zen.ZenDiscovery;
|
||||||
import org.elasticsearch.node.Node;
|
|
||||||
import org.elasticsearch.plugins.Plugin;
|
import org.elasticsearch.plugins.Plugin;
|
||||||
import org.elasticsearch.test.ESIntegTestCase;
|
import org.elasticsearch.test.ESIntegTestCase;
|
||||||
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
|
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
|
||||||
import org.elasticsearch.test.ESIntegTestCase.Scope;
|
import org.elasticsearch.test.ESIntegTestCase.Scope;
|
||||||
import org.elasticsearch.test.InternalTestCluster;
|
|
||||||
import org.elasticsearch.test.MockLogAppender;
|
|
||||||
import org.elasticsearch.test.junit.annotations.TestLogging;
|
import org.elasticsearch.test.junit.annotations.TestLogging;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.BrokenBarrierException;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.CyclicBarrier;
|
|
||||||
import java.util.concurrent.Semaphore;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import static org.elasticsearch.common.settings.Settings.settingsBuilder;
|
import static org.elasticsearch.common.settings.Settings.settingsBuilder;
|
||||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
|
||||||
import static org.hamcrest.Matchers.empty;
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.greaterThan;
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||||
@ -85,74 +67,6 @@ public class ClusterServiceIT extends ESIntegTestCase {
|
|||||||
return pluginList(TestPlugin.class);
|
return pluginList(TestPlugin.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testTimeoutUpdateTask() throws Exception {
|
|
||||||
Settings settings = settingsBuilder()
|
|
||||||
.put("discovery.type", "local")
|
|
||||||
.build();
|
|
||||||
internalCluster().startNode(settings);
|
|
||||||
ClusterService clusterService1 = internalCluster().getInstance(ClusterService.class);
|
|
||||||
final CountDownLatch block = new CountDownLatch(1);
|
|
||||||
clusterService1.submitStateUpdateTask("test1", new ClusterStateUpdateTask() {
|
|
||||||
@Override
|
|
||||||
public ClusterState execute(ClusterState currentState) {
|
|
||||||
try {
|
|
||||||
block.await();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
return currentState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
throw new RuntimeException(t);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final CountDownLatch timedOut = new CountDownLatch(1);
|
|
||||||
final AtomicBoolean executeCalled = new AtomicBoolean();
|
|
||||||
clusterService1.submitStateUpdateTask("test2", new ClusterStateUpdateTask() {
|
|
||||||
@Override
|
|
||||||
public TimeValue timeout() {
|
|
||||||
return TimeValue.timeValueMillis(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
timedOut.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClusterState execute(ClusterState currentState) {
|
|
||||||
executeCalled.set(true);
|
|
||||||
return currentState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
timedOut.await();
|
|
||||||
block.countDown();
|
|
||||||
final CountDownLatch allProcessed = new CountDownLatch(1);
|
|
||||||
clusterService1.submitStateUpdateTask("test3", new ClusterStateUpdateTask() {
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
throw new RuntimeException(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClusterState execute(ClusterState currentState) {
|
|
||||||
allProcessed.countDown();
|
|
||||||
return currentState;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
allProcessed.await(); // executed another task to double check that execute on the timed out update task is not called...
|
|
||||||
assertThat(executeCalled.get(), equalTo(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testAckedUpdateTask() throws Exception {
|
public void testAckedUpdateTask() throws Exception {
|
||||||
Settings settings = settingsBuilder()
|
Settings settings = settingsBuilder()
|
||||||
.put("discovery.type", "local")
|
.put("discovery.type", "local")
|
||||||
@ -299,63 +213,6 @@ public class ClusterServiceIT extends ESIntegTestCase {
|
|||||||
|
|
||||||
assertThat(processedLatch.await(1, TimeUnit.SECONDS), equalTo(true));
|
assertThat(processedLatch.await(1, TimeUnit.SECONDS), equalTo(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMasterAwareExecution() throws Exception {
|
|
||||||
Settings settings = settingsBuilder()
|
|
||||||
.put("discovery.type", "local")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
InternalTestCluster.Async<String> master = internalCluster().startNodeAsync(settings);
|
|
||||||
InternalTestCluster.Async<String> nonMaster = internalCluster().startNodeAsync(settingsBuilder().put(settings).put(Node.NODE_MASTER_SETTING.getKey(), false).build());
|
|
||||||
master.get();
|
|
||||||
ensureGreen(); // make sure we have a cluster
|
|
||||||
|
|
||||||
ClusterService clusterService = internalCluster().getInstance(ClusterService.class, nonMaster.get());
|
|
||||||
|
|
||||||
final boolean[] taskFailed = {false};
|
|
||||||
final CountDownLatch latch1 = new CountDownLatch(1);
|
|
||||||
clusterService.submitStateUpdateTask("test", new ClusterStateUpdateTask() {
|
|
||||||
@Override
|
|
||||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
|
||||||
latch1.countDown();
|
|
||||||
return currentState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
taskFailed[0] = true;
|
|
||||||
latch1.countDown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
latch1.await();
|
|
||||||
assertTrue("cluster state update task was executed on a non-master", taskFailed[0]);
|
|
||||||
|
|
||||||
taskFailed[0] = true;
|
|
||||||
final CountDownLatch latch2 = new CountDownLatch(1);
|
|
||||||
clusterService.submitStateUpdateTask("test", new ClusterStateUpdateTask() {
|
|
||||||
@Override
|
|
||||||
public boolean runOnlyOnMaster() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
|
||||||
taskFailed[0] = false;
|
|
||||||
latch2.countDown();
|
|
||||||
return currentState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
taskFailed[0] = true;
|
|
||||||
latch2.countDown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
latch2.await();
|
|
||||||
assertFalse("non-master cluster state update task was not executed", taskFailed[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testAckedUpdateTaskNoAckExpected() throws Exception {
|
public void testAckedUpdateTaskNoAckExpected() throws Exception {
|
||||||
Settings settings = settingsBuilder()
|
Settings settings = settingsBuilder()
|
||||||
.put("discovery.type", "local")
|
.put("discovery.type", "local")
|
||||||
@ -715,571 +572,6 @@ public class ClusterServiceIT extends ESIntegTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Note, this test can only work as long as we have a single thread executor executing the state update tasks!
|
|
||||||
*/
|
|
||||||
public void testPrioritizedTasks() throws Exception {
|
|
||||||
Settings settings = settingsBuilder()
|
|
||||||
.put("discovery.type", "local")
|
|
||||||
.build();
|
|
||||||
internalCluster().startNode(settings);
|
|
||||||
ClusterService clusterService = internalCluster().getInstance(ClusterService.class);
|
|
||||||
BlockingTask block = new BlockingTask(Priority.IMMEDIATE);
|
|
||||||
clusterService.submitStateUpdateTask("test", block);
|
|
||||||
int taskCount = randomIntBetween(5, 20);
|
|
||||||
Priority[] priorities = Priority.values();
|
|
||||||
|
|
||||||
// will hold all the tasks in the order in which they were executed
|
|
||||||
List<PrioritizedTask> tasks = new ArrayList<>(taskCount);
|
|
||||||
CountDownLatch latch = new CountDownLatch(taskCount);
|
|
||||||
for (int i = 0; i < taskCount; i++) {
|
|
||||||
Priority priority = priorities[randomIntBetween(0, priorities.length - 1)];
|
|
||||||
clusterService.submitStateUpdateTask("test", new PrioritizedTask(priority, latch, tasks));
|
|
||||||
}
|
|
||||||
|
|
||||||
block.release();
|
|
||||||
latch.await();
|
|
||||||
|
|
||||||
Priority prevPriority = null;
|
|
||||||
for (PrioritizedTask task : tasks) {
|
|
||||||
if (prevPriority == null) {
|
|
||||||
prevPriority = task.priority();
|
|
||||||
} else {
|
|
||||||
assertThat(task.priority().sameOrAfter(prevPriority), is(true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test that a listener throwing an exception while handling a
|
|
||||||
* notification does not prevent publication notification to the
|
|
||||||
* executor
|
|
||||||
*/
|
|
||||||
public void testClusterStateTaskListenerThrowingExceptionIsOkay() throws InterruptedException {
|
|
||||||
Settings settings = settingsBuilder()
|
|
||||||
.put("discovery.type", "local")
|
|
||||||
.build();
|
|
||||||
internalCluster().startNode(settings);
|
|
||||||
ClusterService clusterService = internalCluster().getInstance(ClusterService.class);
|
|
||||||
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
AtomicBoolean published = new AtomicBoolean();
|
|
||||||
|
|
||||||
clusterService.submitStateUpdateTask(
|
|
||||||
"testClusterStateTaskListenerThrowingExceptionIsOkay",
|
|
||||||
new Object(),
|
|
||||||
ClusterStateTaskConfig.build(Priority.NORMAL),
|
|
||||||
new ClusterStateTaskExecutor<Object>() {
|
|
||||||
@Override
|
|
||||||
public boolean runOnlyOnMaster() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BatchResult<Object> execute(ClusterState currentState, List<Object> tasks) throws Exception {
|
|
||||||
ClusterState newClusterState = ClusterState.builder(currentState).build();
|
|
||||||
return BatchResult.builder().successes(tasks).build(newClusterState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clusterStatePublished(ClusterState newClusterState) {
|
|
||||||
published.set(true);
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new ClusterStateTaskListener() {
|
|
||||||
@Override
|
|
||||||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
|
||||||
throw new IllegalStateException(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
latch.await();
|
|
||||||
assertTrue(published.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
// test that for a single thread, tasks are executed in the order
|
|
||||||
// that they are submitted
|
|
||||||
public void testClusterStateUpdateTasksAreExecutedInOrder() throws BrokenBarrierException, InterruptedException {
|
|
||||||
Settings settings = settingsBuilder()
|
|
||||||
.put("discovery.type", "local")
|
|
||||||
.build();
|
|
||||||
internalCluster().startNode(settings);
|
|
||||||
ClusterService clusterService = internalCluster().getInstance(ClusterService.class);
|
|
||||||
|
|
||||||
class TaskExecutor implements ClusterStateTaskExecutor<Integer> {
|
|
||||||
List<Integer> tasks = new ArrayList<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BatchResult<Integer> execute(ClusterState currentState, List<Integer> tasks) throws Exception {
|
|
||||||
this.tasks.addAll(tasks);
|
|
||||||
return BatchResult.<Integer>builder().successes(tasks).build(ClusterState.builder(currentState).build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean runOnlyOnMaster() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int numberOfThreads = randomIntBetween(2, 8);
|
|
||||||
TaskExecutor[] executors = new TaskExecutor[numberOfThreads];
|
|
||||||
for (int i = 0; i < numberOfThreads; i++) {
|
|
||||||
executors[i] = new TaskExecutor();
|
|
||||||
}
|
|
||||||
|
|
||||||
int tasksSubmittedPerThread = randomIntBetween(2, 1024);
|
|
||||||
|
|
||||||
CopyOnWriteArrayList<Tuple<String, Throwable>> failures = new CopyOnWriteArrayList<>();
|
|
||||||
CountDownLatch updateLatch = new CountDownLatch(numberOfThreads * tasksSubmittedPerThread);
|
|
||||||
|
|
||||||
ClusterStateTaskListener listener = new ClusterStateTaskListener() {
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
logger.error("unexpected failure: [{}]", t, source);
|
|
||||||
failures.add(new Tuple<>(source, t));
|
|
||||||
updateLatch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
|
||||||
updateLatch.countDown();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
CyclicBarrier barrier = new CyclicBarrier(1 + numberOfThreads);
|
|
||||||
|
|
||||||
for (int i = 0; i < numberOfThreads; i++) {
|
|
||||||
final int index = i;
|
|
||||||
Thread thread = new Thread(() -> {
|
|
||||||
try {
|
|
||||||
barrier.await();
|
|
||||||
for (int j = 0; j < tasksSubmittedPerThread; j++) {
|
|
||||||
clusterService.submitStateUpdateTask("[" + index + "][" + j + "]", j, ClusterStateTaskConfig.build(randomFrom(Priority.values())), executors[index], listener);
|
|
||||||
}
|
|
||||||
barrier.await();
|
|
||||||
} catch (InterruptedException | BrokenBarrierException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for all threads to be ready
|
|
||||||
barrier.await();
|
|
||||||
// wait for all threads to finish
|
|
||||||
barrier.await();
|
|
||||||
|
|
||||||
updateLatch.await();
|
|
||||||
|
|
||||||
assertThat(failures, empty());
|
|
||||||
|
|
||||||
for (int i = 0; i < numberOfThreads; i++) {
|
|
||||||
assertEquals(tasksSubmittedPerThread, executors[i].tasks.size());
|
|
||||||
for (int j = 0; j < tasksSubmittedPerThread; j++) {
|
|
||||||
assertNotNull(executors[i].tasks.get(j));
|
|
||||||
assertEquals("cluster state update task executed out of order", j, (int)executors[i].tasks.get(j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testClusterStateBatchedUpdates() throws BrokenBarrierException, InterruptedException {
|
|
||||||
Settings settings = settingsBuilder()
|
|
||||||
.put("discovery.type", "local")
|
|
||||||
.build();
|
|
||||||
internalCluster().startNode(settings);
|
|
||||||
ClusterService clusterService = internalCluster().getInstance(ClusterService.class);
|
|
||||||
|
|
||||||
AtomicInteger counter = new AtomicInteger();
|
|
||||||
class Task {
|
|
||||||
private AtomicBoolean state = new AtomicBoolean();
|
|
||||||
|
|
||||||
public void execute() {
|
|
||||||
if (!state.compareAndSet(false, true)) {
|
|
||||||
throw new IllegalStateException();
|
|
||||||
} else {
|
|
||||||
counter.incrementAndGet();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int numberOfThreads = randomIntBetween(2, 8);
|
|
||||||
int tasksSubmittedPerThread = randomIntBetween(1, 1024);
|
|
||||||
int numberOfExecutors = Math.max(1, numberOfThreads / 4);
|
|
||||||
final Semaphore semaphore = new Semaphore(numberOfExecutors);
|
|
||||||
|
|
||||||
class TaskExecutor implements ClusterStateTaskExecutor<Task> {
|
|
||||||
private AtomicInteger counter = new AtomicInteger();
|
|
||||||
private AtomicInteger batches = new AtomicInteger();
|
|
||||||
private AtomicInteger published = new AtomicInteger();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BatchResult<Task> execute(ClusterState currentState, List<Task> tasks) throws Exception {
|
|
||||||
tasks.forEach(task -> task.execute());
|
|
||||||
counter.addAndGet(tasks.size());
|
|
||||||
ClusterState maybeUpdatedClusterState = currentState;
|
|
||||||
if (randomBoolean()) {
|
|
||||||
maybeUpdatedClusterState = ClusterState.builder(currentState).build();
|
|
||||||
batches.incrementAndGet();
|
|
||||||
semaphore.acquire();
|
|
||||||
}
|
|
||||||
return BatchResult.<Task>builder().successes(tasks).build(maybeUpdatedClusterState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean runOnlyOnMaster() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clusterStatePublished(ClusterState newClusterState) {
|
|
||||||
published.incrementAndGet();
|
|
||||||
semaphore.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ConcurrentMap<String, AtomicInteger> counters = new ConcurrentHashMap<>();
|
|
||||||
CountDownLatch updateLatch = new CountDownLatch(numberOfThreads * tasksSubmittedPerThread);
|
|
||||||
ClusterStateTaskListener listener = new ClusterStateTaskListener() {
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
assert false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
|
||||||
counters.computeIfAbsent(source, key -> new AtomicInteger()).incrementAndGet();
|
|
||||||
updateLatch.countDown();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
List<TaskExecutor> executors = new ArrayList<>();
|
|
||||||
for (int i = 0; i < numberOfExecutors; i++) {
|
|
||||||
executors.add(new TaskExecutor());
|
|
||||||
}
|
|
||||||
|
|
||||||
// randomly assign tasks to executors
|
|
||||||
List<TaskExecutor> assignments = new ArrayList<>();
|
|
||||||
for (int i = 0; i < numberOfThreads; i++) {
|
|
||||||
for (int j = 0; j < tasksSubmittedPerThread; j++) {
|
|
||||||
assignments.add(randomFrom(executors));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<TaskExecutor, Integer> counts = new HashMap<>();
|
|
||||||
for (TaskExecutor executor : assignments) {
|
|
||||||
counts.merge(executor, 1, (previous, one) -> previous + one);
|
|
||||||
}
|
|
||||||
|
|
||||||
CyclicBarrier barrier = new CyclicBarrier(1 + numberOfThreads);
|
|
||||||
for (int i = 0; i < numberOfThreads; i++) {
|
|
||||||
final int index = i;
|
|
||||||
Thread thread = new Thread(() -> {
|
|
||||||
try {
|
|
||||||
barrier.await();
|
|
||||||
for (int j = 0; j < tasksSubmittedPerThread; j++) {
|
|
||||||
ClusterStateTaskExecutor<Task> executor = assignments.get(index * tasksSubmittedPerThread + j);
|
|
||||||
clusterService.submitStateUpdateTask(
|
|
||||||
Thread.currentThread().getName(),
|
|
||||||
new Task(),
|
|
||||||
ClusterStateTaskConfig.build(randomFrom(Priority.values())),
|
|
||||||
executor,
|
|
||||||
listener);
|
|
||||||
}
|
|
||||||
barrier.await();
|
|
||||||
} catch (BrokenBarrierException | InterruptedException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for all threads to be ready
|
|
||||||
barrier.await();
|
|
||||||
// wait for all threads to finish
|
|
||||||
barrier.await();
|
|
||||||
|
|
||||||
// wait until all the cluster state updates have been processed
|
|
||||||
updateLatch.await();
|
|
||||||
// and until all of the publication callbacks have completed
|
|
||||||
semaphore.acquire(numberOfExecutors);
|
|
||||||
|
|
||||||
// assert the number of executed tasks is correct
|
|
||||||
assertEquals(numberOfThreads * tasksSubmittedPerThread, counter.get());
|
|
||||||
|
|
||||||
// assert each executor executed the correct number of tasks
|
|
||||||
for (TaskExecutor executor : executors) {
|
|
||||||
if (counts.containsKey(executor)) {
|
|
||||||
assertEquals((int) counts.get(executor), executor.counter.get());
|
|
||||||
assertEquals(executor.batches.get(), executor.published.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// assert the correct number of clusterStateProcessed events were triggered
|
|
||||||
for (Map.Entry<String, AtomicInteger> entry : counters.entrySet()) {
|
|
||||||
assertEquals(entry.getValue().get(), tasksSubmittedPerThread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestLogging("cluster:TRACE") // To ensure that we log cluster state events on TRACE level
|
|
||||||
public void testClusterStateUpdateLogging() throws Exception {
|
|
||||||
Settings settings = settingsBuilder()
|
|
||||||
.put("discovery.type", "local")
|
|
||||||
.build();
|
|
||||||
internalCluster().startNode(settings);
|
|
||||||
ClusterService clusterService1 = internalCluster().getInstance(ClusterService.class);
|
|
||||||
MockLogAppender mockAppender = new MockLogAppender();
|
|
||||||
mockAppender.addExpectation(new MockLogAppender.SeenEventExpectation("test1", "cluster.service", Level.DEBUG, "*processing [test1]: took * no change in cluster_state"));
|
|
||||||
mockAppender.addExpectation(new MockLogAppender.SeenEventExpectation("test2", "cluster.service", Level.TRACE, "*failed to execute cluster state update in *"));
|
|
||||||
mockAppender.addExpectation(new MockLogAppender.SeenEventExpectation("test3", "cluster.service", Level.DEBUG, "*processing [test3]: took * done applying updated cluster_state (version: *, uuid: *)"));
|
|
||||||
|
|
||||||
Logger rootLogger = Logger.getRootLogger();
|
|
||||||
rootLogger.addAppender(mockAppender);
|
|
||||||
try {
|
|
||||||
final CountDownLatch latch = new CountDownLatch(4);
|
|
||||||
clusterService1.submitStateUpdateTask("test1", new ClusterStateUpdateTask() {
|
|
||||||
@Override
|
|
||||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
|
||||||
return currentState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
clusterService1.submitStateUpdateTask("test2", new ClusterStateUpdateTask() {
|
|
||||||
@Override
|
|
||||||
public ClusterState execute(ClusterState currentState) {
|
|
||||||
throw new IllegalArgumentException("Testing handling of exceptions in the cluster state task");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
clusterService1.submitStateUpdateTask("test3", new ClusterStateUpdateTask() {
|
|
||||||
@Override
|
|
||||||
public ClusterState execute(ClusterState currentState) {
|
|
||||||
return ClusterState.builder(currentState).incrementVersion().build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Additional update task to make sure all previous logging made it to the logger
|
|
||||||
// We don't check logging for this on since there is no guarantee that it will occur before our check
|
|
||||||
clusterService1.submitStateUpdateTask("test4", new ClusterStateUpdateTask() {
|
|
||||||
@Override
|
|
||||||
public ClusterState execute(ClusterState currentState) {
|
|
||||||
return currentState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assertThat(latch.await(1, TimeUnit.SECONDS), equalTo(true));
|
|
||||||
} finally {
|
|
||||||
rootLogger.removeAppender(mockAppender);
|
|
||||||
}
|
|
||||||
mockAppender.assertAllExpectationsMatched();
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestLogging("cluster:WARN") // To ensure that we log cluster state events on WARN level
|
|
||||||
public void testLongClusterStateUpdateLogging() throws Exception {
|
|
||||||
Settings settings = settingsBuilder()
|
|
||||||
.put("discovery.type", "local")
|
|
||||||
.put(InternalClusterService.CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING.getKey(), "10s")
|
|
||||||
.build();
|
|
||||||
internalCluster().startNode(settings);
|
|
||||||
ClusterService clusterService1 = internalCluster().getInstance(ClusterService.class);
|
|
||||||
MockLogAppender mockAppender = new MockLogAppender();
|
|
||||||
mockAppender.addExpectation(new MockLogAppender.UnseenEventExpectation("test1 shouldn't see because setting is too low", "cluster.service", Level.WARN, "*cluster state update task [test1] took * above the warn threshold of *"));
|
|
||||||
mockAppender.addExpectation(new MockLogAppender.SeenEventExpectation("test2", "cluster.service", Level.WARN, "*cluster state update task [test2] took * above the warn threshold of 10ms"));
|
|
||||||
mockAppender.addExpectation(new MockLogAppender.SeenEventExpectation("test3", "cluster.service", Level.WARN, "*cluster state update task [test3] took * above the warn threshold of 10ms"));
|
|
||||||
mockAppender.addExpectation(new MockLogAppender.SeenEventExpectation("test4", "cluster.service", Level.WARN, "*cluster state update task [test4] took * above the warn threshold of 10ms"));
|
|
||||||
|
|
||||||
Logger rootLogger = Logger.getRootLogger();
|
|
||||||
rootLogger.addAppender(mockAppender);
|
|
||||||
try {
|
|
||||||
final CountDownLatch latch = new CountDownLatch(5);
|
|
||||||
final CountDownLatch processedFirstTask = new CountDownLatch(1);
|
|
||||||
clusterService1.submitStateUpdateTask("test1", new ClusterStateUpdateTask() {
|
|
||||||
@Override
|
|
||||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
|
||||||
Thread.sleep(100);
|
|
||||||
return currentState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
|
||||||
latch.countDown();
|
|
||||||
processedFirstTask.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
processedFirstTask.await(1, TimeUnit.SECONDS);
|
|
||||||
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settingsBuilder()
|
|
||||||
.put(InternalClusterService.CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING.getKey(), "10ms")));
|
|
||||||
|
|
||||||
clusterService1.submitStateUpdateTask("test2", new ClusterStateUpdateTask() {
|
|
||||||
@Override
|
|
||||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
|
||||||
Thread.sleep(100);
|
|
||||||
throw new IllegalArgumentException("Testing handling of exceptions in the cluster state task");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
clusterService1.submitStateUpdateTask("test3", new ClusterStateUpdateTask() {
|
|
||||||
@Override
|
|
||||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
|
||||||
Thread.sleep(100);
|
|
||||||
return ClusterState.builder(currentState).incrementVersion().build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
clusterService1.submitStateUpdateTask("test4", new ClusterStateUpdateTask() {
|
|
||||||
@Override
|
|
||||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
|
||||||
Thread.sleep(100);
|
|
||||||
return currentState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Additional update task to make sure all previous logging made it to the logger
|
|
||||||
// We don't check logging for this on since there is no guarantee that it will occur before our check
|
|
||||||
clusterService1.submitStateUpdateTask("test5", new ClusterStateUpdateTask() {
|
|
||||||
@Override
|
|
||||||
public ClusterState execute(ClusterState currentState) {
|
|
||||||
return currentState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assertThat(latch.await(5, TimeUnit.SECONDS), equalTo(true));
|
|
||||||
} finally {
|
|
||||||
rootLogger.removeAppender(mockAppender);
|
|
||||||
}
|
|
||||||
mockAppender.assertAllExpectationsMatched();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class BlockingTask extends ClusterStateUpdateTask {
|
|
||||||
private final CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
|
|
||||||
public BlockingTask(Priority priority) {
|
|
||||||
super(priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
|
||||||
latch.await();
|
|
||||||
return currentState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void release() {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class PrioritizedTask extends ClusterStateUpdateTask {
|
|
||||||
|
|
||||||
private final CountDownLatch latch;
|
|
||||||
private final List<PrioritizedTask> tasks;
|
|
||||||
|
|
||||||
private PrioritizedTask(Priority priority, CountDownLatch latch, List<PrioritizedTask> tasks) {
|
|
||||||
super(priority);
|
|
||||||
this.latch = latch;
|
|
||||||
this.tasks = tasks;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
|
||||||
tasks.add(this);
|
|
||||||
latch.countDown();
|
|
||||||
return currentState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(String source, Throwable t) {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TestPlugin extends Plugin {
|
public static class TestPlugin extends Plugin {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -639,6 +639,7 @@ public class ClusterStateDiffIT extends ESIntegTestCase {
|
|||||||
return new SnapshotsInProgress(new SnapshotsInProgress.Entry(
|
return new SnapshotsInProgress(new SnapshotsInProgress.Entry(
|
||||||
new SnapshotId(randomName("repo"), randomName("snap")),
|
new SnapshotId(randomName("repo"), randomName("snap")),
|
||||||
randomBoolean(),
|
randomBoolean(),
|
||||||
|
randomBoolean(),
|
||||||
SnapshotsInProgress.State.fromValue((byte) randomIntBetween(0, 6)),
|
SnapshotsInProgress.State.fromValue((byte) randomIntBetween(0, 6)),
|
||||||
Collections.<String>emptyList(),
|
Collections.<String>emptyList(),
|
||||||
Math.abs(randomLong()),
|
Math.abs(randomLong()),
|
||||||
|
@ -143,11 +143,11 @@ public class DiskUsageTests extends ESTestCase {
|
|||||||
};
|
};
|
||||||
NodeStats[] nodeStats = new NodeStats[] {
|
NodeStats[] nodeStats = new NodeStats[] {
|
||||||
new NodeStats(new DiscoveryNode("node_1", DummyTransportAddress.INSTANCE, Version.CURRENT), 0,
|
new NodeStats(new DiscoveryNode("node_1", DummyTransportAddress.INSTANCE, Version.CURRENT), 0,
|
||||||
null,null,null,null,null,new FsInfo(0, node1FSInfo), null,null,null,null,null),
|
null,null,null,null,null,new FsInfo(0, node1FSInfo), null,null,null,null,null, null),
|
||||||
new NodeStats(new DiscoveryNode("node_2", DummyTransportAddress.INSTANCE, Version.CURRENT), 0,
|
new NodeStats(new DiscoveryNode("node_2", DummyTransportAddress.INSTANCE, Version.CURRENT), 0,
|
||||||
null,null,null,null,null, new FsInfo(0, node2FSInfo), null,null,null,null,null),
|
null,null,null,null,null, new FsInfo(0, node2FSInfo), null,null,null,null,null, null),
|
||||||
new NodeStats(new DiscoveryNode("node_3", DummyTransportAddress.INSTANCE, Version.CURRENT), 0,
|
new NodeStats(new DiscoveryNode("node_3", DummyTransportAddress.INSTANCE, Version.CURRENT), 0,
|
||||||
null,null,null,null,null, new FsInfo(0, node3FSInfo), null,null,null,null,null)
|
null,null,null,null,null, new FsInfo(0, node3FSInfo), null,null,null,null,null, null)
|
||||||
};
|
};
|
||||||
InternalClusterInfoService.fillDiskUsagePerNode(logger, nodeStats, newLeastAvaiableUsages, newMostAvaiableUsages);
|
InternalClusterInfoService.fillDiskUsagePerNode(logger, nodeStats, newLeastAvaiableUsages, newMostAvaiableUsages);
|
||||||
DiskUsage leastNode_1 = newLeastAvaiableUsages.get("node_1");
|
DiskUsage leastNode_1 = newLeastAvaiableUsages.get("node_1");
|
||||||
@ -184,11 +184,11 @@ public class DiskUsageTests extends ESTestCase {
|
|||||||
};
|
};
|
||||||
NodeStats[] nodeStats = new NodeStats[] {
|
NodeStats[] nodeStats = new NodeStats[] {
|
||||||
new NodeStats(new DiscoveryNode("node_1", DummyTransportAddress.INSTANCE, Version.CURRENT), 0,
|
new NodeStats(new DiscoveryNode("node_1", DummyTransportAddress.INSTANCE, Version.CURRENT), 0,
|
||||||
null,null,null,null,null,new FsInfo(0, node1FSInfo), null,null,null,null,null),
|
null,null,null,null,null,new FsInfo(0, node1FSInfo), null,null,null,null,null, null),
|
||||||
new NodeStats(new DiscoveryNode("node_2", DummyTransportAddress.INSTANCE, Version.CURRENT), 0,
|
new NodeStats(new DiscoveryNode("node_2", DummyTransportAddress.INSTANCE, Version.CURRENT), 0,
|
||||||
null,null,null,null,null, new FsInfo(0, node2FSInfo), null,null,null,null,null),
|
null,null,null,null,null, new FsInfo(0, node2FSInfo), null,null,null,null,null, null),
|
||||||
new NodeStats(new DiscoveryNode("node_3", DummyTransportAddress.INSTANCE, Version.CURRENT), 0,
|
new NodeStats(new DiscoveryNode("node_3", DummyTransportAddress.INSTANCE, Version.CURRENT), 0,
|
||||||
null,null,null,null,null, new FsInfo(0, node3FSInfo), null,null,null,null,null)
|
null,null,null,null,null, new FsInfo(0, node3FSInfo), null,null,null,null,null, null)
|
||||||
};
|
};
|
||||||
InternalClusterInfoService.fillDiskUsagePerNode(logger, nodeStats, newLeastAvailableUsages, newMostAvailableUsages);
|
InternalClusterInfoService.fillDiskUsagePerNode(logger, nodeStats, newLeastAvailableUsages, newMostAvailableUsages);
|
||||||
DiskUsage leastNode_1 = newLeastAvailableUsages.get("node_1");
|
DiskUsage leastNode_1 = newLeastAvailableUsages.get("node_1");
|
||||||
|
@ -0,0 +1,275 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.cluster;
|
||||||
|
|
||||||
|
import org.elasticsearch.Version;
|
||||||
|
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||||
|
import org.elasticsearch.cluster.node.DiscoveryNodes;
|
||||||
|
import org.elasticsearch.common.component.Lifecycle;
|
||||||
|
import org.elasticsearch.common.component.LifecycleListener;
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.transport.BoundTransportAddress;
|
||||||
|
import org.elasticsearch.common.transport.DummyTransportAddress;
|
||||||
|
import org.elasticsearch.common.transport.TransportAddress;
|
||||||
|
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
|
||||||
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
|
import org.elasticsearch.transport.ConnectTransportException;
|
||||||
|
import org.elasticsearch.transport.Transport;
|
||||||
|
import org.elasticsearch.transport.TransportException;
|
||||||
|
import org.elasticsearch.transport.TransportRequest;
|
||||||
|
import org.elasticsearch.transport.TransportRequestOptions;
|
||||||
|
import org.elasticsearch.transport.TransportService;
|
||||||
|
import org.elasticsearch.transport.TransportServiceAdapter;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
|
||||||
|
public class NodeConnectionsServiceTests extends ESTestCase {
|
||||||
|
|
||||||
|
private static ThreadPool THREAD_POOL;
|
||||||
|
private MockTransport transport;
|
||||||
|
private TransportService transportService;
|
||||||
|
|
||||||
|
private List<DiscoveryNode> generateNodes() {
|
||||||
|
List<DiscoveryNode> nodes = new ArrayList<>();
|
||||||
|
for (int i = randomIntBetween(20, 50); i > 0; i--) {
|
||||||
|
final HashMap<String, String> attributes = new HashMap<>();
|
||||||
|
if (rarely()) {
|
||||||
|
attributes.put("client", "true");
|
||||||
|
} else {
|
||||||
|
attributes.put("master", "" + randomBoolean());
|
||||||
|
attributes.put("data", "" + randomBoolean());
|
||||||
|
attributes.put("ingest", "" + randomBoolean());
|
||||||
|
}
|
||||||
|
nodes.add(new DiscoveryNode("node_" + i, "" + i, DummyTransportAddress.INSTANCE, attributes, Version.CURRENT));
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClusterState clusterStateFromNodes(List<DiscoveryNode> nodes) {
|
||||||
|
final DiscoveryNodes.Builder builder = DiscoveryNodes.builder();
|
||||||
|
for (DiscoveryNode node : nodes) {
|
||||||
|
builder.put(node);
|
||||||
|
}
|
||||||
|
return ClusterState.builder(new ClusterName("test")).nodes(builder).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConnectAndDisconnect() {
|
||||||
|
List<DiscoveryNode> nodes = generateNodes();
|
||||||
|
NodeConnectionsService service = new NodeConnectionsService(Settings.EMPTY, THREAD_POOL, transportService);
|
||||||
|
|
||||||
|
ClusterState current = clusterStateFromNodes(Collections.emptyList());
|
||||||
|
ClusterChangedEvent event = new ClusterChangedEvent("test", clusterStateFromNodes(randomSubsetOf(nodes)), current);
|
||||||
|
|
||||||
|
service.connectToAddedNodes(event);
|
||||||
|
assertConnected(event.nodesDelta().addedNodes());
|
||||||
|
|
||||||
|
service.disconnectFromRemovedNodes(event);
|
||||||
|
assertConnectedExactlyToNodes(event.state());
|
||||||
|
|
||||||
|
current = event.state();
|
||||||
|
event = new ClusterChangedEvent("test", clusterStateFromNodes(randomSubsetOf(nodes)), current);
|
||||||
|
|
||||||
|
service.connectToAddedNodes(event);
|
||||||
|
assertConnected(event.nodesDelta().addedNodes());
|
||||||
|
|
||||||
|
service.disconnectFromRemovedNodes(event);
|
||||||
|
assertConnectedExactlyToNodes(event.state());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void testReconnect() {
|
||||||
|
List<DiscoveryNode> nodes = generateNodes();
|
||||||
|
NodeConnectionsService service = new NodeConnectionsService(Settings.EMPTY, THREAD_POOL, transportService);
|
||||||
|
|
||||||
|
ClusterState current = clusterStateFromNodes(Collections.emptyList());
|
||||||
|
ClusterChangedEvent event = new ClusterChangedEvent("test", clusterStateFromNodes(randomSubsetOf(nodes)), current);
|
||||||
|
|
||||||
|
transport.randomConnectionExceptions = true;
|
||||||
|
|
||||||
|
service.connectToAddedNodes(event);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
// simulate disconnects
|
||||||
|
for (DiscoveryNode node : randomSubsetOf(nodes)) {
|
||||||
|
transport.disconnectFromNode(node);
|
||||||
|
}
|
||||||
|
service.new ConnectionChecker().run();
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable exceptions so things can be restored
|
||||||
|
transport.randomConnectionExceptions = false;
|
||||||
|
service.new ConnectionChecker().run();
|
||||||
|
assertConnectedExactlyToNodes(event.state());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertConnectedExactlyToNodes(ClusterState state) {
|
||||||
|
assertConnected(state.nodes());
|
||||||
|
assertThat(transport.connectedNodes.size(), equalTo(state.nodes().size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertConnected(Iterable<DiscoveryNode> nodes) {
|
||||||
|
for (DiscoveryNode node : nodes) {
|
||||||
|
assertTrue("not connected to " + node, transport.connectedNodes.contains(node));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNotConnected(Iterable<DiscoveryNode> nodes) {
|
||||||
|
for (DiscoveryNode node : nodes) {
|
||||||
|
assertFalse("still connected to " + node, transport.connectedNodes.contains(node));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
this.transport = new MockTransport();
|
||||||
|
transportService = new TransportService(transport, THREAD_POOL);
|
||||||
|
transportService.start();
|
||||||
|
transportService.acceptIncomingRequests();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
transportService.stop();
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void stopThreadPool() {
|
||||||
|
ThreadPool.terminate(THREAD_POOL, 30, TimeUnit.SECONDS);
|
||||||
|
THREAD_POOL = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
final class MockTransport implements Transport {
|
||||||
|
|
||||||
|
Set<DiscoveryNode> connectedNodes = ConcurrentCollections.newConcurrentSet();
|
||||||
|
volatile boolean randomConnectionExceptions = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transportServiceAdapter(TransportServiceAdapter service) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BoundTransportAddress boundAddress() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, BoundTransportAddress> profileBoundAddresses() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TransportAddress[] addressesFromString(String address, int perAddressLimit) throws Exception {
|
||||||
|
return new TransportAddress[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addressSupported(Class<? extends TransportAddress> address) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean nodeConnected(DiscoveryNode node) {
|
||||||
|
return connectedNodes.contains(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connectToNode(DiscoveryNode node) throws ConnectTransportException {
|
||||||
|
if (connectedNodes.contains(node) == false && randomConnectionExceptions && randomBoolean()) {
|
||||||
|
throw new ConnectTransportException(node, "simulated");
|
||||||
|
}
|
||||||
|
connectedNodes.add(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connectToNodeLight(DiscoveryNode node) throws ConnectTransportException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnectFromNode(DiscoveryNode node) {
|
||||||
|
connectedNodes.remove(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendRequest(DiscoveryNode node, long requestId, String action, TransportRequest request,
|
||||||
|
TransportRequestOptions options) throws IOException, TransportException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long serverOpen() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getLocalAddresses() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Lifecycle.State lifecycleState() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addLifecycleListener(LifecycleListener listener) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeLifecycleListener(LifecycleListener listener) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transport start() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transport stop() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,7 @@ import org.elasticsearch.cluster.ClusterState;
|
|||||||
import org.elasticsearch.cluster.ClusterStateTaskExecutor;
|
import org.elasticsearch.cluster.ClusterStateTaskExecutor;
|
||||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||||
import org.elasticsearch.cluster.metadata.MetaData;
|
import org.elasticsearch.cluster.metadata.MetaData;
|
||||||
|
import org.elasticsearch.cluster.node.DiscoveryNodeService;
|
||||||
import org.elasticsearch.cluster.node.DiscoveryNodes;
|
import org.elasticsearch.cluster.node.DiscoveryNodes;
|
||||||
import org.elasticsearch.cluster.routing.GroupShardsIterator;
|
import org.elasticsearch.cluster.routing.GroupShardsIterator;
|
||||||
import org.elasticsearch.cluster.routing.IndexShardRoutingTable;
|
import org.elasticsearch.cluster.routing.IndexShardRoutingTable;
|
||||||
@ -40,7 +41,6 @@ import org.elasticsearch.cluster.routing.allocation.AllocationService;
|
|||||||
import org.elasticsearch.cluster.routing.allocation.FailedRerouteAllocation;
|
import org.elasticsearch.cluster.routing.allocation.FailedRerouteAllocation;
|
||||||
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
|
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.ClusterRebalanceAllocationDecider;
|
import org.elasticsearch.cluster.routing.allocation.decider.ClusterRebalanceAllocationDecider;
|
||||||
import org.elasticsearch.cluster.service.InternalClusterService;
|
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.index.Index;
|
import org.elasticsearch.index.Index;
|
||||||
import org.elasticsearch.test.ESAllocationTestCase;
|
import org.elasticsearch.test.ESAllocationTestCase;
|
||||||
@ -305,7 +305,7 @@ public class ShardFailedClusterStateTaskExecutorTests extends ESAllocationTestCa
|
|||||||
return randomSubsetOf(1, shards.toArray(new ShardRouting[0])).get(0);
|
return randomSubsetOf(1, shards.toArray(new ShardRouting[0])).get(0);
|
||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
TestShardRouting.newShardRouting(shardRouting.index(), shardRouting.id(), InternalClusterService.generateNodeId(Settings.EMPTY), randomBoolean(), randomFrom(ShardRoutingState.values()));
|
TestShardRouting.newShardRouting(shardRouting.index(), shardRouting.id(), DiscoveryNodeService.generateNodeId(Settings.EMPTY), randomBoolean(), randomFrom(ShardRoutingState.values()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ import org.elasticsearch.cluster.routing.ShardRouting;
|
|||||||
import org.elasticsearch.cluster.routing.ShardRoutingState;
|
import org.elasticsearch.cluster.routing.ShardRoutingState;
|
||||||
import org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator;
|
import org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator;
|
||||||
import org.elasticsearch.cluster.routing.allocation.allocator.ShardsAllocator;
|
import org.elasticsearch.cluster.routing.allocation.allocator.ShardsAllocator;
|
||||||
import org.elasticsearch.cluster.routing.allocation.allocator.ShardsAllocators;
|
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.ClusterRebalanceAllocationDecider;
|
import org.elasticsearch.cluster.routing.allocation.decider.ClusterRebalanceAllocationDecider;
|
||||||
import org.elasticsearch.common.logging.ESLogger;
|
import org.elasticsearch.common.logging.ESLogger;
|
||||||
import org.elasticsearch.common.logging.Loggers;
|
import org.elasticsearch.common.logging.Loggers;
|
||||||
@ -311,29 +310,9 @@ public class BalanceConfigurationTests extends ESAllocationTestCase {
|
|||||||
public void testNoRebalanceOnPrimaryOverload() {
|
public void testNoRebalanceOnPrimaryOverload() {
|
||||||
Settings.Builder settings = settingsBuilder();
|
Settings.Builder settings = settingsBuilder();
|
||||||
AllocationService strategy = new AllocationService(settings.build(), randomAllocationDeciders(settings.build(),
|
AllocationService strategy = new AllocationService(settings.build(), randomAllocationDeciders(settings.build(),
|
||||||
new ClusterSettings(Settings.Builder.EMPTY_SETTINGS, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), getRandom()), new ShardsAllocators(settings.build(),
|
new ClusterSettings(Settings.Builder.EMPTY_SETTINGS, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), getRandom()),
|
||||||
NoopGatewayAllocator.INSTANCE, new ShardsAllocator() {
|
NoopGatewayAllocator.INSTANCE, new ShardsAllocator() {
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean rebalance(RoutingAllocation allocation) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean moveShards(RoutingAllocation allocation) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void applyStartedShards(StartedRerouteAllocation allocation) {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void applyFailedShards(FailedRerouteAllocation allocation) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* // this allocator tries to rebuild this scenario where a rebalance is
|
* // this allocator tries to rebuild this scenario where a rebalance is
|
||||||
* // triggered solely by the primary overload on node [1] where a shard
|
* // triggered solely by the primary overload on node [1] where a shard
|
||||||
@ -354,9 +333,8 @@ public class BalanceConfigurationTests extends ESAllocationTestCase {
|
|||||||
--------[test][2], node[3], [P], s[STARTED]
|
--------[test][2], node[3], [P], s[STARTED]
|
||||||
--------[test][3], node[3], [P], s[STARTED]
|
--------[test][3], node[3], [P], s[STARTED]
|
||||||
---- unassigned
|
---- unassigned
|
||||||
*/
|
*/
|
||||||
@Override
|
public boolean allocate(RoutingAllocation allocation) {
|
||||||
public boolean allocateUnassigned(RoutingAllocation allocation) {
|
|
||||||
RoutingNodes.UnassignedShards unassigned = allocation.routingNodes().unassigned();
|
RoutingNodes.UnassignedShards unassigned = allocation.routingNodes().unassigned();
|
||||||
boolean changed = !unassigned.isEmpty();
|
boolean changed = !unassigned.isEmpty();
|
||||||
ShardRouting[] drain = unassigned.drain();
|
ShardRouting[] drain = unassigned.drain();
|
||||||
@ -403,7 +381,7 @@ public class BalanceConfigurationTests extends ESAllocationTestCase {
|
|||||||
}
|
}
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
}), EmptyClusterInfoService.INSTANCE);
|
}, EmptyClusterInfoService.INSTANCE);
|
||||||
MetaData.Builder metaDataBuilder = MetaData.builder();
|
MetaData.Builder metaDataBuilder = MetaData.builder();
|
||||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
||||||
IndexMetaData.Builder indexMeta = IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(5).numberOfReplicas(1);
|
IndexMetaData.Builder indexMeta = IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(5).numberOfReplicas(1);
|
||||||
|
@ -36,7 +36,7 @@ import org.elasticsearch.cluster.routing.RoutingTable;
|
|||||||
import org.elasticsearch.cluster.routing.ShardRouting;
|
import org.elasticsearch.cluster.routing.ShardRouting;
|
||||||
import org.elasticsearch.cluster.routing.ShardRoutingState;
|
import org.elasticsearch.cluster.routing.ShardRoutingState;
|
||||||
import org.elasticsearch.cluster.routing.TestShardRouting;
|
import org.elasticsearch.cluster.routing.TestShardRouting;
|
||||||
import org.elasticsearch.cluster.routing.allocation.allocator.ShardsAllocators;
|
import org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator;
|
||||||
import org.elasticsearch.cluster.routing.allocation.command.AllocationCommands;
|
import org.elasticsearch.cluster.routing.allocation.command.AllocationCommands;
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDecider;
|
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDecider;
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders;
|
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders;
|
||||||
@ -333,7 +333,7 @@ public class NodeVersionAllocationDeciderTests extends ESAllocationTestCase {
|
|||||||
AllocationDeciders allocationDeciders = new AllocationDeciders(Settings.EMPTY, new AllocationDecider[] {new NodeVersionAllocationDecider(Settings.EMPTY)});
|
AllocationDeciders allocationDeciders = new AllocationDeciders(Settings.EMPTY, new AllocationDecider[] {new NodeVersionAllocationDecider(Settings.EMPTY)});
|
||||||
AllocationService strategy = new MockAllocationService(Settings.EMPTY,
|
AllocationService strategy = new MockAllocationService(Settings.EMPTY,
|
||||||
allocationDeciders,
|
allocationDeciders,
|
||||||
new ShardsAllocators(Settings.EMPTY, NoopGatewayAllocator.INSTANCE), EmptyClusterInfoService.INSTANCE);
|
NoopGatewayAllocator.INSTANCE, new BalancedShardsAllocator(Settings.EMPTY), EmptyClusterInfoService.INSTANCE);
|
||||||
RoutingAllocation.Result result = strategy.reroute(state, new AllocationCommands(), true);
|
RoutingAllocation.Result result = strategy.reroute(state, new AllocationCommands(), true);
|
||||||
// the two indices must stay as is, the replicas cannot move to oldNode2 because versions don't match
|
// the two indices must stay as is, the replicas cannot move to oldNode2 because versions don't match
|
||||||
state = ClusterState.builder(state).routingResult(result).build();
|
state = ClusterState.builder(state).routingResult(result).build();
|
||||||
@ -363,7 +363,7 @@ public class NodeVersionAllocationDeciderTests extends ESAllocationTestCase {
|
|||||||
new NodeVersionAllocationDecider(Settings.EMPTY)});
|
new NodeVersionAllocationDecider(Settings.EMPTY)});
|
||||||
AllocationService strategy = new MockAllocationService(Settings.EMPTY,
|
AllocationService strategy = new MockAllocationService(Settings.EMPTY,
|
||||||
allocationDeciders,
|
allocationDeciders,
|
||||||
new ShardsAllocators(Settings.EMPTY, NoopGatewayAllocator.INSTANCE), EmptyClusterInfoService.INSTANCE);
|
NoopGatewayAllocator.INSTANCE, new BalancedShardsAllocator(Settings.EMPTY), EmptyClusterInfoService.INSTANCE);
|
||||||
RoutingAllocation.Result result = strategy.reroute(state, new AllocationCommands(), true);
|
RoutingAllocation.Result result = strategy.reroute(state, new AllocationCommands(), true);
|
||||||
|
|
||||||
// Make sure that primary shards are only allocated on the new node
|
// Make sure that primary shards are only allocated on the new node
|
||||||
|
@ -30,7 +30,7 @@ import org.elasticsearch.cluster.routing.RoutingNode;
|
|||||||
import org.elasticsearch.cluster.routing.RoutingTable;
|
import org.elasticsearch.cluster.routing.RoutingTable;
|
||||||
import org.elasticsearch.cluster.routing.ShardRouting;
|
import org.elasticsearch.cluster.routing.ShardRouting;
|
||||||
import org.elasticsearch.cluster.routing.ShardRoutingState;
|
import org.elasticsearch.cluster.routing.ShardRoutingState;
|
||||||
import org.elasticsearch.cluster.routing.allocation.allocator.ShardsAllocators;
|
import org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator;
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDecider;
|
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDecider;
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders;
|
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders;
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.Decision;
|
import org.elasticsearch.cluster.routing.allocation.decider.Decision;
|
||||||
@ -59,7 +59,7 @@ public class RandomAllocationDeciderTests extends ESAllocationTestCase {
|
|||||||
RandomAllocationDecider randomAllocationDecider = new RandomAllocationDecider(getRandom());
|
RandomAllocationDecider randomAllocationDecider = new RandomAllocationDecider(getRandom());
|
||||||
AllocationService strategy = new AllocationService(settingsBuilder().build(), new AllocationDeciders(Settings.EMPTY,
|
AllocationService strategy = new AllocationService(settingsBuilder().build(), new AllocationDeciders(Settings.EMPTY,
|
||||||
new HashSet<>(Arrays.asList(new SameShardAllocationDecider(Settings.EMPTY), new ReplicaAfterPrimaryActiveAllocationDecider(Settings.EMPTY),
|
new HashSet<>(Arrays.asList(new SameShardAllocationDecider(Settings.EMPTY), new ReplicaAfterPrimaryActiveAllocationDecider(Settings.EMPTY),
|
||||||
randomAllocationDecider))), new ShardsAllocators(NoopGatewayAllocator.INSTANCE), EmptyClusterInfoService.INSTANCE);
|
randomAllocationDecider))), NoopGatewayAllocator.INSTANCE, new BalancedShardsAllocator(Settings.EMPTY), EmptyClusterInfoService.INSTANCE);
|
||||||
int indices = scaledRandomIntBetween(1, 20);
|
int indices = scaledRandomIntBetween(1, 20);
|
||||||
Builder metaBuilder = MetaData.builder();
|
Builder metaBuilder = MetaData.builder();
|
||||||
int maxNumReplicas = 1;
|
int maxNumReplicas = 1;
|
||||||
|
@ -39,7 +39,7 @@ import org.elasticsearch.cluster.routing.ShardRoutingState;
|
|||||||
import org.elasticsearch.cluster.routing.TestShardRouting;
|
import org.elasticsearch.cluster.routing.TestShardRouting;
|
||||||
import org.elasticsearch.cluster.routing.allocation.AllocationService;
|
import org.elasticsearch.cluster.routing.allocation.AllocationService;
|
||||||
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
|
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
|
||||||
import org.elasticsearch.cluster.routing.allocation.allocator.ShardsAllocators;
|
import org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator;
|
||||||
import org.elasticsearch.cluster.routing.allocation.command.AllocationCommand;
|
import org.elasticsearch.cluster.routing.allocation.command.AllocationCommand;
|
||||||
import org.elasticsearch.cluster.routing.allocation.command.AllocationCommands;
|
import org.elasticsearch.cluster.routing.allocation.command.AllocationCommands;
|
||||||
import org.elasticsearch.cluster.routing.allocation.command.MoveAllocationCommand;
|
import org.elasticsearch.cluster.routing.allocation.command.MoveAllocationCommand;
|
||||||
@ -65,10 +65,6 @@ import static org.hamcrest.Matchers.nullValue;
|
|||||||
|
|
||||||
public class DiskThresholdDeciderTests extends ESAllocationTestCase {
|
public class DiskThresholdDeciderTests extends ESAllocationTestCase {
|
||||||
|
|
||||||
private static ShardsAllocators makeShardsAllocators() {
|
|
||||||
return new ShardsAllocators(NoopGatewayAllocator.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testDiskThreshold() {
|
public void testDiskThreshold() {
|
||||||
Settings diskSettings = settingsBuilder()
|
Settings diskSettings = settingsBuilder()
|
||||||
.put(DiskThresholdDecider.CLUSTER_ROUTING_ALLOCATION_DISK_THRESHOLD_ENABLED_SETTING.getKey(), true)
|
.put(DiskThresholdDecider.CLUSTER_ROUTING_ALLOCATION_DISK_THRESHOLD_ENABLED_SETTING.getKey(), true)
|
||||||
@ -109,7 +105,7 @@ public class DiskThresholdDeciderTests extends ESAllocationTestCase {
|
|||||||
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
||||||
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
||||||
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
||||||
.build(), deciders, makeShardsAllocators(), cis);
|
.build(), deciders, NoopGatewayAllocator.INSTANCE, new BalancedShardsAllocator(Settings.EMPTY), cis);
|
||||||
|
|
||||||
MetaData metaData = MetaData.builder()
|
MetaData metaData = MetaData.builder()
|
||||||
.put(IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(1).numberOfReplicas(1))
|
.put(IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(1).numberOfReplicas(1))
|
||||||
@ -194,7 +190,7 @@ public class DiskThresholdDeciderTests extends ESAllocationTestCase {
|
|||||||
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
||||||
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
||||||
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
||||||
.build(), deciders, makeShardsAllocators(), cis);
|
.build(), deciders, NoopGatewayAllocator.INSTANCE, new BalancedShardsAllocator(Settings.EMPTY), cis);
|
||||||
|
|
||||||
routingTable = strategy.reroute(clusterState, "reroute").routingTable();
|
routingTable = strategy.reroute(clusterState, "reroute").routingTable();
|
||||||
clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
|
clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
|
||||||
@ -225,7 +221,7 @@ public class DiskThresholdDeciderTests extends ESAllocationTestCase {
|
|||||||
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
||||||
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
||||||
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
||||||
.build(), deciders, makeShardsAllocators(), cis);
|
.build(), deciders, NoopGatewayAllocator.INSTANCE, new BalancedShardsAllocator(Settings.EMPTY), cis);
|
||||||
|
|
||||||
routingTable = strategy.reroute(clusterState, "reroute").routingTable();
|
routingTable = strategy.reroute(clusterState, "reroute").routingTable();
|
||||||
clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
|
clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
|
||||||
@ -305,7 +301,7 @@ public class DiskThresholdDeciderTests extends ESAllocationTestCase {
|
|||||||
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
||||||
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
||||||
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
||||||
.build(), deciders, makeShardsAllocators(), cis);
|
.build(), deciders, NoopGatewayAllocator.INSTANCE, new BalancedShardsAllocator(Settings.EMPTY), cis);
|
||||||
|
|
||||||
MetaData metaData = MetaData.builder()
|
MetaData metaData = MetaData.builder()
|
||||||
.put(IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(1).numberOfReplicas(2))
|
.put(IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(1).numberOfReplicas(2))
|
||||||
@ -362,7 +358,7 @@ public class DiskThresholdDeciderTests extends ESAllocationTestCase {
|
|||||||
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
||||||
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
||||||
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
||||||
.build(), deciders, makeShardsAllocators(), cis);
|
.build(), deciders, NoopGatewayAllocator.INSTANCE, new BalancedShardsAllocator(Settings.EMPTY), cis);
|
||||||
|
|
||||||
routingTable = strategy.reroute(clusterState, "reroute").routingTable();
|
routingTable = strategy.reroute(clusterState, "reroute").routingTable();
|
||||||
clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
|
clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
|
||||||
@ -429,7 +425,7 @@ public class DiskThresholdDeciderTests extends ESAllocationTestCase {
|
|||||||
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
||||||
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
||||||
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
||||||
.build(), deciders, makeShardsAllocators(), cis);
|
.build(), deciders, NoopGatewayAllocator.INSTANCE, new BalancedShardsAllocator(Settings.EMPTY), cis);
|
||||||
|
|
||||||
routingTable = strategy.reroute(clusterState, "reroute").routingTable();
|
routingTable = strategy.reroute(clusterState, "reroute").routingTable();
|
||||||
clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
|
clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
|
||||||
@ -460,7 +456,7 @@ public class DiskThresholdDeciderTests extends ESAllocationTestCase {
|
|||||||
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
||||||
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
||||||
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
||||||
.build(), deciders, makeShardsAllocators(), cis);
|
.build(), deciders, NoopGatewayAllocator.INSTANCE, new BalancedShardsAllocator(Settings.EMPTY), cis);
|
||||||
|
|
||||||
routingTable = strategy.reroute(clusterState, "reroute").routingTable();
|
routingTable = strategy.reroute(clusterState, "reroute").routingTable();
|
||||||
clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
|
clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
|
||||||
@ -569,7 +565,7 @@ public class DiskThresholdDeciderTests extends ESAllocationTestCase {
|
|||||||
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
||||||
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
||||||
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
||||||
.build(), deciders, makeShardsAllocators(), cis);
|
.build(), deciders, NoopGatewayAllocator.INSTANCE, new BalancedShardsAllocator(Settings.EMPTY), cis);
|
||||||
|
|
||||||
MetaData metaData = MetaData.builder()
|
MetaData metaData = MetaData.builder()
|
||||||
.put(IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(1).numberOfReplicas(0))
|
.put(IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(1).numberOfReplicas(0))
|
||||||
@ -637,7 +633,7 @@ public class DiskThresholdDeciderTests extends ESAllocationTestCase {
|
|||||||
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
||||||
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
||||||
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
||||||
.build(), deciders, makeShardsAllocators(), cis);
|
.build(), deciders, NoopGatewayAllocator.INSTANCE, new BalancedShardsAllocator(Settings.EMPTY), cis);
|
||||||
|
|
||||||
MetaData metaData = MetaData.builder()
|
MetaData metaData = MetaData.builder()
|
||||||
.put(IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(1).numberOfReplicas(0))
|
.put(IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(1).numberOfReplicas(0))
|
||||||
@ -740,7 +736,7 @@ public class DiskThresholdDeciderTests extends ESAllocationTestCase {
|
|||||||
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
||||||
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
||||||
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
||||||
.build(), deciders, makeShardsAllocators(), cis);
|
.build(), deciders, NoopGatewayAllocator.INSTANCE, new BalancedShardsAllocator(Settings.EMPTY), cis);
|
||||||
|
|
||||||
MetaData metaData = MetaData.builder()
|
MetaData metaData = MetaData.builder()
|
||||||
.put(IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(1).numberOfReplicas(1))
|
.put(IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(1).numberOfReplicas(1))
|
||||||
@ -902,7 +898,7 @@ public class DiskThresholdDeciderTests extends ESAllocationTestCase {
|
|||||||
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
.put("cluster.routing.allocation.node_concurrent_recoveries", 10)
|
||||||
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
||||||
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
||||||
.build(), deciders, makeShardsAllocators(), cis);
|
.build(), deciders, NoopGatewayAllocator.INSTANCE, new BalancedShardsAllocator(Settings.EMPTY), cis);
|
||||||
// Ensure that the reroute call doesn't alter the routing table, since the first primary is relocating away
|
// Ensure that the reroute call doesn't alter the routing table, since the first primary is relocating away
|
||||||
// and therefor we will have sufficient disk space on node1.
|
// and therefor we will have sufficient disk space on node1.
|
||||||
RoutingAllocation.Result result = strategy.reroute(clusterState, "reroute");
|
RoutingAllocation.Result result = strategy.reroute(clusterState, "reroute");
|
||||||
@ -1003,7 +999,7 @@ public class DiskThresholdDeciderTests extends ESAllocationTestCase {
|
|||||||
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING.getKey(), "always")
|
||||||
|
|
||||||
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
.put("cluster.routing.allocation.cluster_concurrent_rebalance", -1)
|
||||||
.build(), deciders, makeShardsAllocators(), cis);
|
.build(), deciders, NoopGatewayAllocator.INSTANCE, new BalancedShardsAllocator(Settings.EMPTY), cis);
|
||||||
RoutingAllocation.Result result = strategy.reroute(clusterState, "reroute");
|
RoutingAllocation.Result result = strategy.reroute(clusterState, "reroute");
|
||||||
|
|
||||||
assertThat(result.routingTable().index("test").getShards().get(0).primaryShard().state(), equalTo(STARTED));
|
assertThat(result.routingTable().index("test").getShards().get(0).primaryShard().state(), equalTo(STARTED));
|
||||||
|
@ -0,0 +1,824 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.elasticsearch.cluster.service;
|
||||||
|
|
||||||
|
import org.apache.log4j.Level;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.elasticsearch.Version;
|
||||||
|
import org.elasticsearch.cluster.ClusterChangedEvent;
|
||||||
|
import org.elasticsearch.cluster.ClusterName;
|
||||||
|
import org.elasticsearch.cluster.ClusterService;
|
||||||
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
|
import org.elasticsearch.cluster.ClusterStateTaskConfig;
|
||||||
|
import org.elasticsearch.cluster.ClusterStateTaskExecutor;
|
||||||
|
import org.elasticsearch.cluster.ClusterStateTaskListener;
|
||||||
|
import org.elasticsearch.cluster.ClusterStateUpdateTask;
|
||||||
|
import org.elasticsearch.cluster.NodeConnectionsService;
|
||||||
|
import org.elasticsearch.cluster.block.ClusterBlocks;
|
||||||
|
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||||
|
import org.elasticsearch.cluster.node.DiscoveryNodes;
|
||||||
|
import org.elasticsearch.cluster.routing.OperationRouting;
|
||||||
|
import org.elasticsearch.common.Priority;
|
||||||
|
import org.elasticsearch.common.collect.Tuple;
|
||||||
|
import org.elasticsearch.common.settings.ClusterSettings;
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.transport.DummyTransportAddress;
|
||||||
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
import org.elasticsearch.test.MockLogAppender;
|
||||||
|
import org.elasticsearch.test.junit.annotations.TestLogging;
|
||||||
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.BrokenBarrierException;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.CyclicBarrier;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import static org.elasticsearch.common.settings.Settings.settingsBuilder;
|
||||||
|
import static org.hamcrest.Matchers.empty;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
|
public class ClusterServiceTests extends ESTestCase {
|
||||||
|
|
||||||
|
static ThreadPool threadPool;
|
||||||
|
TimedClusterService clusterService;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void createThreadPool() {
|
||||||
|
threadPool = new ThreadPool(ClusterServiceTests.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void stopThreadPool() {
|
||||||
|
if (threadPool != null) {
|
||||||
|
threadPool.shutdownNow();
|
||||||
|
threadPool = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
clusterService = createClusterService(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
clusterService.close();
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
TimedClusterService createClusterService(boolean makeMaster) throws InterruptedException {
|
||||||
|
TimedClusterService test = new TimedClusterService(Settings.EMPTY, null,
|
||||||
|
new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS),
|
||||||
|
threadPool, new ClusterName("ClusterServiceTests"));
|
||||||
|
test.setLocalNode(new DiscoveryNode("node1", DummyTransportAddress.INSTANCE, Version.CURRENT));
|
||||||
|
test.setNodeConnectionsService(new NodeConnectionsService(Settings.EMPTY, null, null) {
|
||||||
|
@Override
|
||||||
|
public void connectToAddedNodes(ClusterChangedEvent event) {
|
||||||
|
// skip
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnectFromRemovedNodes(ClusterChangedEvent event) {
|
||||||
|
// skip
|
||||||
|
}
|
||||||
|
});
|
||||||
|
test.setClusterStatePublisher((event, ackListener) -> {
|
||||||
|
});
|
||||||
|
test.start();
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
test.submitStateUpdateTask("making a master", new ClusterStateUpdateTask() {
|
||||||
|
@Override
|
||||||
|
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||||
|
final DiscoveryNodes nodes = currentState.nodes();
|
||||||
|
final DiscoveryNodes.Builder nodesBuilder = DiscoveryNodes.builder(nodes)
|
||||||
|
.masterNodeId(makeMaster ? nodes.localNodeId() : null);
|
||||||
|
return ClusterState.builder(currentState).blocks(ClusterBlocks.EMPTY_CLUSTER_BLOCK).nodes(nodesBuilder).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean runOnlyOnMaster() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
logger.warn("unexpected exception", t);
|
||||||
|
fail("unexpected exception" + t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
latch.await();
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testTimeoutUpdateTask() throws Exception {
|
||||||
|
final CountDownLatch block = new CountDownLatch(1);
|
||||||
|
clusterService.submitStateUpdateTask("test1", new ClusterStateUpdateTask() {
|
||||||
|
@Override
|
||||||
|
public ClusterState execute(ClusterState currentState) {
|
||||||
|
try {
|
||||||
|
block.await();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
throw new RuntimeException(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final CountDownLatch timedOut = new CountDownLatch(1);
|
||||||
|
final AtomicBoolean executeCalled = new AtomicBoolean();
|
||||||
|
clusterService.submitStateUpdateTask("test2", new ClusterStateUpdateTask() {
|
||||||
|
@Override
|
||||||
|
public TimeValue timeout() {
|
||||||
|
return TimeValue.timeValueMillis(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
timedOut.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClusterState execute(ClusterState currentState) {
|
||||||
|
executeCalled.set(true);
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
timedOut.await();
|
||||||
|
block.countDown();
|
||||||
|
final CountDownLatch allProcessed = new CountDownLatch(1);
|
||||||
|
clusterService.submitStateUpdateTask("test3", new ClusterStateUpdateTask() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
throw new RuntimeException(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClusterState execute(ClusterState currentState) {
|
||||||
|
allProcessed.countDown();
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
allProcessed.await(); // executed another task to double check that execute on the timed out update task is not called...
|
||||||
|
assertThat(executeCalled.get(), equalTo(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void testMasterAwareExecution() throws Exception {
|
||||||
|
ClusterService nonMaster = createClusterService(false);
|
||||||
|
|
||||||
|
final boolean[] taskFailed = {false};
|
||||||
|
final CountDownLatch latch1 = new CountDownLatch(1);
|
||||||
|
nonMaster.submitStateUpdateTask("test", new ClusterStateUpdateTask() {
|
||||||
|
@Override
|
||||||
|
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||||
|
latch1.countDown();
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
taskFailed[0] = true;
|
||||||
|
latch1.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
latch1.await();
|
||||||
|
assertTrue("cluster state update task was executed on a non-master", taskFailed[0]);
|
||||||
|
|
||||||
|
taskFailed[0] = true;
|
||||||
|
final CountDownLatch latch2 = new CountDownLatch(1);
|
||||||
|
nonMaster.submitStateUpdateTask("test", new ClusterStateUpdateTask() {
|
||||||
|
@Override
|
||||||
|
public boolean runOnlyOnMaster() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||||
|
taskFailed[0] = false;
|
||||||
|
latch2.countDown();
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
taskFailed[0] = true;
|
||||||
|
latch2.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
latch2.await();
|
||||||
|
assertFalse("non-master cluster state update task was not executed", taskFailed[0]);
|
||||||
|
|
||||||
|
nonMaster.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* test that a listener throwing an exception while handling a
|
||||||
|
* notification does not prevent publication notification to the
|
||||||
|
* executor
|
||||||
|
*/
|
||||||
|
public void testClusterStateTaskListenerThrowingExceptionIsOkay() throws InterruptedException {
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
AtomicBoolean published = new AtomicBoolean();
|
||||||
|
|
||||||
|
clusterService.submitStateUpdateTask(
|
||||||
|
"testClusterStateTaskListenerThrowingExceptionIsOkay",
|
||||||
|
new Object(),
|
||||||
|
ClusterStateTaskConfig.build(Priority.NORMAL),
|
||||||
|
new ClusterStateTaskExecutor<Object>() {
|
||||||
|
@Override
|
||||||
|
public boolean runOnlyOnMaster() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BatchResult<Object> execute(ClusterState currentState, List<Object> tasks) throws Exception {
|
||||||
|
ClusterState newClusterState = ClusterState.builder(currentState).build();
|
||||||
|
return BatchResult.builder().successes(tasks).build(newClusterState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clusterStatePublished(ClusterState newClusterState) {
|
||||||
|
published.set(true);
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new ClusterStateTaskListener() {
|
||||||
|
@Override
|
||||||
|
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||||
|
throw new IllegalStateException(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
latch.await();
|
||||||
|
assertTrue(published.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
// test that for a single thread, tasks are executed in the order
|
||||||
|
// that they are submitted
|
||||||
|
public void testClusterStateUpdateTasksAreExecutedInOrder() throws BrokenBarrierException, InterruptedException {
|
||||||
|
class TaskExecutor implements ClusterStateTaskExecutor<Integer> {
|
||||||
|
List<Integer> tasks = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BatchResult<Integer> execute(ClusterState currentState, List<Integer> tasks) throws Exception {
|
||||||
|
this.tasks.addAll(tasks);
|
||||||
|
return BatchResult.<Integer>builder().successes(tasks).build(ClusterState.builder(currentState).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean runOnlyOnMaster() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int numberOfThreads = randomIntBetween(2, 8);
|
||||||
|
TaskExecutor[] executors = new TaskExecutor[numberOfThreads];
|
||||||
|
for (int i = 0; i < numberOfThreads; i++) {
|
||||||
|
executors[i] = new TaskExecutor();
|
||||||
|
}
|
||||||
|
|
||||||
|
int tasksSubmittedPerThread = randomIntBetween(2, 1024);
|
||||||
|
|
||||||
|
CopyOnWriteArrayList<Tuple<String, Throwable>> failures = new CopyOnWriteArrayList<>();
|
||||||
|
CountDownLatch updateLatch = new CountDownLatch(numberOfThreads * tasksSubmittedPerThread);
|
||||||
|
|
||||||
|
ClusterStateTaskListener listener = new ClusterStateTaskListener() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
logger.error("unexpected failure: [{}]", t, source);
|
||||||
|
failures.add(new Tuple<>(source, t));
|
||||||
|
updateLatch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||||
|
updateLatch.countDown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CyclicBarrier barrier = new CyclicBarrier(1 + numberOfThreads);
|
||||||
|
|
||||||
|
for (int i = 0; i < numberOfThreads; i++) {
|
||||||
|
final int index = i;
|
||||||
|
Thread thread = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
barrier.await();
|
||||||
|
for (int j = 0; j < tasksSubmittedPerThread; j++) {
|
||||||
|
clusterService.submitStateUpdateTask("[" + index + "][" + j + "]", j,
|
||||||
|
ClusterStateTaskConfig.build(randomFrom(Priority.values())), executors[index], listener);
|
||||||
|
}
|
||||||
|
barrier.await();
|
||||||
|
} catch (InterruptedException | BrokenBarrierException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for all threads to be ready
|
||||||
|
barrier.await();
|
||||||
|
// wait for all threads to finish
|
||||||
|
barrier.await();
|
||||||
|
|
||||||
|
updateLatch.await();
|
||||||
|
|
||||||
|
assertThat(failures, empty());
|
||||||
|
|
||||||
|
for (int i = 0; i < numberOfThreads; i++) {
|
||||||
|
assertEquals(tasksSubmittedPerThread, executors[i].tasks.size());
|
||||||
|
for (int j = 0; j < tasksSubmittedPerThread; j++) {
|
||||||
|
assertNotNull(executors[i].tasks.get(j));
|
||||||
|
assertEquals("cluster state update task executed out of order", j, (int) executors[i].tasks.get(j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testClusterStateBatchedUpdates() throws BrokenBarrierException, InterruptedException {
|
||||||
|
AtomicInteger counter = new AtomicInteger();
|
||||||
|
class Task {
|
||||||
|
private AtomicBoolean state = new AtomicBoolean();
|
||||||
|
|
||||||
|
public void execute() {
|
||||||
|
if (!state.compareAndSet(false, true)) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
} else {
|
||||||
|
counter.incrementAndGet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int numberOfThreads = randomIntBetween(2, 8);
|
||||||
|
int tasksSubmittedPerThread = randomIntBetween(1, 1024);
|
||||||
|
int numberOfExecutors = Math.max(1, numberOfThreads / 4);
|
||||||
|
final Semaphore semaphore = new Semaphore(numberOfExecutors);
|
||||||
|
|
||||||
|
class TaskExecutor implements ClusterStateTaskExecutor<Task> {
|
||||||
|
private AtomicInteger counter = new AtomicInteger();
|
||||||
|
private AtomicInteger batches = new AtomicInteger();
|
||||||
|
private AtomicInteger published = new AtomicInteger();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BatchResult<Task> execute(ClusterState currentState, List<Task> tasks) throws Exception {
|
||||||
|
tasks.forEach(task -> task.execute());
|
||||||
|
counter.addAndGet(tasks.size());
|
||||||
|
ClusterState maybeUpdatedClusterState = currentState;
|
||||||
|
if (randomBoolean()) {
|
||||||
|
maybeUpdatedClusterState = ClusterState.builder(currentState).build();
|
||||||
|
batches.incrementAndGet();
|
||||||
|
semaphore.acquire();
|
||||||
|
}
|
||||||
|
return BatchResult.<Task>builder().successes(tasks).build(maybeUpdatedClusterState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean runOnlyOnMaster() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clusterStatePublished(ClusterState newClusterState) {
|
||||||
|
published.incrementAndGet();
|
||||||
|
semaphore.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcurrentMap<String, AtomicInteger> counters = new ConcurrentHashMap<>();
|
||||||
|
CountDownLatch updateLatch = new CountDownLatch(numberOfThreads * tasksSubmittedPerThread);
|
||||||
|
ClusterStateTaskListener listener = new ClusterStateTaskListener() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
assert false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||||
|
counters.computeIfAbsent(source, key -> new AtomicInteger()).incrementAndGet();
|
||||||
|
updateLatch.countDown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
List<TaskExecutor> executors = new ArrayList<>();
|
||||||
|
for (int i = 0; i < numberOfExecutors; i++) {
|
||||||
|
executors.add(new TaskExecutor());
|
||||||
|
}
|
||||||
|
|
||||||
|
// randomly assign tasks to executors
|
||||||
|
List<TaskExecutor> assignments = new ArrayList<>();
|
||||||
|
for (int i = 0; i < numberOfThreads; i++) {
|
||||||
|
for (int j = 0; j < tasksSubmittedPerThread; j++) {
|
||||||
|
assignments.add(randomFrom(executors));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<TaskExecutor, Integer> counts = new HashMap<>();
|
||||||
|
for (TaskExecutor executor : assignments) {
|
||||||
|
counts.merge(executor, 1, (previous, one) -> previous + one);
|
||||||
|
}
|
||||||
|
|
||||||
|
CyclicBarrier barrier = new CyclicBarrier(1 + numberOfThreads);
|
||||||
|
for (int i = 0; i < numberOfThreads; i++) {
|
||||||
|
final int index = i;
|
||||||
|
Thread thread = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
barrier.await();
|
||||||
|
for (int j = 0; j < tasksSubmittedPerThread; j++) {
|
||||||
|
ClusterStateTaskExecutor<Task> executor = assignments.get(index * tasksSubmittedPerThread + j);
|
||||||
|
clusterService.submitStateUpdateTask(
|
||||||
|
Thread.currentThread().getName(),
|
||||||
|
new Task(),
|
||||||
|
ClusterStateTaskConfig.build(randomFrom(Priority.values())),
|
||||||
|
executor,
|
||||||
|
listener);
|
||||||
|
}
|
||||||
|
barrier.await();
|
||||||
|
} catch (BrokenBarrierException | InterruptedException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for all threads to be ready
|
||||||
|
barrier.await();
|
||||||
|
// wait for all threads to finish
|
||||||
|
barrier.await();
|
||||||
|
|
||||||
|
// wait until all the cluster state updates have been processed
|
||||||
|
updateLatch.await();
|
||||||
|
// and until all of the publication callbacks have completed
|
||||||
|
semaphore.acquire(numberOfExecutors);
|
||||||
|
|
||||||
|
// assert the number of executed tasks is correct
|
||||||
|
assertEquals(numberOfThreads * tasksSubmittedPerThread, counter.get());
|
||||||
|
|
||||||
|
// assert each executor executed the correct number of tasks
|
||||||
|
for (TaskExecutor executor : executors) {
|
||||||
|
if (counts.containsKey(executor)) {
|
||||||
|
assertEquals((int) counts.get(executor), executor.counter.get());
|
||||||
|
assertEquals(executor.batches.get(), executor.published.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert the correct number of clusterStateProcessed events were triggered
|
||||||
|
for (Map.Entry<String, AtomicInteger> entry : counters.entrySet()) {
|
||||||
|
assertEquals(entry.getValue().get(), tasksSubmittedPerThread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note, this test can only work as long as we have a single thread executor executing the state update tasks!
|
||||||
|
*/
|
||||||
|
public void testPrioritizedTasks() throws Exception {
|
||||||
|
Settings settings = settingsBuilder()
|
||||||
|
.put("discovery.type", "local")
|
||||||
|
.build();
|
||||||
|
BlockingTask block = new BlockingTask(Priority.IMMEDIATE);
|
||||||
|
clusterService.submitStateUpdateTask("test", block);
|
||||||
|
int taskCount = randomIntBetween(5, 20);
|
||||||
|
Priority[] priorities = Priority.values();
|
||||||
|
|
||||||
|
// will hold all the tasks in the order in which they were executed
|
||||||
|
List<PrioritizedTask> tasks = new ArrayList<>(taskCount);
|
||||||
|
CountDownLatch latch = new CountDownLatch(taskCount);
|
||||||
|
for (int i = 0; i < taskCount; i++) {
|
||||||
|
Priority priority = priorities[randomIntBetween(0, priorities.length - 1)];
|
||||||
|
clusterService.submitStateUpdateTask("test", new PrioritizedTask(priority, latch, tasks));
|
||||||
|
}
|
||||||
|
|
||||||
|
block.release();
|
||||||
|
latch.await();
|
||||||
|
|
||||||
|
Priority prevPriority = null;
|
||||||
|
for (PrioritizedTask task : tasks) {
|
||||||
|
if (prevPriority == null) {
|
||||||
|
prevPriority = task.priority();
|
||||||
|
} else {
|
||||||
|
assertThat(task.priority().sameOrAfter(prevPriority), is(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestLogging("cluster:TRACE") // To ensure that we log cluster state events on TRACE level
|
||||||
|
public void testClusterStateUpdateLogging() throws Exception {
|
||||||
|
MockLogAppender mockAppender = new MockLogAppender();
|
||||||
|
mockAppender.addExpectation(new MockLogAppender.SeenEventExpectation("test1", "cluster.service", Level.DEBUG,
|
||||||
|
"*processing [test1]: took [1s] no change in cluster_state"));
|
||||||
|
mockAppender.addExpectation(new MockLogAppender.SeenEventExpectation("test2", "cluster.service", Level.TRACE,
|
||||||
|
"*failed to execute cluster state update in [2s]*"));
|
||||||
|
mockAppender.addExpectation(new MockLogAppender.SeenEventExpectation("test3", "cluster.service", Level.DEBUG,
|
||||||
|
"*processing [test3]: took [3s] done applying updated cluster_state (version: *, uuid: *)"));
|
||||||
|
|
||||||
|
Logger rootLogger = Logger.getRootLogger();
|
||||||
|
rootLogger.addAppender(mockAppender);
|
||||||
|
try {
|
||||||
|
final CountDownLatch latch = new CountDownLatch(4);
|
||||||
|
clusterService.currentTimeOverride = System.nanoTime();
|
||||||
|
clusterService.submitStateUpdateTask("test1", new ClusterStateUpdateTask() {
|
||||||
|
@Override
|
||||||
|
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||||
|
clusterService.currentTimeOverride += TimeValue.timeValueSeconds(1).nanos();
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
clusterService.submitStateUpdateTask("test2", new ClusterStateUpdateTask() {
|
||||||
|
@Override
|
||||||
|
public ClusterState execute(ClusterState currentState) {
|
||||||
|
clusterService.currentTimeOverride += TimeValue.timeValueSeconds(2).nanos();
|
||||||
|
throw new IllegalArgumentException("Testing handling of exceptions in the cluster state task");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
clusterService.submitStateUpdateTask("test3", new ClusterStateUpdateTask() {
|
||||||
|
@Override
|
||||||
|
public ClusterState execute(ClusterState currentState) {
|
||||||
|
clusterService.currentTimeOverride += TimeValue.timeValueSeconds(3).nanos();
|
||||||
|
return ClusterState.builder(currentState).incrementVersion().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Additional update task to make sure all previous logging made it to the logger
|
||||||
|
// We don't check logging for this on since there is no guarantee that it will occur before our check
|
||||||
|
clusterService.submitStateUpdateTask("test4", new ClusterStateUpdateTask() {
|
||||||
|
@Override
|
||||||
|
public ClusterState execute(ClusterState currentState) {
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
latch.await();
|
||||||
|
} finally {
|
||||||
|
rootLogger.removeAppender(mockAppender);
|
||||||
|
}
|
||||||
|
mockAppender.assertAllExpectationsMatched();
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestLogging("cluster:WARN") // To ensure that we log cluster state events on WARN level
|
||||||
|
public void testLongClusterStateUpdateLogging() throws Exception {
|
||||||
|
MockLogAppender mockAppender = new MockLogAppender();
|
||||||
|
mockAppender.addExpectation(new MockLogAppender.UnseenEventExpectation("test1 shouldn't see because setting is too low",
|
||||||
|
"cluster.service", Level.WARN, "*cluster state update task [test1] took [*] above the warn threshold of *"));
|
||||||
|
mockAppender.addExpectation(new MockLogAppender.SeenEventExpectation("test2", "cluster.service", Level.WARN,
|
||||||
|
"*cluster state update task [test2] took [32s] above the warn threshold of *"));
|
||||||
|
mockAppender.addExpectation(new MockLogAppender.SeenEventExpectation("test3", "cluster.service", Level.WARN,
|
||||||
|
"*cluster state update task [test3] took [33s] above the warn threshold of *"));
|
||||||
|
mockAppender.addExpectation(new MockLogAppender.SeenEventExpectation("test4", "cluster.service", Level.WARN,
|
||||||
|
"*cluster state update task [test4] took [34s] above the warn threshold of *"));
|
||||||
|
|
||||||
|
Logger rootLogger = Logger.getRootLogger();
|
||||||
|
rootLogger.addAppender(mockAppender);
|
||||||
|
try {
|
||||||
|
final CountDownLatch latch = new CountDownLatch(5);
|
||||||
|
final CountDownLatch processedFirstTask = new CountDownLatch(1);
|
||||||
|
clusterService.currentTimeOverride = System.nanoTime();
|
||||||
|
clusterService.submitStateUpdateTask("test1", new ClusterStateUpdateTask() {
|
||||||
|
@Override
|
||||||
|
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||||
|
clusterService.currentTimeOverride += TimeValue.timeValueSeconds(1).nanos();
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||||
|
latch.countDown();
|
||||||
|
processedFirstTask.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
processedFirstTask.await();
|
||||||
|
clusterService.submitStateUpdateTask("test2", new ClusterStateUpdateTask() {
|
||||||
|
@Override
|
||||||
|
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||||
|
clusterService.currentTimeOverride += TimeValue.timeValueSeconds(32).nanos();
|
||||||
|
throw new IllegalArgumentException("Testing handling of exceptions in the cluster state task");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
clusterService.submitStateUpdateTask("test3", new ClusterStateUpdateTask() {
|
||||||
|
@Override
|
||||||
|
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||||
|
clusterService.currentTimeOverride += TimeValue.timeValueSeconds(33).nanos();
|
||||||
|
return ClusterState.builder(currentState).incrementVersion().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
clusterService.submitStateUpdateTask("test4", new ClusterStateUpdateTask() {
|
||||||
|
@Override
|
||||||
|
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||||
|
clusterService.currentTimeOverride += TimeValue.timeValueSeconds(34).nanos();
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Additional update task to make sure all previous logging made it to the logger
|
||||||
|
// We don't check logging for this on since there is no guarantee that it will occur before our check
|
||||||
|
clusterService.submitStateUpdateTask("test5", new ClusterStateUpdateTask() {
|
||||||
|
@Override
|
||||||
|
public ClusterState execute(ClusterState currentState) {
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
latch.await();
|
||||||
|
} finally {
|
||||||
|
rootLogger.removeAppender(mockAppender);
|
||||||
|
}
|
||||||
|
mockAppender.assertAllExpectationsMatched();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BlockingTask extends ClusterStateUpdateTask {
|
||||||
|
private final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
|
||||||
|
public BlockingTask(Priority priority) {
|
||||||
|
super(priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||||
|
latch.await();
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void release() {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PrioritizedTask extends ClusterStateUpdateTask {
|
||||||
|
|
||||||
|
private final CountDownLatch latch;
|
||||||
|
private final List<PrioritizedTask> tasks;
|
||||||
|
|
||||||
|
private PrioritizedTask(Priority priority, CountDownLatch latch, List<PrioritizedTask> tasks) {
|
||||||
|
super(priority);
|
||||||
|
this.latch = latch;
|
||||||
|
this.tasks = tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||||
|
tasks.add(this);
|
||||||
|
latch.countDown();
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Throwable t) {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TimedClusterService extends InternalClusterService {
|
||||||
|
|
||||||
|
public volatile Long currentTimeOverride = null;
|
||||||
|
|
||||||
|
public TimedClusterService(Settings settings, OperationRouting operationRouting, ClusterSettings clusterSettings,
|
||||||
|
ThreadPool threadPool, ClusterName clusterName) {
|
||||||
|
super(settings, operationRouting, clusterSettings, threadPool, clusterName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected long currentTimeInNanos() {
|
||||||
|
if (currentTimeOverride != null) {
|
||||||
|
return currentTimeOverride;
|
||||||
|
}
|
||||||
|
return super.currentTimeInNanos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -492,7 +492,7 @@ public class NodeJoinControllerTests extends ESTestCase {
|
|||||||
static class NoopAllocationService extends AllocationService {
|
static class NoopAllocationService extends AllocationService {
|
||||||
|
|
||||||
public NoopAllocationService(Settings settings) {
|
public NoopAllocationService(Settings settings) {
|
||||||
super(settings, null, null, null);
|
super(settings, null, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -19,12 +19,20 @@
|
|||||||
|
|
||||||
package org.elasticsearch.index.mapper;
|
package org.elasticsearch.index.mapper;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.elasticsearch.common.bytes.BytesReference;
|
import org.elasticsearch.common.bytes.BytesReference;
|
||||||
import org.elasticsearch.common.compress.CompressedXContent;
|
import org.elasticsearch.common.compress.CompressedXContent;
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.index.mapper.internal.UidFieldMapper;
|
import org.elasticsearch.index.mapper.internal.UidFieldMapper;
|
||||||
|
import org.elasticsearch.index.mapper.object.ObjectMapper;
|
||||||
import org.elasticsearch.test.ESSingleNodeTestCase;
|
import org.elasticsearch.test.ESSingleNodeTestCase;
|
||||||
|
|
||||||
|
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||||
|
|
||||||
// TODO: make this a real unit test
|
// TODO: make this a real unit test
|
||||||
public class DocumentParserTests extends ESSingleNodeTestCase {
|
public class DocumentParserTests extends ESSingleNodeTestCase {
|
||||||
|
|
||||||
@ -61,4 +69,113 @@ public class DocumentParserTests extends ESSingleNodeTestCase {
|
|||||||
assertNotNull(doc.rootDoc().getField("bar"));
|
assertNotNull(doc.rootDoc().getField("bar"));
|
||||||
assertNotNull(doc.rootDoc().getField(UidFieldMapper.NAME));
|
assertNotNull(doc.rootDoc().getField(UidFieldMapper.NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DocumentMapper createDummyMapping(MapperService mapperService) throws Exception {
|
||||||
|
String mapping = jsonBuilder().startObject().startObject("type").startObject("properties")
|
||||||
|
.startObject("y").field("type", "object").endObject()
|
||||||
|
.startObject("x").startObject("properties")
|
||||||
|
.startObject("subx").field("type", "object").startObject("properties")
|
||||||
|
.startObject("subsubx").field("type", "object")
|
||||||
|
.endObject().endObject().endObject().endObject().endObject().endObject().endObject().endObject().string();
|
||||||
|
|
||||||
|
DocumentMapper defaultMapper = mapperService.documentMapperParser().parse("type", new CompressedXContent(mapping));
|
||||||
|
return defaultMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
// creates an object mapper, which is about 100x harder than it should be....
|
||||||
|
ObjectMapper createObjectMapper(MapperService mapperService, String name) throws Exception {
|
||||||
|
String[] nameParts = name.split("\\.");
|
||||||
|
ContentPath path = new ContentPath();
|
||||||
|
for (int i = 0; i < nameParts.length - 1; ++i) {
|
||||||
|
path.add(nameParts[i]);
|
||||||
|
}
|
||||||
|
ParseContext context = new ParseContext.InternalParseContext(Settings.EMPTY,
|
||||||
|
mapperService.documentMapperParser(), mapperService.documentMapper("type"), path);
|
||||||
|
Mapper.Builder builder = new ObjectMapper.Builder(nameParts[nameParts.length - 1]).enabled(true);
|
||||||
|
Mapper.BuilderContext builderContext = new Mapper.BuilderContext(context.indexSettings(), context.path());
|
||||||
|
return (ObjectMapper)builder.build(builderContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEmptyMappingUpdate() throws Exception {
|
||||||
|
DocumentMapper docMapper = createDummyMapping(createIndex("test").mapperService());
|
||||||
|
assertNull(DocumentParser.createDynamicUpdate(docMapper.mapping(), docMapper, Collections.emptyList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSingleMappingUpdate() throws Exception {
|
||||||
|
DocumentMapper docMapper = createDummyMapping(createIndex("test").mapperService());
|
||||||
|
List<Mapper> updates = Collections.singletonList(new MockFieldMapper("foo"));
|
||||||
|
Mapping mapping = DocumentParser.createDynamicUpdate(docMapper.mapping(), docMapper, updates);
|
||||||
|
assertNotNull(mapping.root().getMapper("foo"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSubfieldMappingUpdate() throws Exception {
|
||||||
|
DocumentMapper docMapper = createDummyMapping(createIndex("test").mapperService());
|
||||||
|
List<Mapper> updates = Collections.singletonList(new MockFieldMapper("x.foo"));
|
||||||
|
Mapping mapping = DocumentParser.createDynamicUpdate(docMapper.mapping(), docMapper, updates);
|
||||||
|
Mapper xMapper = mapping.root().getMapper("x");
|
||||||
|
assertNotNull(xMapper);
|
||||||
|
assertTrue(xMapper instanceof ObjectMapper);
|
||||||
|
assertNotNull(((ObjectMapper)xMapper).getMapper("foo"));
|
||||||
|
assertNull(((ObjectMapper)xMapper).getMapper("subx"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMultipleSubfieldMappingUpdate() throws Exception {
|
||||||
|
DocumentMapper docMapper = createDummyMapping(createIndex("test").mapperService());
|
||||||
|
List<Mapper> updates = new ArrayList<>();
|
||||||
|
updates.add(new MockFieldMapper("x.foo"));
|
||||||
|
updates.add(new MockFieldMapper("x.bar"));
|
||||||
|
Mapping mapping = DocumentParser.createDynamicUpdate(docMapper.mapping(), docMapper, updates);
|
||||||
|
Mapper xMapper = mapping.root().getMapper("x");
|
||||||
|
assertNotNull(xMapper);
|
||||||
|
assertTrue(xMapper instanceof ObjectMapper);
|
||||||
|
assertNotNull(((ObjectMapper)xMapper).getMapper("foo"));
|
||||||
|
assertNotNull(((ObjectMapper)xMapper).getMapper("bar"));
|
||||||
|
assertNull(((ObjectMapper)xMapper).getMapper("subx"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDeepSubfieldMappingUpdate() throws Exception {
|
||||||
|
DocumentMapper docMapper = createDummyMapping(createIndex("test").mapperService());
|
||||||
|
List<Mapper> updates = Collections.singletonList(new MockFieldMapper("x.subx.foo"));
|
||||||
|
Mapping mapping = DocumentParser.createDynamicUpdate(docMapper.mapping(), docMapper, updates);
|
||||||
|
Mapper xMapper = mapping.root().getMapper("x");
|
||||||
|
assertNotNull(xMapper);
|
||||||
|
assertTrue(xMapper instanceof ObjectMapper);
|
||||||
|
Mapper subxMapper = ((ObjectMapper)xMapper).getMapper("subx");
|
||||||
|
assertTrue(subxMapper instanceof ObjectMapper);
|
||||||
|
assertNotNull(((ObjectMapper)subxMapper).getMapper("foo"));
|
||||||
|
assertNull(((ObjectMapper)subxMapper).getMapper("subsubx"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDeepSubfieldAfterSubfieldMappingUpdate() throws Exception {
|
||||||
|
DocumentMapper docMapper = createDummyMapping(createIndex("test").mapperService());
|
||||||
|
List<Mapper> updates = new ArrayList<>();
|
||||||
|
updates.add(new MockFieldMapper("x.a"));
|
||||||
|
updates.add(new MockFieldMapper("x.subx.b"));
|
||||||
|
Mapping mapping = DocumentParser.createDynamicUpdate(docMapper.mapping(), docMapper, updates);
|
||||||
|
Mapper xMapper = mapping.root().getMapper("x");
|
||||||
|
assertNotNull(xMapper);
|
||||||
|
assertTrue(xMapper instanceof ObjectMapper);
|
||||||
|
assertNotNull(((ObjectMapper)xMapper).getMapper("a"));
|
||||||
|
Mapper subxMapper = ((ObjectMapper)xMapper).getMapper("subx");
|
||||||
|
assertTrue(subxMapper instanceof ObjectMapper);
|
||||||
|
assertNotNull(((ObjectMapper)subxMapper).getMapper("b"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testObjectMappingUpdate() throws Exception {
|
||||||
|
MapperService mapperService = createIndex("test").mapperService();
|
||||||
|
DocumentMapper docMapper = createDummyMapping(mapperService);
|
||||||
|
List<Mapper> updates = new ArrayList<>();
|
||||||
|
updates.add(createObjectMapper(mapperService, "foo"));
|
||||||
|
updates.add(createObjectMapper(mapperService, "foo.bar"));
|
||||||
|
updates.add(new MockFieldMapper("foo.bar.baz"));
|
||||||
|
updates.add(new MockFieldMapper("foo.field"));
|
||||||
|
Mapping mapping = DocumentParser.createDynamicUpdate(docMapper.mapping(), docMapper, updates);
|
||||||
|
Mapper fooMapper = mapping.root().getMapper("foo");
|
||||||
|
assertNotNull(fooMapper);
|
||||||
|
assertTrue(fooMapper instanceof ObjectMapper);
|
||||||
|
assertNotNull(((ObjectMapper)fooMapper).getMapper("field"));
|
||||||
|
Mapper barMapper = ((ObjectMapper)fooMapper).getMapper("bar");
|
||||||
|
assertTrue(barMapper instanceof ObjectMapper);
|
||||||
|
assertNotNull(((ObjectMapper)barMapper).getMapper("baz"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ import org.elasticsearch.index.mapper.core.TextFieldMapper;
|
|||||||
import org.elasticsearch.test.ESSingleNodeTestCase;
|
import org.elasticsearch.test.ESSingleNodeTestCase;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static java.util.Collections.emptyMap;
|
import static java.util.Collections.emptyMap;
|
||||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||||
@ -211,7 +212,9 @@ public class DynamicMappingTests extends ESSingleNodeTestCase {
|
|||||||
ctx.reset(XContentHelper.createParser(source.source()), new ParseContext.Document(), source);
|
ctx.reset(XContentHelper.createParser(source.source()), new ParseContext.Document(), source);
|
||||||
assertEquals(XContentParser.Token.START_OBJECT, ctx.parser().nextToken());
|
assertEquals(XContentParser.Token.START_OBJECT, ctx.parser().nextToken());
|
||||||
ctx.parser().nextToken();
|
ctx.parser().nextToken();
|
||||||
return DocumentParser.parseObject(ctx, mapper.root(), true);
|
DocumentParser.parseObjectOrNested(ctx, mapper.root(), true);
|
||||||
|
Mapping mapping = DocumentParser.createDynamicUpdate(mapper.mapping(), mapper, ctx.getDynamicMappers());
|
||||||
|
return mapping == null ? null : mapping.root();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDynamicMappingsNotNeeded() throws Exception {
|
public void testDynamicMappingsNotNeeded() throws Exception {
|
||||||
|
@ -19,12 +19,8 @@
|
|||||||
|
|
||||||
package org.elasticsearch.index.mapper;
|
package org.elasticsearch.index.mapper;
|
||||||
|
|
||||||
import org.elasticsearch.Version;
|
|
||||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
|
||||||
import org.elasticsearch.common.settings.Settings;
|
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -59,7 +55,7 @@ public class FieldTypeLookupTests extends ESTestCase {
|
|||||||
|
|
||||||
public void testAddNewField() {
|
public void testAddNewField() {
|
||||||
FieldTypeLookup lookup = new FieldTypeLookup();
|
FieldTypeLookup lookup = new FieldTypeLookup();
|
||||||
FakeFieldMapper f = new FakeFieldMapper("foo");
|
MockFieldMapper f = new MockFieldMapper("foo");
|
||||||
FieldTypeLookup lookup2 = lookup.copyAndAddAll("type", newList(f), randomBoolean());
|
FieldTypeLookup lookup2 = lookup.copyAndAddAll("type", newList(f), randomBoolean());
|
||||||
assertNull(lookup.get("foo"));
|
assertNull(lookup.get("foo"));
|
||||||
assertNull(lookup.get("bar"));
|
assertNull(lookup.get("bar"));
|
||||||
@ -73,8 +69,8 @@ public class FieldTypeLookupTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testAddExistingField() {
|
public void testAddExistingField() {
|
||||||
FakeFieldMapper f = new FakeFieldMapper("foo");
|
MockFieldMapper f = new MockFieldMapper("foo");
|
||||||
FakeFieldMapper f2 = new FakeFieldMapper("foo");
|
MockFieldMapper f2 = new MockFieldMapper("foo");
|
||||||
FieldTypeLookup lookup = new FieldTypeLookup();
|
FieldTypeLookup lookup = new FieldTypeLookup();
|
||||||
lookup = lookup.copyAndAddAll("type1", newList(f), randomBoolean());
|
lookup = lookup.copyAndAddAll("type1", newList(f), randomBoolean());
|
||||||
FieldTypeLookup lookup2 = lookup.copyAndAddAll("type2", newList(f2), randomBoolean());
|
FieldTypeLookup lookup2 = lookup.copyAndAddAll("type2", newList(f2), randomBoolean());
|
||||||
@ -84,8 +80,8 @@ public class FieldTypeLookupTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testAddExistingIndexName() {
|
public void testAddExistingIndexName() {
|
||||||
FakeFieldMapper f = new FakeFieldMapper("foo");
|
MockFieldMapper f = new MockFieldMapper("foo");
|
||||||
FakeFieldMapper f2 = new FakeFieldMapper("bar");
|
MockFieldMapper f2 = new MockFieldMapper("bar");
|
||||||
FieldTypeLookup lookup = new FieldTypeLookup();
|
FieldTypeLookup lookup = new FieldTypeLookup();
|
||||||
lookup = lookup.copyAndAddAll("type1", newList(f), randomBoolean());
|
lookup = lookup.copyAndAddAll("type1", newList(f), randomBoolean());
|
||||||
FieldTypeLookup lookup2 = lookup.copyAndAddAll("type2", newList(f2), randomBoolean());
|
FieldTypeLookup lookup2 = lookup.copyAndAddAll("type2", newList(f2), randomBoolean());
|
||||||
@ -96,8 +92,8 @@ public class FieldTypeLookupTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testAddExistingFullName() {
|
public void testAddExistingFullName() {
|
||||||
FakeFieldMapper f = new FakeFieldMapper("foo");
|
MockFieldMapper f = new MockFieldMapper("foo");
|
||||||
FakeFieldMapper f2 = new FakeFieldMapper("foo");
|
MockFieldMapper f2 = new MockFieldMapper("foo");
|
||||||
FieldTypeLookup lookup = new FieldTypeLookup();
|
FieldTypeLookup lookup = new FieldTypeLookup();
|
||||||
try {
|
try {
|
||||||
lookup.copyAndAddAll("type2", newList(f2), randomBoolean());
|
lookup.copyAndAddAll("type2", newList(f2), randomBoolean());
|
||||||
@ -107,12 +103,13 @@ public class FieldTypeLookupTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testCheckCompatibilityMismatchedTypes() {
|
public void testCheckCompatibilityMismatchedTypes() {
|
||||||
FieldMapper f1 = new FakeFieldMapper("foo");
|
FieldMapper f1 = new MockFieldMapper("foo");
|
||||||
FieldTypeLookup lookup = new FieldTypeLookup();
|
FieldTypeLookup lookup = new FieldTypeLookup();
|
||||||
lookup = lookup.copyAndAddAll("type", newList(f1), randomBoolean());
|
lookup = lookup.copyAndAddAll("type", newList(f1), randomBoolean());
|
||||||
|
|
||||||
MappedFieldType ft2 = FakeFieldMapper.makeOtherFieldType("foo");
|
OtherFakeFieldType ft2 = new OtherFakeFieldType();
|
||||||
FieldMapper f2 = new FakeFieldMapper("foo", ft2);
|
ft2.setName("foo");
|
||||||
|
FieldMapper f2 = new MockFieldMapper("foo", ft2);
|
||||||
try {
|
try {
|
||||||
lookup.copyAndAddAll("type2", newList(f2), false);
|
lookup.copyAndAddAll("type2", newList(f2), false);
|
||||||
fail("expected type mismatch");
|
fail("expected type mismatch");
|
||||||
@ -129,13 +126,14 @@ public class FieldTypeLookupTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testCheckCompatibilityConflict() {
|
public void testCheckCompatibilityConflict() {
|
||||||
FieldMapper f1 = new FakeFieldMapper("foo");
|
FieldMapper f1 = new MockFieldMapper("foo");
|
||||||
FieldTypeLookup lookup = new FieldTypeLookup();
|
FieldTypeLookup lookup = new FieldTypeLookup();
|
||||||
lookup = lookup.copyAndAddAll("type", newList(f1), randomBoolean());
|
lookup = lookup.copyAndAddAll("type", newList(f1), randomBoolean());
|
||||||
|
|
||||||
MappedFieldType ft2 = FakeFieldMapper.makeFieldType("foo");
|
MappedFieldType ft2 = new MockFieldMapper.FakeFieldType();
|
||||||
|
ft2.setName("foo");
|
||||||
ft2.setBoost(2.0f);
|
ft2.setBoost(2.0f);
|
||||||
FieldMapper f2 = new FakeFieldMapper("foo", ft2);
|
FieldMapper f2 = new MockFieldMapper("foo", ft2);
|
||||||
try {
|
try {
|
||||||
// different type
|
// different type
|
||||||
lookup.copyAndAddAll("type2", newList(f2), false);
|
lookup.copyAndAddAll("type2", newList(f2), false);
|
||||||
@ -146,9 +144,10 @@ public class FieldTypeLookupTests extends ESTestCase {
|
|||||||
lookup.copyAndAddAll("type", newList(f2), false); // boost is updateable, so ok since we are implicitly updating all types
|
lookup.copyAndAddAll("type", newList(f2), false); // boost is updateable, so ok since we are implicitly updating all types
|
||||||
lookup.copyAndAddAll("type2", newList(f2), true); // boost is updateable, so ok if forcing
|
lookup.copyAndAddAll("type2", newList(f2), true); // boost is updateable, so ok if forcing
|
||||||
// now with a non changeable setting
|
// now with a non changeable setting
|
||||||
MappedFieldType ft3 = FakeFieldMapper.makeFieldType("foo");
|
MappedFieldType ft3 = new MockFieldMapper.FakeFieldType();
|
||||||
|
ft3.setName("foo");
|
||||||
ft3.setStored(true);
|
ft3.setStored(true);
|
||||||
FieldMapper f3 = new FakeFieldMapper("foo", ft3);
|
FieldMapper f3 = new MockFieldMapper("foo", ft3);
|
||||||
try {
|
try {
|
||||||
lookup.copyAndAddAll("type2", newList(f3), false);
|
lookup.copyAndAddAll("type2", newList(f3), false);
|
||||||
fail("expected conflict");
|
fail("expected conflict");
|
||||||
@ -165,8 +164,8 @@ public class FieldTypeLookupTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testSimpleMatchFullNames() {
|
public void testSimpleMatchFullNames() {
|
||||||
FakeFieldMapper f1 = new FakeFieldMapper("foo");
|
MockFieldMapper f1 = new MockFieldMapper("foo");
|
||||||
FakeFieldMapper f2 = new FakeFieldMapper("bar");
|
MockFieldMapper f2 = new MockFieldMapper("bar");
|
||||||
FieldTypeLookup lookup = new FieldTypeLookup();
|
FieldTypeLookup lookup = new FieldTypeLookup();
|
||||||
lookup = lookup.copyAndAddAll("type", newList(f1, f2), randomBoolean());
|
lookup = lookup.copyAndAddAll("type", newList(f1, f2), randomBoolean());
|
||||||
Collection<String> names = lookup.simpleMatchToFullName("b*");
|
Collection<String> names = lookup.simpleMatchToFullName("b*");
|
||||||
@ -175,7 +174,7 @@ public class FieldTypeLookupTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testIteratorImmutable() {
|
public void testIteratorImmutable() {
|
||||||
FakeFieldMapper f1 = new FakeFieldMapper("foo");
|
MockFieldMapper f1 = new MockFieldMapper("foo");
|
||||||
FieldTypeLookup lookup = new FieldTypeLookup();
|
FieldTypeLookup lookup = new FieldTypeLookup();
|
||||||
lookup = lookup.copyAndAddAll("type", newList(f1), randomBoolean());
|
lookup = lookup.copyAndAddAll("type", newList(f1), randomBoolean());
|
||||||
|
|
||||||
@ -194,59 +193,6 @@ public class FieldTypeLookupTests extends ESTestCase {
|
|||||||
return Arrays.asList(mapper);
|
return Arrays.asList(mapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
// this sucks how much must be overridden just do get a dummy field mapper...
|
|
||||||
static class FakeFieldMapper extends FieldMapper {
|
|
||||||
static Settings dummySettings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT.id).build();
|
|
||||||
public FakeFieldMapper(String fullName) {
|
|
||||||
super(fullName, makeFieldType(fullName), makeFieldType(fullName), dummySettings, null, null);
|
|
||||||
}
|
|
||||||
public FakeFieldMapper(String fullName, MappedFieldType fieldType) {
|
|
||||||
super(fullName, fieldType, fieldType, dummySettings, null, null);
|
|
||||||
}
|
|
||||||
static MappedFieldType makeFieldType(String fullName) {
|
|
||||||
FakeFieldType fieldType = new FakeFieldType();
|
|
||||||
fieldType.setName(fullName);
|
|
||||||
return fieldType;
|
|
||||||
}
|
|
||||||
static MappedFieldType makeOtherFieldType(String fullName) {
|
|
||||||
OtherFakeFieldType fieldType = new OtherFakeFieldType();
|
|
||||||
fieldType.setName(fullName);
|
|
||||||
return fieldType;
|
|
||||||
}
|
|
||||||
static class FakeFieldType extends MappedFieldType {
|
|
||||||
public FakeFieldType() {}
|
|
||||||
protected FakeFieldType(FakeFieldType ref) {
|
|
||||||
super(ref);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public MappedFieldType clone() {
|
|
||||||
return new FakeFieldType(this);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public String typeName() {
|
|
||||||
return "faketype";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static class OtherFakeFieldType extends MappedFieldType {
|
|
||||||
public OtherFakeFieldType() {}
|
|
||||||
protected OtherFakeFieldType(OtherFakeFieldType ref) {
|
|
||||||
super(ref);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public MappedFieldType clone() {
|
|
||||||
return new OtherFakeFieldType(this);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public String typeName() {
|
|
||||||
return "otherfaketype";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
protected String contentType() { return null; }
|
|
||||||
@Override
|
|
||||||
protected void parseCreateField(ParseContext context, List list) throws IOException {}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int size(Iterator<MappedFieldType> iterator) {
|
private int size(Iterator<MappedFieldType> iterator) {
|
||||||
if (iterator == null) {
|
if (iterator == null) {
|
||||||
throw new NullPointerException("iterator");
|
throw new NullPointerException("iterator");
|
||||||
@ -258,4 +204,23 @@ public class FieldTypeLookupTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class OtherFakeFieldType extends MappedFieldType {
|
||||||
|
public OtherFakeFieldType() {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected OtherFakeFieldType(OtherFakeFieldType ref) {
|
||||||
|
super(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MappedFieldType clone() {
|
||||||
|
return new OtherFakeFieldType(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeName() {
|
||||||
|
return "otherfaketype";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,177 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.index.mapper.core;
|
||||||
|
|
||||||
|
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
|
||||||
|
|
||||||
|
import org.apache.lucene.index.IndexOptions;
|
||||||
|
import org.elasticsearch.Version;
|
||||||
|
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||||
|
import org.elasticsearch.common.compress.CompressedXContent;
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
|
import org.elasticsearch.index.IndexService;
|
||||||
|
import org.elasticsearch.index.mapper.DocumentMapper;
|
||||||
|
import org.elasticsearch.index.mapper.DocumentMapperParser;
|
||||||
|
import org.elasticsearch.index.mapper.FieldMapper;
|
||||||
|
import org.elasticsearch.plugins.Plugin;
|
||||||
|
import org.elasticsearch.test.ESSingleNodeTestCase;
|
||||||
|
import org.elasticsearch.test.InternalSettingsPlugin;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
|
|
||||||
|
public class StringMappingUpgradeTests extends ESSingleNodeTestCase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Collection<Class<? extends Plugin>> getPlugins() {
|
||||||
|
return pluginList(InternalSettingsPlugin.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testUpgradeDefaults() throws IOException {
|
||||||
|
IndexService indexService = createIndex("test");
|
||||||
|
DocumentMapperParser parser = indexService.mapperService().documentMapperParser();
|
||||||
|
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||||
|
.startObject("properties").startObject("field").field("type", "string").endObject().endObject()
|
||||||
|
.endObject().endObject().string();
|
||||||
|
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
|
||||||
|
FieldMapper field = mapper.mappers().getMapper("field");
|
||||||
|
assertThat(field, instanceOf(TextFieldMapper.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testUpgradeAnalyzedString() throws IOException {
|
||||||
|
IndexService indexService = createIndex("test");
|
||||||
|
DocumentMapperParser parser = indexService.mapperService().documentMapperParser();
|
||||||
|
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||||
|
.startObject("properties").startObject("field").field("type", "string").field("index", "analyzed").endObject().endObject()
|
||||||
|
.endObject().endObject().string();
|
||||||
|
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
|
||||||
|
FieldMapper field = mapper.mappers().getMapper("field");
|
||||||
|
assertThat(field, instanceOf(TextFieldMapper.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testUpgradeNotAnalyzedString() throws IOException {
|
||||||
|
IndexService indexService = createIndex("test");
|
||||||
|
DocumentMapperParser parser = indexService.mapperService().documentMapperParser();
|
||||||
|
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||||
|
.startObject("properties").startObject("field").field("type", "string")
|
||||||
|
.field("index", "not_analyzed").endObject().endObject()
|
||||||
|
.endObject().endObject().string();
|
||||||
|
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
|
||||||
|
FieldMapper field = mapper.mappers().getMapper("field");
|
||||||
|
assertThat(field, instanceOf(KeywordFieldMapper.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testUpgradeNotIndexedString() throws IOException {
|
||||||
|
IndexService indexService = createIndex("test");
|
||||||
|
DocumentMapperParser parser = indexService.mapperService().documentMapperParser();
|
||||||
|
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||||
|
.startObject("properties").startObject("field").field("type", "string").field("index", "no").endObject().endObject()
|
||||||
|
.endObject().endObject().string();
|
||||||
|
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
|
||||||
|
FieldMapper field = mapper.mappers().getMapper("field");
|
||||||
|
assertThat(field, instanceOf(KeywordFieldMapper.class));
|
||||||
|
assertEquals(IndexOptions.NONE, field.fieldType().indexOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNotSupportedUpgrade() throws IOException {
|
||||||
|
IndexService indexService = createIndex("test");
|
||||||
|
DocumentMapperParser parser = indexService.mapperService().documentMapperParser();
|
||||||
|
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||||
|
.startObject("properties").startObject("field").field("type", "string").field("analyzer", "keyword").endObject().endObject()
|
||||||
|
.endObject().endObject().string();
|
||||||
|
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||||
|
() -> parser.parse("type", new CompressedXContent(mapping)));
|
||||||
|
assertThat(e.getMessage(), containsString("The [string] type is removed in 5.0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testUpgradeRandomMapping() throws IOException {
|
||||||
|
final int iters = 20;
|
||||||
|
for (int i = 0; i < iters; ++i) {
|
||||||
|
doTestUpgradeRandomMapping(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doTestUpgradeRandomMapping(int iter) throws IOException {
|
||||||
|
IndexService indexService;
|
||||||
|
boolean oldIndex = randomBoolean();
|
||||||
|
String indexName = "test" + iter;
|
||||||
|
if (oldIndex) {
|
||||||
|
Settings settings = Settings.builder()
|
||||||
|
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_2_3_0)
|
||||||
|
.build();
|
||||||
|
indexService = createIndex(indexName, settings);
|
||||||
|
} else {
|
||||||
|
indexService = createIndex(indexName);
|
||||||
|
}
|
||||||
|
DocumentMapperParser parser = indexService.mapperService().documentMapperParser();
|
||||||
|
XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||||
|
.startObject("properties").startObject("field").field("type", "string");
|
||||||
|
boolean keyword = randomBoolean();
|
||||||
|
boolean shouldUpgrade = true;
|
||||||
|
if (keyword) {
|
||||||
|
mapping.field("index", randomBoolean() ? "not_analyzed" : "no");
|
||||||
|
} else if (randomBoolean()) {
|
||||||
|
mapping.field("index", "analyzed");
|
||||||
|
}
|
||||||
|
if (randomBoolean()) {
|
||||||
|
mapping.field("store", RandomPicks.randomFrom(random(), Arrays.asList("yes", "no", true, false)));
|
||||||
|
}
|
||||||
|
if (keyword && randomBoolean()) {
|
||||||
|
mapping.field("doc_values", randomBoolean());
|
||||||
|
}
|
||||||
|
if (randomBoolean()) {
|
||||||
|
mapping.field("omit_norms", randomBoolean());
|
||||||
|
}
|
||||||
|
if (randomBoolean()) {
|
||||||
|
mapping.startObject("fields").startObject("raw").field("type", "keyword").endObject().endObject();
|
||||||
|
}
|
||||||
|
if (randomBoolean()) {
|
||||||
|
mapping.field("copy_to", "bar");
|
||||||
|
}
|
||||||
|
if (randomBoolean()) {
|
||||||
|
// this option is not upgraded automatically
|
||||||
|
mapping.field("index_options", "docs");
|
||||||
|
shouldUpgrade = false;
|
||||||
|
}
|
||||||
|
mapping.endObject().endObject().endObject().endObject();
|
||||||
|
|
||||||
|
if (oldIndex == false && shouldUpgrade == false) {
|
||||||
|
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||||
|
() -> parser.parse("type", new CompressedXContent(mapping.string())));
|
||||||
|
assertThat(e.getMessage(), containsString("The [string] type is removed in 5.0"));
|
||||||
|
} else {
|
||||||
|
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping.string()));
|
||||||
|
FieldMapper field = mapper.mappers().getMapper("field");
|
||||||
|
if (oldIndex) {
|
||||||
|
assertThat(field, instanceOf(StringFieldMapper.class));
|
||||||
|
} else if (keyword) {
|
||||||
|
assertThat(field, instanceOf(KeywordFieldMapper.class));
|
||||||
|
} else {
|
||||||
|
assertThat(field, instanceOf(TextFieldMapper.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -28,32 +28,28 @@ import org.elasticsearch.test.ESSingleNodeTestCase;
|
|||||||
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
public class SimpleObjectMappingTests extends ESSingleNodeTestCase {
|
public class SimpleObjectMappingTests extends ESSingleNodeTestCase {
|
||||||
public void testDifferentInnerObjectTokenFailure() throws Exception {
|
public void testDifferentInnerObjectTokenFailure() throws Exception {
|
||||||
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||||
.endObject().endObject().string();
|
.endObject().endObject().string();
|
||||||
|
|
||||||
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type", new CompressedXContent(mapping));
|
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type", new CompressedXContent(mapping));
|
||||||
try {
|
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> {
|
||||||
defaultMapper.parse("test", "type", "1", new BytesArray(" {\n" +
|
defaultMapper.parse("test", "type", "1", new BytesArray(" {\n" +
|
||||||
" \"object\": {\n" +
|
" \"object\": {\n" +
|
||||||
" \"array\":[\n" +
|
" \"array\":[\n" +
|
||||||
" {\n" +
|
" {\n" +
|
||||||
" \"object\": { \"value\": \"value\" }\n" +
|
" \"object\": { \"value\": \"value\" }\n" +
|
||||||
" },\n" +
|
" },\n" +
|
||||||
" {\n" +
|
" {\n" +
|
||||||
" \"object\":\"value\"\n" +
|
" \"object\":\"value\"\n" +
|
||||||
" }\n" +
|
" }\n" +
|
||||||
" ]\n" +
|
" ]\n" +
|
||||||
" },\n" +
|
" },\n" +
|
||||||
" \"value\":\"value\"\n" +
|
" \"value\":\"value\"\n" +
|
||||||
" }"));
|
" }"));
|
||||||
fail();
|
});
|
||||||
} catch (MapperParsingException e) {
|
assertTrue(e.getMessage(), e.getMessage().contains("different type"));
|
||||||
// all is well
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEmptyArrayProperties() throws Exception {
|
public void testEmptyArrayProperties() throws Exception {
|
||||||
|
@ -25,6 +25,7 @@ import org.elasticsearch.action.bulk.BulkRequest;
|
|||||||
import org.elasticsearch.action.delete.DeleteRequest;
|
import org.elasticsearch.action.delete.DeleteRequest;
|
||||||
import org.elasticsearch.action.index.IndexRequest;
|
import org.elasticsearch.action.index.IndexRequest;
|
||||||
import org.elasticsearch.action.update.UpdateRequest;
|
import org.elasticsearch.action.update.UpdateRequest;
|
||||||
|
import org.elasticsearch.common.bytes.BytesArray;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.ingest.core.CompoundProcessor;
|
import org.elasticsearch.ingest.core.CompoundProcessor;
|
||||||
import org.elasticsearch.ingest.core.IngestDocument;
|
import org.elasticsearch.ingest.core.IngestDocument;
|
||||||
@ -38,15 +39,16 @@ import org.mockito.ArgumentMatcher;
|
|||||||
import org.mockito.invocation.InvocationOnMock;
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.mockito.Matchers.anyBoolean;
|
||||||
import static org.mockito.Matchers.eq;
|
import static org.mockito.Matchers.eq;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Matchers.anyBoolean;
|
|
||||||
import static org.mockito.Matchers.anyString;
|
import static org.mockito.Matchers.anyString;
|
||||||
import static org.mockito.Matchers.argThat;
|
import static org.mockito.Matchers.argThat;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
@ -341,6 +343,43 @@ public class PipelineExecutionServiceTests extends ESTestCase {
|
|||||||
verify(completionHandler, times(1)).accept(null);
|
verify(completionHandler, times(1)).accept(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testStats() throws Exception {
|
||||||
|
IngestStats ingestStats = executionService.stats();
|
||||||
|
assertThat(ingestStats.getStatsPerPipeline().size(), equalTo(0));
|
||||||
|
assertThat(ingestStats.getTotalStats().getIngestCount(), equalTo(0L));
|
||||||
|
assertThat(ingestStats.getTotalStats().getIngestCurrent(), equalTo(0L));
|
||||||
|
assertThat(ingestStats.getTotalStats().getIngestFailedCount(), equalTo(0L));
|
||||||
|
assertThat(ingestStats.getTotalStats().getIngestTimeInMillis(), equalTo(0L));
|
||||||
|
|
||||||
|
when(store.get("_id1")).thenReturn(new Pipeline("_id1", null, new CompoundProcessor()));
|
||||||
|
when(store.get("_id2")).thenReturn(new Pipeline("_id2", null, new CompoundProcessor()));
|
||||||
|
|
||||||
|
Map<String, PipelineConfiguration> configurationMap = new HashMap<>();
|
||||||
|
configurationMap.put("_id1", new PipelineConfiguration("_id1", new BytesArray("{}")));
|
||||||
|
configurationMap.put("_id2", new PipelineConfiguration("_id2", new BytesArray("{}")));
|
||||||
|
executionService.updatePipelineStats(new IngestMetadata(configurationMap));
|
||||||
|
|
||||||
|
Consumer<Throwable> failureHandler = mock(Consumer.class);
|
||||||
|
Consumer<Boolean> completionHandler = mock(Consumer.class);
|
||||||
|
|
||||||
|
IndexRequest indexRequest = new IndexRequest("_index");
|
||||||
|
indexRequest.setPipeline("_id1");
|
||||||
|
executionService.executeIndexRequest(indexRequest, failureHandler, completionHandler);
|
||||||
|
ingestStats = executionService.stats();
|
||||||
|
assertThat(ingestStats.getStatsPerPipeline().size(), equalTo(2));
|
||||||
|
assertThat(ingestStats.getStatsPerPipeline().get("_id1").getIngestCount(), equalTo(1L));
|
||||||
|
assertThat(ingestStats.getStatsPerPipeline().get("_id2").getIngestCount(), equalTo(0L));
|
||||||
|
assertThat(ingestStats.getTotalStats().getIngestCount(), equalTo(1L));
|
||||||
|
|
||||||
|
indexRequest.setPipeline("_id2");
|
||||||
|
executionService.executeIndexRequest(indexRequest, failureHandler, completionHandler);
|
||||||
|
ingestStats = executionService.stats();
|
||||||
|
assertThat(ingestStats.getStatsPerPipeline().size(), equalTo(2));
|
||||||
|
assertThat(ingestStats.getStatsPerPipeline().get("_id1").getIngestCount(), equalTo(1L));
|
||||||
|
assertThat(ingestStats.getStatsPerPipeline().get("_id2").getIngestCount(), equalTo(1L));
|
||||||
|
assertThat(ingestStats.getTotalStats().getIngestCount(), equalTo(2L));
|
||||||
|
}
|
||||||
|
|
||||||
private IngestDocument eqID(String index, String type, String id, Map<String, Object> source) {
|
private IngestDocument eqID(String index, String type, String id, Map<String, Object> source) {
|
||||||
return argThat(new IngestDocumentMatcher(index, type, id, source));
|
return argThat(new IngestDocumentMatcher(index, type, id, source));
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ import org.elasticsearch.search.aggregations.metrics.stats.Stats;
|
|||||||
import org.elasticsearch.search.aggregations.metrics.sum.Sum;
|
import org.elasticsearch.search.aggregations.metrics.sum.Sum;
|
||||||
import org.elasticsearch.search.aggregations.pipeline.BucketHelpers.GapPolicy;
|
import org.elasticsearch.search.aggregations.pipeline.BucketHelpers.GapPolicy;
|
||||||
import org.elasticsearch.search.aggregations.pipeline.derivative.Derivative;
|
import org.elasticsearch.search.aggregations.pipeline.derivative.Derivative;
|
||||||
|
import org.elasticsearch.search.aggregations.pipeline.movavg.models.SimpleModel;
|
||||||
import org.elasticsearch.search.aggregations.support.AggregationPath;
|
import org.elasticsearch.search.aggregations.support.AggregationPath;
|
||||||
import org.elasticsearch.test.ESIntegTestCase;
|
import org.elasticsearch.test.ESIntegTestCase;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
@ -47,7 +48,9 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.filters;
|
|||||||
import static org.elasticsearch.search.aggregations.AggregationBuilders.histogram;
|
import static org.elasticsearch.search.aggregations.AggregationBuilders.histogram;
|
||||||
import static org.elasticsearch.search.aggregations.AggregationBuilders.stats;
|
import static org.elasticsearch.search.aggregations.AggregationBuilders.stats;
|
||||||
import static org.elasticsearch.search.aggregations.AggregationBuilders.sum;
|
import static org.elasticsearch.search.aggregations.AggregationBuilders.sum;
|
||||||
|
import static org.elasticsearch.search.aggregations.AggregationBuilders.avg;
|
||||||
import static org.elasticsearch.search.aggregations.pipeline.PipelineAggregatorBuilders.derivative;
|
import static org.elasticsearch.search.aggregations.pipeline.PipelineAggregatorBuilders.derivative;
|
||||||
|
import static org.elasticsearch.search.aggregations.pipeline.PipelineAggregatorBuilders.movingAvg;
|
||||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse;
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse;
|
||||||
import static org.hamcrest.Matchers.closeTo;
|
import static org.hamcrest.Matchers.closeTo;
|
||||||
@ -614,6 +617,37 @@ public class DerivativeIT extends ESIntegTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testAvgMovavgDerivNPE() throws Exception {
|
||||||
|
createIndex("movavg_npe");
|
||||||
|
ensureYellow("movavg_npe");
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
Integer value = i;
|
||||||
|
if (i == 1 || i == 3) {
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
XContentBuilder doc = jsonBuilder()
|
||||||
|
.startObject()
|
||||||
|
.field("tick", i)
|
||||||
|
.field("value", value)
|
||||||
|
.endObject();
|
||||||
|
client().prepareIndex("movavg_npe", "type").setSource(doc).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
SearchResponse response = client()
|
||||||
|
.prepareSearch("movavg_npe")
|
||||||
|
.addAggregation(
|
||||||
|
histogram("histo").field("tick").interval(1)
|
||||||
|
.subAggregation(avg("avg").field("value"))
|
||||||
|
.subAggregation(movingAvg("movavg", "avg").modelBuilder(new SimpleModel.SimpleModelBuilder()).window(3))
|
||||||
|
.subAggregation(derivative("deriv", "movavg"))).execute().actionGet();
|
||||||
|
|
||||||
|
assertSearchResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
private void checkBucketKeyAndDocCount(final String msg, final Histogram.Bucket bucket, final long expectedKey,
|
private void checkBucketKeyAndDocCount(final String msg, final Histogram.Bucket bucket, final long expectedKey,
|
||||||
final long expectedDocCount) {
|
final long expectedDocCount) {
|
||||||
assertThat(msg, bucket, notNullValue());
|
assertThat(msg, bucket, notNullValue());
|
||||||
|
@ -1813,19 +1813,31 @@ public class SharedClusterSnapshotRestoreIT extends AbstractSnapshotIntegTestCas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDeleteIndexDuringSnapshot() throws Exception {
|
public void testCloseOrDeleteIndexDuringSnapshot() throws Exception {
|
||||||
Client client = client();
|
Client client = client();
|
||||||
|
|
||||||
boolean allowPartial = randomBoolean();
|
boolean allowPartial = randomBoolean();
|
||||||
|
|
||||||
logger.info("--> creating repository");
|
logger.info("--> creating repository");
|
||||||
assertAcked(client.admin().cluster().preparePutRepository("test-repo")
|
|
||||||
|
// only block on repo init if we have partial snapshot or we run into deadlock when acquiring shard locks for index deletion/closing
|
||||||
|
boolean initBlocking = allowPartial || randomBoolean();
|
||||||
|
if (initBlocking) {
|
||||||
|
assertAcked(client.admin().cluster().preparePutRepository("test-repo")
|
||||||
.setType("mock").setSettings(Settings.settingsBuilder()
|
.setType("mock").setSettings(Settings.settingsBuilder()
|
||||||
.put("location", randomRepoPath())
|
.put("location", randomRepoPath())
|
||||||
.put("compress", randomBoolean())
|
.put("compress", randomBoolean())
|
||||||
.put("chunk_size", randomIntBetween(100, 1000), ByteSizeUnit.BYTES)
|
.put("chunk_size", randomIntBetween(100, 1000), ByteSizeUnit.BYTES)
|
||||||
.put("block_on_init", true)
|
.put("block_on_init", true)
|
||||||
));
|
));
|
||||||
|
} else {
|
||||||
|
assertAcked(client.admin().cluster().preparePutRepository("test-repo")
|
||||||
|
.setType("mock").setSettings(Settings.settingsBuilder()
|
||||||
|
.put("location", randomRepoPath())
|
||||||
|
.put("compress", randomBoolean())
|
||||||
|
.put("chunk_size", randomIntBetween(100, 1000), ByteSizeUnit.BYTES)
|
||||||
|
.put("block_on_data", true)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
createIndex("test-idx-1", "test-idx-2", "test-idx-3");
|
createIndex("test-idx-1", "test-idx-2", "test-idx-3");
|
||||||
ensureGreen();
|
ensureGreen();
|
||||||
@ -1843,25 +1855,61 @@ public class SharedClusterSnapshotRestoreIT extends AbstractSnapshotIntegTestCas
|
|||||||
|
|
||||||
logger.info("--> snapshot allow partial {}", allowPartial);
|
logger.info("--> snapshot allow partial {}", allowPartial);
|
||||||
ListenableActionFuture<CreateSnapshotResponse> future = client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap")
|
ListenableActionFuture<CreateSnapshotResponse> future = client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap")
|
||||||
.setIndices("test-idx-*").setWaitForCompletion(true).setPartial(allowPartial).execute();
|
.setIndices("test-idx-*").setWaitForCompletion(true).setPartial(allowPartial).execute();
|
||||||
logger.info("--> wait for block to kick in");
|
logger.info("--> wait for block to kick in");
|
||||||
waitForBlock(internalCluster().getMasterName(), "test-repo", TimeValue.timeValueMinutes(1));
|
if (initBlocking) {
|
||||||
logger.info("--> delete some indices while snapshot is running");
|
waitForBlock(internalCluster().getMasterName(), "test-repo", TimeValue.timeValueMinutes(1));
|
||||||
client.admin().indices().prepareDelete("test-idx-1", "test-idx-2").get();
|
} else {
|
||||||
logger.info("--> unblock running master node");
|
waitForBlockOnAnyDataNode("test-repo", TimeValue.timeValueMinutes(1));
|
||||||
unblockNode(internalCluster().getMasterName());
|
}
|
||||||
|
if (allowPartial) {
|
||||||
|
// partial snapshots allow close / delete operations
|
||||||
|
if (randomBoolean()) {
|
||||||
|
logger.info("--> delete index while partial snapshot is running");
|
||||||
|
client.admin().indices().prepareDelete("test-idx-1").get();
|
||||||
|
} else {
|
||||||
|
logger.info("--> close index while partial snapshot is running");
|
||||||
|
client.admin().indices().prepareClose("test-idx-1").get();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// non-partial snapshots do not allow close / delete operations on indices where snapshot has not been completed
|
||||||
|
if (randomBoolean()) {
|
||||||
|
try {
|
||||||
|
logger.info("--> delete index while non-partial snapshot is running");
|
||||||
|
client.admin().indices().prepareDelete("test-idx-1").get();
|
||||||
|
fail("Expected deleting index to fail during snapshot");
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
assertThat(e.getMessage(), containsString("Cannot delete indices that are being snapshotted: [test-idx-1]"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
logger.info("--> close index while non-partial snapshot is running");
|
||||||
|
client.admin().indices().prepareClose("test-idx-1").get();
|
||||||
|
fail("Expected closing index to fail during snapshot");
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
assertThat(e.getMessage(), containsString("Cannot close indices that are being snapshotted: [test-idx-1]"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (initBlocking) {
|
||||||
|
logger.info("--> unblock running master node");
|
||||||
|
unblockNode(internalCluster().getMasterName());
|
||||||
|
} else {
|
||||||
|
logger.info("--> unblock all data nodes");
|
||||||
|
unblockAllDataNodes("test-repo");
|
||||||
|
}
|
||||||
logger.info("--> waiting for snapshot to finish");
|
logger.info("--> waiting for snapshot to finish");
|
||||||
CreateSnapshotResponse createSnapshotResponse = future.get();
|
CreateSnapshotResponse createSnapshotResponse = future.get();
|
||||||
|
|
||||||
if (allowPartial) {
|
if (allowPartial) {
|
||||||
logger.info("Deleted index during snapshot, but allow partial");
|
logger.info("Deleted/Closed index during snapshot, but allow partial");
|
||||||
assertThat(createSnapshotResponse.getSnapshotInfo().state(), equalTo((SnapshotState.PARTIAL)));
|
assertThat(createSnapshotResponse.getSnapshotInfo().state(), equalTo((SnapshotState.PARTIAL)));
|
||||||
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0));
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0));
|
||||||
assertThat(createSnapshotResponse.getSnapshotInfo().failedShards(), greaterThan(0));
|
assertThat(createSnapshotResponse.getSnapshotInfo().failedShards(), greaterThan(0));
|
||||||
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), lessThan(createSnapshotResponse.getSnapshotInfo().totalShards()));
|
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), lessThan(createSnapshotResponse.getSnapshotInfo().totalShards()));
|
||||||
} else {
|
} else {
|
||||||
logger.info("Deleted index during snapshot and doesn't allow partial");
|
logger.info("Snapshot successfully completed");
|
||||||
assertThat(createSnapshotResponse.getSnapshotInfo().state(), equalTo((SnapshotState.FAILED)));
|
assertThat(createSnapshotResponse.getSnapshotInfo().state(), equalTo((SnapshotState.SUCCESS)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1960,7 +2008,7 @@ public class SharedClusterSnapshotRestoreIT extends AbstractSnapshotIntegTestCas
|
|||||||
shards.put(new ShardId("test-idx", "_na_", 1), new ShardSnapshotStatus("unknown-node", State.ABORTED));
|
shards.put(new ShardId("test-idx", "_na_", 1), new ShardSnapshotStatus("unknown-node", State.ABORTED));
|
||||||
shards.put(new ShardId("test-idx", "_na_", 2), new ShardSnapshotStatus("unknown-node", State.ABORTED));
|
shards.put(new ShardId("test-idx", "_na_", 2), new ShardSnapshotStatus("unknown-node", State.ABORTED));
|
||||||
List<Entry> entries = new ArrayList<>();
|
List<Entry> entries = new ArrayList<>();
|
||||||
entries.add(new Entry(new SnapshotId("test-repo", "test-snap"), true, State.ABORTED, Collections.singletonList("test-idx"), System.currentTimeMillis(), shards.build()));
|
entries.add(new Entry(new SnapshotId("test-repo", "test-snap"), true, false, State.ABORTED, Collections.singletonList("test-idx"), System.currentTimeMillis(), shards.build()));
|
||||||
return ClusterState.builder(currentState).putCustom(SnapshotsInProgress.TYPE, new SnapshotsInProgress(Collections.unmodifiableList(entries))).build();
|
return ClusterState.builder(currentState).putCustom(SnapshotsInProgress.TYPE, new SnapshotsInProgress(Collections.unmodifiableList(entries))).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ public class MockLogAppender extends AppenderSkeleton {
|
|||||||
protected final String logger;
|
protected final String logger;
|
||||||
protected final Level level;
|
protected final Level level;
|
||||||
protected final String message;
|
protected final String message;
|
||||||
protected boolean saw;
|
volatile boolean saw;
|
||||||
|
|
||||||
public AbstractEventExpectation(String name, String logger, Level level, String message) {
|
public AbstractEventExpectation(String name, String logger, Level level, String message) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package org.elasticsearch.transport.netty;
|
package org.elasticsearch.transport.netty;
|
||||||
|
|
||||||
|
import org.elasticsearch.common.lease.Releasable;
|
||||||
import org.elasticsearch.common.util.concurrent.KeyedLock;
|
import org.elasticsearch.common.util.concurrent.KeyedLock;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
@ -29,9 +30,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.is;
|
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
|
|
||||||
public class KeyedLockTests extends ESTestCase {
|
public class KeyedLockTests extends ESTestCase {
|
||||||
@ -68,28 +67,6 @@ public class KeyedLockTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCannotAcquireTwoLocks() throws InterruptedException {
|
|
||||||
KeyedLock<String> connectionLock = new KeyedLock<String>();
|
|
||||||
String name = randomRealisticUnicodeOfLength(scaledRandomIntBetween(10, 50));
|
|
||||||
connectionLock.acquire(name);
|
|
||||||
try {
|
|
||||||
connectionLock.acquire(name);
|
|
||||||
fail("Expected IllegalStateException");
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
assertThat(e.getMessage(), containsString("Lock already acquired"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCannotReleaseUnacquiredLock() throws InterruptedException {
|
|
||||||
KeyedLock<String> connectionLock = new KeyedLock<String>();
|
|
||||||
String name = randomRealisticUnicodeOfLength(scaledRandomIntBetween(10, 50));
|
|
||||||
try {
|
|
||||||
connectionLock.release(name);
|
|
||||||
fail("Expected IllegalStateException");
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
assertThat(e.getMessage(), is("Lock not acquired"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class AcquireAndReleaseThread extends Thread {
|
public static class AcquireAndReleaseThread extends Thread {
|
||||||
private CountDownLatch startLatch;
|
private CountDownLatch startLatch;
|
||||||
@ -117,16 +94,16 @@ public class KeyedLockTests extends ESTestCase {
|
|||||||
int numRuns = scaledRandomIntBetween(5000, 50000);
|
int numRuns = scaledRandomIntBetween(5000, 50000);
|
||||||
for (int i = 0; i < numRuns; i++) {
|
for (int i = 0; i < numRuns; i++) {
|
||||||
String curName = names[randomInt(names.length - 1)];
|
String curName = names[randomInt(names.length - 1)];
|
||||||
connectionLock.acquire(curName);
|
assert connectionLock.isHeldByCurrentThread(curName) == false;
|
||||||
try {
|
try (Releasable ignored = connectionLock.acquire(curName)) {
|
||||||
|
assert connectionLock.isHeldByCurrentThread(curName);
|
||||||
|
assert connectionLock.isHeldByCurrentThread(curName + "bla") == false;
|
||||||
Integer integer = counter.get(curName);
|
Integer integer = counter.get(curName);
|
||||||
if (integer == null) {
|
if (integer == null) {
|
||||||
counter.put(curName, 1);
|
counter.put(curName, 1);
|
||||||
} else {
|
} else {
|
||||||
counter.put(curName, integer.intValue() + 1);
|
counter.put(curName, integer.intValue() + 1);
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
connectionLock.release(curName);
|
|
||||||
}
|
}
|
||||||
AtomicInteger atomicInteger = new AtomicInteger(0);
|
AtomicInteger atomicInteger = new AtomicInteger(0);
|
||||||
AtomicInteger value = safeCounter.putIfAbsent(curName, atomicInteger);
|
AtomicInteger value = safeCounter.putIfAbsent(curName, atomicInteger);
|
||||||
|
@ -29,7 +29,7 @@ my $Issue_URL = "http://github.com/${User_Repo}issues/";
|
|||||||
|
|
||||||
my @Groups = qw(
|
my @Groups = qw(
|
||||||
breaking deprecation feature
|
breaking deprecation feature
|
||||||
enhancement bug regression upgrade build doc test
|
enhancement bug regression upgrade non-issue build docs test
|
||||||
);
|
);
|
||||||
my %Group_Labels = (
|
my %Group_Labels = (
|
||||||
breaking => 'Breaking changes',
|
breaking => 'Breaking changes',
|
||||||
@ -42,6 +42,7 @@ my %Group_Labels = (
|
|||||||
regression => 'Regressions',
|
regression => 'Regressions',
|
||||||
test => 'Tests',
|
test => 'Tests',
|
||||||
upgrade => 'Upgrades',
|
upgrade => 'Upgrades',
|
||||||
|
"non-issue" => 'Non-issue',
|
||||||
other => 'NOT CLASSIFIED',
|
other => 'NOT CLASSIFIED',
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -157,6 +158,8 @@ sub fetch_issues {
|
|||||||
ISSUE:
|
ISSUE:
|
||||||
for my $issue (@issues) {
|
for my $issue (@issues) {
|
||||||
next if $seen{ $issue->{number} } && !$issue->{pull_request};
|
next if $seen{ $issue->{number} } && !$issue->{pull_request};
|
||||||
|
# uncomment for including/excluding PRs already issued in other versions
|
||||||
|
# next if grep {$_->{name}=~/^v2/} @{$issue->{labels}};
|
||||||
my %labels = map { $_->{name} => 1 } @{ $issue->{labels} };
|
my %labels = map { $_->{name} => 1 } @{ $issue->{labels} };
|
||||||
my ($header) = map { substr( $_, 1 ) } grep {/^:/} keys %labels;
|
my ($header) = map { substr( $_, 1 ) } grep {/^:/} keys %labels;
|
||||||
$header ||= 'NOT CLASSIFIED';
|
$header ||= 'NOT CLASSIFIED';
|
||||||
|
@ -107,6 +107,7 @@ export ES_DIRECT_SIZE
|
|||||||
export ES_JAVA_OPTS
|
export ES_JAVA_OPTS
|
||||||
export ES_GC_LOG_FILE
|
export ES_GC_LOG_FILE
|
||||||
export JAVA_HOME
|
export JAVA_HOME
|
||||||
|
export ES_INCLUDE
|
||||||
|
|
||||||
# Check DAEMON exists
|
# Check DAEMON exists
|
||||||
test -x $DAEMON || exit 0
|
test -x $DAEMON || exit 0
|
||||||
|
@ -66,6 +66,7 @@ export ES_JAVA_OPTS
|
|||||||
export ES_GC_LOG_FILE
|
export ES_GC_LOG_FILE
|
||||||
export ES_STARTUP_SLEEP_TIME
|
export ES_STARTUP_SLEEP_TIME
|
||||||
export JAVA_HOME
|
export JAVA_HOME
|
||||||
|
export ES_INCLUDE
|
||||||
|
|
||||||
lockfile=/var/lock/subsys/$prog
|
lockfile=/var/lock/subsys/$prog
|
||||||
|
|
||||||
|
Binary file not shown.
@ -81,6 +81,10 @@ JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError"
|
|||||||
# Disables explicit GC
|
# Disables explicit GC
|
||||||
JAVA_OPTS="$JAVA_OPTS -XX:+DisableExplicitGC"
|
JAVA_OPTS="$JAVA_OPTS -XX:+DisableExplicitGC"
|
||||||
|
|
||||||
|
# Enable pre-touching of memory pages used by the JVM during hotspot
|
||||||
|
# initialization
|
||||||
|
JAVA_OPTS="$JAVA_OPTS -XX:+AlwaysPreTouch"
|
||||||
|
|
||||||
# Ensure UTF-8 encoding by default (e.g. filenames)
|
# Ensure UTF-8 encoding by default (e.g. filenames)
|
||||||
JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8"
|
JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8"
|
||||||
|
|
||||||
|
@ -12,12 +12,16 @@ Obtaining an elasticsearch `Client` is simple. The most common way to
|
|||||||
get a client is by creating a <<transport-client,`TransportClient`>>
|
get a client is by creating a <<transport-client,`TransportClient`>>
|
||||||
that connects to a cluster.
|
that connects to a cluster.
|
||||||
|
|
||||||
*Important:*
|
[IMPORTANT]
|
||||||
______________________________________________________________________________________________________________________________________________________________
|
==============================
|
||||||
Please note that you are encouraged to use the same version on client
|
|
||||||
and cluster sides. You may hit some incompatibility issues when mixing
|
The client must have the same major version (e.g. `2.x`, or `5.x`) as the
|
||||||
major versions.
|
nodes in the cluster. Clients may connect to clusters which have a different
|
||||||
______________________________________________________________________________________________________________________________________________________________
|
minor version (e.g. `2.3.x`) but it is possible that new funcionality may not
|
||||||
|
be supported. Ideally, the client should have the same version as the
|
||||||
|
cluster.
|
||||||
|
|
||||||
|
==============================
|
||||||
|
|
||||||
|
|
||||||
[[transport-client]]
|
[[transport-client]]
|
||||||
@ -53,11 +57,23 @@ Client client = TransportClient.builder().settings(settings).build();
|
|||||||
//Add transport addresses and do something with the client...
|
//Add transport addresses and do something with the client...
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
|
||||||
The client allows sniffing the rest of the cluster, which adds data nodes
|
The Transport client comes with a cluster sniffing feature which
|
||||||
into its list of machines to use. In this case, note that the IP addresses
|
allows it to dynamically add new hosts and remove old ones.
|
||||||
used will be the ones that the other nodes were started with (the
|
When sniffing is enabled the the transport client will connect to the nodes in its
|
||||||
"publish" address). In order to enable it, set the
|
internal node list, which is built via calls to addTransportAddress.
|
||||||
`client.transport.sniff` to `true`:
|
After this, the client will call the internal cluster state API on those nodes
|
||||||
|
to discover available data nodes. The internal node list of the client will
|
||||||
|
be replaced with those data nodes only. This list is refreshed every five seconds by default.
|
||||||
|
Note that the IP addresses the sniffer connects to are the ones declared as the 'publish'
|
||||||
|
address in those node's elasticsearch config.
|
||||||
|
|
||||||
|
Keep in mind that list might possibly not include the original node it connected to
|
||||||
|
if that node is not a data node. If, for instance, you initially connect to a
|
||||||
|
master node, after sniffing no further requests will go to that master node,
|
||||||
|
but rather to any data nodes instead. The reason the transport excludes non-data
|
||||||
|
nodes is to avoid sending search traffic to master only nodes.
|
||||||
|
|
||||||
|
In order to enable sniffing, set `client.transport.sniff` to `true`:
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
@ -176,7 +176,7 @@ need to specify the `type` (like `string` or `date`) since it is already known.
|
|||||||
[[mapper-attachments-copy-to]]
|
[[mapper-attachments-copy-to]]
|
||||||
==== Copy To feature
|
==== Copy To feature
|
||||||
|
|
||||||
If you want to use http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-core-types.html#copy-to[copy_to]
|
If you want to use https://www.elastic.co/guide/en/elasticsearch/reference/current/copy-to.html[copy_to]
|
||||||
feature, you need to define it on each sub-field you want to copy to another field:
|
feature, you need to define it on each sub-field you want to copy to another field:
|
||||||
|
|
||||||
[source,js]
|
[source,js]
|
||||||
|
@ -60,6 +60,9 @@ of `indices`, `os`, `process`, `jvm`, `transport`, `http`,
|
|||||||
`discovery`::
|
`discovery`::
|
||||||
Statistics about the discovery
|
Statistics about the discovery
|
||||||
|
|
||||||
|
`ingest`::
|
||||||
|
Statistics about ingest preprocessing
|
||||||
|
|
||||||
[source,js]
|
[source,js]
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
# return indices and os
|
# return indices and os
|
||||||
@ -227,3 +230,23 @@ curl -XGET 'http://localhost:9200/_nodes/stats?pretty&groups=_all'
|
|||||||
# Some groups from just the indices stats
|
# Some groups from just the indices stats
|
||||||
curl -XGET 'http://localhost:9200/_nodes/stats/indices?pretty&groups=foo,bar'
|
curl -XGET 'http://localhost:9200/_nodes/stats/indices?pretty&groups=foo,bar'
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
|
||||||
|
[float]
|
||||||
|
[[ingest-stats]]
|
||||||
|
=== Ingest statistics
|
||||||
|
|
||||||
|
The `ingest` flag can be set to retrieve statistics that concern ingest:
|
||||||
|
|
||||||
|
`ingest.total.count`::
|
||||||
|
The total number of document ingested during the lifetime of this node
|
||||||
|
|
||||||
|
`ingest.total.time_in_millis`::
|
||||||
|
The total time spent on ingest preprocessing documents during the lifetime of this node
|
||||||
|
|
||||||
|
`ingest.total.current`::
|
||||||
|
The total number of documents currently being ingested.
|
||||||
|
|
||||||
|
`ingest.total.failed`::
|
||||||
|
The total number ingest preprocessing operations failed during the lifetime of this node
|
||||||
|
|
||||||
|
On top of these overall ingest statistics, these statistics are also provided on a per pipeline basis.
|
@ -83,6 +83,16 @@ Cluster wide settings can be returned using:
|
|||||||
curl -XGET localhost:9200/_cluster/settings
|
curl -XGET localhost:9200/_cluster/settings
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
|
||||||
|
[float]
|
||||||
|
=== Precedence of settings
|
||||||
|
|
||||||
|
Transient cluster settings take precedence over persistent cluster settings,
|
||||||
|
which take precedence over settings configured in the `elasticsearch.yml`
|
||||||
|
config file.
|
||||||
|
|
||||||
|
For this reason it is preferrable to use the `elasticsearch.yml` file only
|
||||||
|
for local configurations, and set all cluster-wider settings with the
|
||||||
|
`settings` API.
|
||||||
|
|
||||||
A list of dynamically updatable settings can be found in the
|
A list of dynamically updatable settings can be found in the
|
||||||
<<modules,Modules>> documentation.
|
<<modules,Modules>> documentation.
|
||||||
|
@ -251,5 +251,15 @@ sure the document doesn't change during the update. You can use the `version`
|
|||||||
parameter to specify that the document should only be updated if its version
|
parameter to specify that the document should only be updated if its version
|
||||||
matches the one specified. By setting version type to `force` you can force
|
matches the one specified. By setting version type to `force` you can force
|
||||||
the new version of the document after update (use with care! with `force`
|
the new version of the document after update (use with care! with `force`
|
||||||
there is no guarantee the document didn't change).Version types `external` &
|
there is no guarantee the document didn't change).
|
||||||
`external_gte` are not supported.
|
|
||||||
|
[NOTE]
|
||||||
|
.The update API does not support external versioning
|
||||||
|
=====================================================
|
||||||
|
|
||||||
|
External versioning (version types `external` & `external_gte`) is not
|
||||||
|
supported by the update API as it would result in Elasticsearch version
|
||||||
|
numbers being out of sync with the external system. Use the
|
||||||
|
<<docs-index_,`index` API>> instead.
|
||||||
|
|
||||||
|
=====================================================
|
||||||
|
@ -634,6 +634,9 @@ plugin.mandatory: ingest-attachment,ingest-geoip
|
|||||||
|
|
||||||
A node will not start if either of these plugins are not available.
|
A node will not start if either of these plugins are not available.
|
||||||
|
|
||||||
|
The <<ingest-stats,node stats API>> can be used to fetch ingest usage statistics, globally and on a per
|
||||||
|
pipeline basis. Useful to find out which pipelines are used the most or spent the most time on preprocessing.
|
||||||
|
|
||||||
[[append-procesesor]]
|
[[append-procesesor]]
|
||||||
=== Append Processor
|
=== Append Processor
|
||||||
Appends one or more values to an existing array if the field already exists and it is an array.
|
Appends one or more values to an existing array if the field already exists and it is an array.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[[mapping-store]]
|
[[mapping-store]]
|
||||||
=== `store`
|
=== `store`
|
||||||
|
|
||||||
By default, field values <<mapping-index,indexed>> to make them searchable,
|
By default, field values are <<mapping-index,indexed>> to make them searchable,
|
||||||
but they are not _stored_. This means that the field can be queried, but the
|
but they are not _stored_. This means that the field can be queried, but the
|
||||||
original field value cannot be retrieved.
|
original field value cannot be retrieved.
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ your application to Elasticsearch 2.2.
|
|||||||
==== Geo Point Type
|
==== Geo Point Type
|
||||||
|
|
||||||
The `geo_point` format has been changed to reduce index size and the time required to both index and query
|
The `geo_point` format has been changed to reduce index size and the time required to both index and query
|
||||||
geo point data. To make these performance improvements possible both `doc_values` are `coerce` are required
|
geo point data. To make these performance improvements possible both `doc_values` and `coerce` are required
|
||||||
and therefore cannot be changed. For this reason the `doc_values` and `coerce` parameters have been removed
|
and therefore cannot be changed. For this reason the `doc_values` and `coerce` parameters have been removed
|
||||||
from the <<geo-point, geo_point>> field mapping.
|
from the <<geo-point, geo_point>> field mapping.
|
||||||
|
|
||||||
@ -43,6 +43,21 @@ changed to now route standard output to the journal and standard error
|
|||||||
to inherit this setting (these are the defaults for systemd). These
|
to inherit this setting (these are the defaults for systemd). These
|
||||||
settings can be modified by editing the `elasticsearch.service` file.
|
settings can be modified by editing the `elasticsearch.service` file.
|
||||||
|
|
||||||
|
[float]
|
||||||
|
=== Java Client
|
||||||
|
|
||||||
|
Previously it was possible to iterate over `ClusterHealthResponse` to get information about `ClusterIndexHealth`.
|
||||||
|
While this is still possible, it requires now iterating over the values returned from `getIndices()`:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
---------------
|
||||||
|
ClusterHealthResponse clusterHealthResponse = client.admin().cluster().prepareHealth().get();
|
||||||
|
for (Map.Entry<String, ClusterIndexHealth> index : clusterHealthResponse.getIndices().entrySet()) {
|
||||||
|
String indexName = index.getKey();
|
||||||
|
ClusterIndexHealth health = index.getValue();
|
||||||
|
}
|
||||||
|
---------------
|
||||||
|
|
||||||
[float]
|
[float]
|
||||||
=== Cloud AWS Plugin
|
=== Cloud AWS Plugin
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ your application to Elasticsearch 5.0.
|
|||||||
* <<breaking_50_scripting>>
|
* <<breaking_50_scripting>>
|
||||||
* <<breaking_50_term_vectors>>
|
* <<breaking_50_term_vectors>>
|
||||||
* <<breaking_50_security>>
|
* <<breaking_50_security>>
|
||||||
|
* <<breaking_50_snapshot_restore>>
|
||||||
|
|
||||||
[[breaking_50_search_changes]]
|
[[breaking_50_search_changes]]
|
||||||
=== Warmers
|
=== Warmers
|
||||||
@ -819,6 +820,15 @@ changed to now route standard output to the journal and standard error
|
|||||||
to inherit this setting (these are the defaults for systemd). These
|
to inherit this setting (these are the defaults for systemd). These
|
||||||
settings can be modified by editing the elasticsearch.service file.
|
settings can be modified by editing the elasticsearch.service file.
|
||||||
|
|
||||||
|
==== Longer startup times
|
||||||
|
|
||||||
|
In Elasticsearch 5.0.0 the `-XX:+AlwaysPreTouch` flag has been added to the JVM
|
||||||
|
startup options. This option touches all memory pages used by the JVM heap
|
||||||
|
during initialization of the HotSpot VM to reduce the chance of having to commit
|
||||||
|
a memory page during GC time. This will increase the startup time of
|
||||||
|
Elasticsearch as well as increasing the initial resident memory usage of the
|
||||||
|
Java process.
|
||||||
|
|
||||||
[[breaking_50_scripting]]
|
[[breaking_50_scripting]]
|
||||||
=== Scripting
|
=== Scripting
|
||||||
|
|
||||||
@ -855,3 +865,12 @@ distributed document frequencies anymore.
|
|||||||
|
|
||||||
The option to disable the security manager `--security.manager.enabled` has been removed. In order to grant special
|
The option to disable the security manager `--security.manager.enabled` has been removed. In order to grant special
|
||||||
permissions to elasticsearch users must tweak the local Java Security Policy.
|
permissions to elasticsearch users must tweak the local Java Security Policy.
|
||||||
|
|
||||||
|
[[breaking_50_snapshot_restore]]
|
||||||
|
=== Snapshot/Restore
|
||||||
|
|
||||||
|
==== Closing / deleting indices while running snapshot
|
||||||
|
|
||||||
|
In previous versions of Elasticsearch, closing or deleting an index during a full snapshot would make the snapshot fail. This is now changed
|
||||||
|
by failing the close/delete index request instead. The behavior for partial snapshots remains unchanged: Closing or deleting an index during
|
||||||
|
a partial snapshot is still possible. The snapshot result is then marked as partial.
|
||||||
|
@ -215,6 +215,22 @@ There are a number of options for the `field_value_factor` function:
|
|||||||
`log1p`, `log2p`, `ln`, `ln1p`, `ln2p`, `square`, `sqrt`, or `reciprocal`.
|
`log1p`, `log2p`, `ln`, `ln1p`, `ln2p`, `square`, `sqrt`, or `reciprocal`.
|
||||||
Defaults to `none`.
|
Defaults to `none`.
|
||||||
|
|
||||||
|
[cols="<,<",options="header",]
|
||||||
|
|=======================================================================
|
||||||
|
| Modifier | Meaning
|
||||||
|
|
||||||
|
| `none` | Do not apply any multiplier to the field value
|
||||||
|
| `log` | Take the https://en.wikipedia.org/wiki/Logarithm[logarithm] of the field value
|
||||||
|
| `log1p` | Add 1 to the field value and take the logarithm
|
||||||
|
| `log2p` | Add 2 to the field value and take the logarithm
|
||||||
|
| `ln` | Take the https://en.wikipedia.org/wiki/Natural_logarithm[natural logarithm] of the field value
|
||||||
|
| `ln1p` | Add 1 to the field value and take the natural logarithm
|
||||||
|
| `ln2p` | Add 2 to the field value and take the natural logarithm
|
||||||
|
| `square` | Square the field value (multiply it by itself)
|
||||||
|
| `sqrt` | Take the https://en.wikipedia.org/wiki/Square_root[square root] of the field value
|
||||||
|
| `reciprocal` | https://en.wikipedia.org/wiki/Multiplicative_inverse[Reciprocate] the field value, same as `1/x` where `x` is the field's value
|
||||||
|
|=======================================================================
|
||||||
|
|
||||||
`missing`::
|
`missing`::
|
||||||
|
|
||||||
Value used if the document doesn't have that field. The modifier
|
Value used if the document doesn't have that field. The modifier
|
||||||
|
@ -185,10 +185,10 @@ values separately.
|
|||||||
"filter" : {
|
"filter" : {
|
||||||
"geo_bounding_box" : {
|
"geo_bounding_box" : {
|
||||||
"pin.location" : {
|
"pin.location" : {
|
||||||
"top" : -74.1,
|
"top" : 40.73,
|
||||||
"left" : 40.73,
|
"left" : -74.1,
|
||||||
"bottom" : -71.12,
|
"bottom" : 40.01,
|
||||||
"right" : 40.01
|
"right" : -71.12
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ inside the `has_child` query:
|
|||||||
{
|
{
|
||||||
"has_child" : {
|
"has_child" : {
|
||||||
"type" : "blog_tag",
|
"type" : "blog_tag",
|
||||||
"score_mode" : "sum",
|
"score_mode" : "min",
|
||||||
"query" : {
|
"query" : {
|
||||||
"term" : {
|
"term" : {
|
||||||
"tag" : "something"
|
"tag" : "something"
|
||||||
@ -57,7 +57,7 @@ a match:
|
|||||||
{
|
{
|
||||||
"has_child" : {
|
"has_child" : {
|
||||||
"type" : "blog_tag",
|
"type" : "blog_tag",
|
||||||
"score_mode" : "sum",
|
"score_mode" : "min",
|
||||||
"min_children": 2, <1>
|
"min_children": 2, <1>
|
||||||
"max_children": 10, <1>
|
"max_children": 10, <1>
|
||||||
"query" : {
|
"query" : {
|
||||||
|
@ -73,7 +73,6 @@ present in the index, the syntax is similar to <<docs-termvectors-artificial-doc
|
|||||||
},
|
},
|
||||||
"tweet": "You got no idea what I'd... what I'd give to be invisible."
|
"tweet": "You got no idea what I'd... what I'd give to be invisible."
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_index" : "marvel",
|
"_index" : "marvel",
|
||||||
|
@ -47,6 +47,7 @@ import org.elasticsearch.threadpool.ThreadPool;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -103,7 +104,8 @@ public abstract class AbstractAsyncBulkByScrollAction<Request extends AbstractBu
|
|||||||
|
|
||||||
protected abstract BulkRequest buildBulk(Iterable<SearchHit> docs);
|
protected abstract BulkRequest buildBulk(Iterable<SearchHit> docs);
|
||||||
|
|
||||||
protected abstract Response buildResponse(TimeValue took, List<Failure> indexingFailures, List<ShardSearchFailure> searchFailures);
|
protected abstract Response buildResponse(TimeValue took, List<Failure> indexingFailures, List<ShardSearchFailure> searchFailures,
|
||||||
|
boolean timedOut);
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
initialSearch();
|
initialSearch();
|
||||||
@ -161,8 +163,13 @@ public abstract class AbstractAsyncBulkByScrollAction<Request extends AbstractBu
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setScroll(searchResponse.getScrollId());
|
setScroll(searchResponse.getScrollId());
|
||||||
if (searchResponse.getShardFailures() != null && searchResponse.getShardFailures().length > 0) {
|
if ( // If any of the shards failed that should abort the request.
|
||||||
startNormalTermination(emptyList(), unmodifiableList(Arrays.asList(searchResponse.getShardFailures())));
|
(searchResponse.getShardFailures() != null && searchResponse.getShardFailures().length > 0)
|
||||||
|
// Timeouts aren't shard failures but we still need to pass them back to the user.
|
||||||
|
|| searchResponse.isTimedOut()
|
||||||
|
) {
|
||||||
|
startNormalTermination(emptyList(), unmodifiableList(Arrays.asList(searchResponse.getShardFailures())),
|
||||||
|
searchResponse.isTimedOut());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
long total = searchResponse.getHits().totalHits();
|
long total = searchResponse.getHits().totalHits();
|
||||||
@ -176,7 +183,7 @@ public abstract class AbstractAsyncBulkByScrollAction<Request extends AbstractBu
|
|||||||
SearchHit[] docs = searchResponse.getHits().getHits();
|
SearchHit[] docs = searchResponse.getHits().getHits();
|
||||||
logger.debug("scroll returned [{}] documents with a scroll id of [{}]", docs.length, searchResponse.getScrollId());
|
logger.debug("scroll returned [{}] documents with a scroll id of [{}]", docs.length, searchResponse.getScrollId());
|
||||||
if (docs.length == 0) {
|
if (docs.length == 0) {
|
||||||
startNormalTermination(emptyList(), emptyList());
|
startNormalTermination(emptyList(), emptyList(), false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
task.countBatch();
|
task.countBatch();
|
||||||
@ -261,18 +268,18 @@ public abstract class AbstractAsyncBulkByScrollAction<Request extends AbstractBu
|
|||||||
throw new IllegalArgumentException("Unknown op type: " + item.getOpType());
|
throw new IllegalArgumentException("Unknown op type: " + item.getOpType());
|
||||||
}
|
}
|
||||||
// Track the indexes we've seen so we can refresh them if requested
|
// Track the indexes we've seen so we can refresh them if requested
|
||||||
destinationIndices.add(item.getIndex());
|
destinationIndicesThisBatch.add(item.getIndex());
|
||||||
}
|
}
|
||||||
destinationIndices.addAll(destinationIndicesThisBatch);
|
addDestinationIndices(destinationIndicesThisBatch);
|
||||||
|
|
||||||
if (false == failures.isEmpty()) {
|
if (false == failures.isEmpty()) {
|
||||||
startNormalTermination(unmodifiableList(failures), emptyList());
|
startNormalTermination(unmodifiableList(failures), emptyList(), false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mainRequest.getSize() != SIZE_ALL_MATCHES && task.getSuccessfullyProcessed() >= mainRequest.getSize()) {
|
if (mainRequest.getSize() != SIZE_ALL_MATCHES && task.getSuccessfullyProcessed() >= mainRequest.getSize()) {
|
||||||
// We've processed all the requested docs.
|
// We've processed all the requested docs.
|
||||||
startNormalTermination(emptyList(), emptyList());
|
startNormalTermination(emptyList(), emptyList(), false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
startNextScroll();
|
startNextScroll();
|
||||||
@ -311,9 +318,9 @@ public abstract class AbstractAsyncBulkByScrollAction<Request extends AbstractBu
|
|||||||
failures.add(failure);
|
failures.add(failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
void startNormalTermination(List<Failure> indexingFailures, List<ShardSearchFailure> searchFailures) {
|
void startNormalTermination(List<Failure> indexingFailures, List<ShardSearchFailure> searchFailures, boolean timedOut) {
|
||||||
if (false == mainRequest.isRefresh()) {
|
if (task.isCancelled() || false == mainRequest.isRefresh()) {
|
||||||
finishHim(null, indexingFailures, searchFailures);
|
finishHim(null, indexingFailures, searchFailures, timedOut);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
RefreshRequest refresh = new RefreshRequest();
|
RefreshRequest refresh = new RefreshRequest();
|
||||||
@ -321,7 +328,7 @@ public abstract class AbstractAsyncBulkByScrollAction<Request extends AbstractBu
|
|||||||
client.admin().indices().refresh(refresh, new ActionListener<RefreshResponse>() {
|
client.admin().indices().refresh(refresh, new ActionListener<RefreshResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(RefreshResponse response) {
|
public void onResponse(RefreshResponse response) {
|
||||||
finishHim(null, indexingFailures, searchFailures);
|
finishHim(null, indexingFailures, searchFailures, timedOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -337,7 +344,7 @@ public abstract class AbstractAsyncBulkByScrollAction<Request extends AbstractBu
|
|||||||
* @param failure if non null then the request failed catastrophically with this exception
|
* @param failure if non null then the request failed catastrophically with this exception
|
||||||
*/
|
*/
|
||||||
void finishHim(Throwable failure) {
|
void finishHim(Throwable failure) {
|
||||||
finishHim(failure, emptyList(), emptyList());
|
finishHim(failure, emptyList(), emptyList(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -346,8 +353,9 @@ public abstract class AbstractAsyncBulkByScrollAction<Request extends AbstractBu
|
|||||||
* @param failure if non null then the request failed catastrophically with this exception
|
* @param failure if non null then the request failed catastrophically with this exception
|
||||||
* @param indexingFailures any indexing failures accumulated during the request
|
* @param indexingFailures any indexing failures accumulated during the request
|
||||||
* @param searchFailures any search failures accumulated during the request
|
* @param searchFailures any search failures accumulated during the request
|
||||||
|
* @param timedOut have any of the sub-requests timed out?
|
||||||
*/
|
*/
|
||||||
void finishHim(Throwable failure, List<Failure> indexingFailures, List<ShardSearchFailure> searchFailures) {
|
void finishHim(Throwable failure, List<Failure> indexingFailures, List<ShardSearchFailure> searchFailures, boolean timedOut) {
|
||||||
String scrollId = scroll.get();
|
String scrollId = scroll.get();
|
||||||
if (Strings.hasLength(scrollId)) {
|
if (Strings.hasLength(scrollId)) {
|
||||||
/*
|
/*
|
||||||
@ -369,7 +377,8 @@ public abstract class AbstractAsyncBulkByScrollAction<Request extends AbstractBu
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (failure == null) {
|
if (failure == null) {
|
||||||
listener.onResponse(buildResponse(timeValueNanos(System.nanoTime() - startTime.get()), indexingFailures, searchFailures));
|
listener.onResponse(
|
||||||
|
buildResponse(timeValueNanos(System.nanoTime() - startTime.get()), indexingFailures, searchFailures, timedOut));
|
||||||
} else {
|
} else {
|
||||||
listener.onFailure(failure);
|
listener.onFailure(failure);
|
||||||
}
|
}
|
||||||
@ -382,6 +391,14 @@ public abstract class AbstractAsyncBulkByScrollAction<Request extends AbstractBu
|
|||||||
return exponentialBackoff(mainRequest.getRetryBackoffInitialTime(), mainRequest.getMaxRetries());
|
return exponentialBackoff(mainRequest.getRetryBackoffInitialTime(), mainRequest.getMaxRetries());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add to the list of indices that were modified by this request. This is the list of indices refreshed at the end of the request if the
|
||||||
|
* request asks for a refresh.
|
||||||
|
*/
|
||||||
|
void addDestinationIndices(Collection<String> indices) {
|
||||||
|
destinationIndices.addAll(indices);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps a backoffPolicy in another policy that counts the number of backoffs acquired.
|
* Wraps a backoffPolicy in another policy that counts the number of backoffs acquired.
|
||||||
*/
|
*/
|
||||||
|
@ -45,16 +45,18 @@ public class BulkIndexByScrollResponse extends ActionResponse implements ToXCont
|
|||||||
private BulkByScrollTask.Status status;
|
private BulkByScrollTask.Status status;
|
||||||
private List<Failure> indexingFailures;
|
private List<Failure> indexingFailures;
|
||||||
private List<ShardSearchFailure> searchFailures;
|
private List<ShardSearchFailure> searchFailures;
|
||||||
|
private boolean timedOut;
|
||||||
|
|
||||||
public BulkIndexByScrollResponse() {
|
public BulkIndexByScrollResponse() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public BulkIndexByScrollResponse(TimeValue took, BulkByScrollTask.Status status, List<Failure> indexingFailures,
|
public BulkIndexByScrollResponse(TimeValue took, BulkByScrollTask.Status status, List<Failure> indexingFailures,
|
||||||
List<ShardSearchFailure> searchFailures) {
|
List<ShardSearchFailure> searchFailures, boolean timedOut) {
|
||||||
this.took = took;
|
this.took = took;
|
||||||
this.status = requireNonNull(status, "Null status not supported");
|
this.status = requireNonNull(status, "Null status not supported");
|
||||||
this.indexingFailures = indexingFailures;
|
this.indexingFailures = indexingFailures;
|
||||||
this.searchFailures = searchFailures;
|
this.searchFailures = searchFailures;
|
||||||
|
this.timedOut = timedOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TimeValue getTook() {
|
public TimeValue getTook() {
|
||||||
@ -103,6 +105,13 @@ public class BulkIndexByScrollResponse extends ActionResponse implements ToXCont
|
|||||||
return searchFailures;
|
return searchFailures;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Did any of the sub-requests that were part of this request timeout?
|
||||||
|
*/
|
||||||
|
public boolean isTimedOut() {
|
||||||
|
return timedOut;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
super.writeTo(out);
|
super.writeTo(out);
|
||||||
@ -116,6 +125,7 @@ public class BulkIndexByScrollResponse extends ActionResponse implements ToXCont
|
|||||||
for (ShardSearchFailure failure: searchFailures) {
|
for (ShardSearchFailure failure: searchFailures) {
|
||||||
failure.writeTo(out);
|
failure.writeTo(out);
|
||||||
}
|
}
|
||||||
|
out.writeBoolean(timedOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -135,11 +145,13 @@ public class BulkIndexByScrollResponse extends ActionResponse implements ToXCont
|
|||||||
searchFailures.add(readShardSearchFailure(in));
|
searchFailures.add(readShardSearchFailure(in));
|
||||||
}
|
}
|
||||||
this.searchFailures = unmodifiableList(searchFailures);
|
this.searchFailures = unmodifiableList(searchFailures);
|
||||||
|
this.timedOut = in.readBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
builder.field("took", took.millis());
|
builder.field("took", took.millis());
|
||||||
|
builder.field("timed_out", timedOut);
|
||||||
status.innerXContent(builder, params, false, false);
|
status.innerXContent(builder, params, false, false);
|
||||||
builder.startArray("failures");
|
builder.startArray("failures");
|
||||||
for (Failure failure: indexingFailures) {
|
for (Failure failure: indexingFailures) {
|
||||||
|
@ -19,7 +19,9 @@
|
|||||||
|
|
||||||
package org.elasticsearch.index.reindex;
|
package org.elasticsearch.index.reindex;
|
||||||
|
|
||||||
|
import org.elasticsearch.ExceptionsHelper;
|
||||||
import org.elasticsearch.action.bulk.BulkItemResponse.Failure;
|
import org.elasticsearch.action.bulk.BulkItemResponse.Failure;
|
||||||
|
import org.elasticsearch.action.search.ShardSearchFailure;
|
||||||
import org.elasticsearch.rest.RestChannel;
|
import org.elasticsearch.rest.RestChannel;
|
||||||
import org.elasticsearch.rest.RestStatus;
|
import org.elasticsearch.rest.RestStatus;
|
||||||
import org.elasticsearch.rest.action.support.RestToXContentListener;
|
import org.elasticsearch.rest.action.support.RestToXContentListener;
|
||||||
@ -35,12 +37,25 @@ public class BulkIndexByScrollResponseContentListener<R extends BulkIndexByScrol
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RestStatus getStatus(R response) {
|
protected RestStatus getStatus(R response) {
|
||||||
|
/*
|
||||||
|
* Return the highest numbered rest status under the assumption that higher numbered statuses are "more error" and thus more
|
||||||
|
* interesting to the user.
|
||||||
|
*/
|
||||||
RestStatus status = RestStatus.OK;
|
RestStatus status = RestStatus.OK;
|
||||||
|
if (response.isTimedOut()) {
|
||||||
|
status = RestStatus.REQUEST_TIMEOUT;
|
||||||
|
}
|
||||||
for (Failure failure : response.getIndexingFailures()) {
|
for (Failure failure : response.getIndexingFailures()) {
|
||||||
if (failure.getStatus().getStatus() > status.getStatus()) {
|
if (failure.getStatus().getStatus() > status.getStatus()) {
|
||||||
status = failure.getStatus();
|
status = failure.getStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (ShardSearchFailure failure: response.getSearchFailures()) {
|
||||||
|
RestStatus failureStatus = ExceptionsHelper.status(failure.getCause());
|
||||||
|
if (failureStatus.getStatus() > status.getStatus()) {
|
||||||
|
status = failureStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,9 @@ public class ReindexResponse extends BulkIndexByScrollResponse {
|
|||||||
public ReindexResponse() {
|
public ReindexResponse() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReindexResponse(TimeValue took, Status status, List<Failure> indexingFailures, List<ShardSearchFailure> searchFailures) {
|
public ReindexResponse(TimeValue took, Status status, List<Failure> indexingFailures, List<ShardSearchFailure> searchFailures,
|
||||||
super(took, status, indexingFailures, searchFailures);
|
boolean timedOut) {
|
||||||
|
super(took, status, indexingFailures, searchFailures, timedOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getCreated() {
|
public long getCreated() {
|
||||||
@ -46,6 +47,7 @@ public class ReindexResponse extends BulkIndexByScrollResponse {
|
|||||||
@Override
|
@Override
|
||||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
builder.field("took", getTook());
|
builder.field("took", getTook());
|
||||||
|
builder.field("timed_out", isTimedOut());
|
||||||
getStatus().innerXContent(builder, params, true, false);
|
getStatus().innerXContent(builder, params, true, false);
|
||||||
builder.startArray("failures");
|
builder.startArray("failures");
|
||||||
for (Failure failure: getIndexingFailures()) {
|
for (Failure failure: getIndexingFailures()) {
|
||||||
|
@ -107,7 +107,10 @@ public class RestUpdateByQueryAction extends
|
|||||||
internalRequest.setSize(internalRequest.getSearchRequest().source().size());
|
internalRequest.setSize(internalRequest.getSearchRequest().source().size());
|
||||||
internalRequest.setPipeline(request.param("pipeline"));
|
internalRequest.setPipeline(request.param("pipeline"));
|
||||||
internalRequest.getSearchRequest().source().size(request.paramAsInt("scroll_size", scrollSize));
|
internalRequest.getSearchRequest().source().size(request.paramAsInt("scroll_size", scrollSize));
|
||||||
|
// Let the requester set search timeout. It is probably only going to be useful for testing but who knows.
|
||||||
|
if (request.hasParam("search_timeout")) {
|
||||||
|
internalRequest.getSearchRequest().source().timeout(request.paramAsTime("search_timeout", null));
|
||||||
|
}
|
||||||
|
|
||||||
execute(request, internalRequest, channel);
|
execute(request, internalRequest, channel);
|
||||||
}
|
}
|
||||||
|
@ -191,8 +191,9 @@ public class TransportReindexAction extends HandledTransportAction<ReindexReques
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ReindexResponse buildResponse(TimeValue took, List<Failure> indexingFailures, List<ShardSearchFailure> searchFailures) {
|
protected ReindexResponse buildResponse(TimeValue took, List<Failure> indexingFailures, List<ShardSearchFailure> searchFailures,
|
||||||
return new ReindexResponse(took, task.getStatus(), indexingFailures, searchFailures);
|
boolean timedOut) {
|
||||||
|
return new ReindexResponse(took, task.getStatus(), indexingFailures, searchFailures, timedOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -96,8 +96,8 @@ public class TransportUpdateByQueryAction extends HandledTransportAction<UpdateB
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BulkIndexByScrollResponse buildResponse(TimeValue took, List<Failure> indexingFailures,
|
protected BulkIndexByScrollResponse buildResponse(TimeValue took, List<Failure> indexingFailures,
|
||||||
List<ShardSearchFailure> searchFailures) {
|
List<ShardSearchFailure> searchFailures, boolean timedOut) {
|
||||||
return new BulkIndexByScrollResponse(took, task.getStatus(), indexingFailures, searchFailures);
|
return new BulkIndexByScrollResponse(took, task.getStatus(), indexingFailures, searchFailures, timedOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -25,6 +25,7 @@ import org.elasticsearch.action.ActionRequest;
|
|||||||
import org.elasticsearch.action.ActionRequestBuilder;
|
import org.elasticsearch.action.ActionRequestBuilder;
|
||||||
import org.elasticsearch.action.ActionResponse;
|
import org.elasticsearch.action.ActionResponse;
|
||||||
import org.elasticsearch.action.DocWriteResponse;
|
import org.elasticsearch.action.DocWriteResponse;
|
||||||
|
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
|
||||||
import org.elasticsearch.action.bulk.BackoffPolicy;
|
import org.elasticsearch.action.bulk.BackoffPolicy;
|
||||||
import org.elasticsearch.action.bulk.BulkItemResponse;
|
import org.elasticsearch.action.bulk.BulkItemResponse;
|
||||||
import org.elasticsearch.action.bulk.BulkItemResponse.Failure;
|
import org.elasticsearch.action.bulk.BulkItemResponse.Failure;
|
||||||
@ -74,10 +75,12 @@ import java.util.concurrent.ExecutionException;
|
|||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.emptyMap;
|
import static java.util.Collections.emptyMap;
|
||||||
|
import static java.util.Collections.singleton;
|
||||||
import static org.apache.lucene.util.TestUtil.randomSimpleString;
|
import static org.apache.lucene.util.TestUtil.randomSimpleString;
|
||||||
import static org.elasticsearch.action.bulk.BackoffPolicy.constantBackoff;
|
import static org.elasticsearch.action.bulk.BackoffPolicy.constantBackoff;
|
||||||
import static org.elasticsearch.common.unit.TimeValue.timeValueMillis;
|
import static org.elasticsearch.common.unit.TimeValue.timeValueMillis;
|
||||||
@ -248,15 +251,33 @@ public class AsyncBulkByScrollActionTests extends ESTestCase {
|
|||||||
*/
|
*/
|
||||||
public void testShardFailuresAbortRequest() throws Exception {
|
public void testShardFailuresAbortRequest() throws Exception {
|
||||||
ShardSearchFailure shardFailure = new ShardSearchFailure(new RuntimeException("test"));
|
ShardSearchFailure shardFailure = new ShardSearchFailure(new RuntimeException("test"));
|
||||||
new DummyAbstractAsyncBulkByScrollAction()
|
InternalSearchResponse internalResponse = new InternalSearchResponse(null, null, null, null, false, null);
|
||||||
.onScrollResponse(new SearchResponse(null, scrollId(), 5, 4, randomLong(), new ShardSearchFailure[] { shardFailure }));
|
new DummyAbstractAsyncBulkByScrollAction().onScrollResponse(
|
||||||
|
new SearchResponse(internalResponse, scrollId(), 5, 4, randomLong(), new ShardSearchFailure[] { shardFailure }));
|
||||||
BulkIndexByScrollResponse response = listener.get();
|
BulkIndexByScrollResponse response = listener.get();
|
||||||
assertThat(response.getIndexingFailures(), emptyCollectionOf(Failure.class));
|
assertThat(response.getIndexingFailures(), emptyCollectionOf(Failure.class));
|
||||||
assertThat(response.getSearchFailures(), contains(shardFailure));
|
assertThat(response.getSearchFailures(), contains(shardFailure));
|
||||||
|
assertFalse(response.isTimedOut());
|
||||||
assertNull(response.getReasonCancelled());
|
assertNull(response.getReasonCancelled());
|
||||||
assertThat(client.scrollsCleared, contains(scrollId));
|
assertThat(client.scrollsCleared, contains(scrollId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mimicks search timeouts.
|
||||||
|
*/
|
||||||
|
public void testSearchTimeoutsAbortRequest() throws Exception {
|
||||||
|
InternalSearchResponse internalResponse = new InternalSearchResponse(null, null, null, null, true, null);
|
||||||
|
new DummyAbstractAsyncBulkByScrollAction()
|
||||||
|
.onScrollResponse(new SearchResponse(internalResponse, scrollId(), 5, 4, randomLong(), new ShardSearchFailure[0]));
|
||||||
|
BulkIndexByScrollResponse response = listener.get();
|
||||||
|
assertThat(response.getIndexingFailures(), emptyCollectionOf(Failure.class));
|
||||||
|
assertThat(response.getSearchFailures(), emptyCollectionOf(ShardSearchFailure.class));
|
||||||
|
assertTrue(response.isTimedOut());
|
||||||
|
assertNull(response.getReasonCancelled());
|
||||||
|
assertThat(client.scrollsCleared, contains(scrollId));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mimicks bulk indexing failures.
|
* Mimicks bulk indexing failures.
|
||||||
*/
|
*/
|
||||||
@ -370,6 +391,32 @@ public class AsyncBulkByScrollActionTests extends ESTestCase {
|
|||||||
assertEquals(defaultBackoffBeforeFailing, millis);
|
assertEquals(defaultBackoffBeforeFailing, millis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testRefreshIsFalseByDefault() throws Exception {
|
||||||
|
refreshTestCase(null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRefreshFalseDoesntMakeVisible() throws Exception {
|
||||||
|
refreshTestCase(false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRefreshTrueMakesVisible() throws Exception {
|
||||||
|
refreshTestCase(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshTestCase(Boolean refresh, boolean shouldRefresh) {
|
||||||
|
if (refresh != null) {
|
||||||
|
mainRequest.setRefresh(refresh);
|
||||||
|
}
|
||||||
|
DummyAbstractAsyncBulkByScrollAction action = new DummyAbstractAsyncBulkByScrollAction();
|
||||||
|
action.addDestinationIndices(singleton("foo"));
|
||||||
|
action.startNormalTermination(emptyList(), emptyList(), false);
|
||||||
|
if (shouldRefresh) {
|
||||||
|
assertArrayEquals(new String[] {"foo"}, client.lastRefreshRequest.get().indices());
|
||||||
|
} else {
|
||||||
|
assertNull("No refresh was attempted", client.lastRefreshRequest.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void testCancelBeforeInitialSearch() throws Exception {
|
public void testCancelBeforeInitialSearch() throws Exception {
|
||||||
cancelTaskCase((DummyAbstractAsyncBulkByScrollAction action) -> action.initialSearch());
|
cancelTaskCase((DummyAbstractAsyncBulkByScrollAction action) -> action.initialSearch());
|
||||||
}
|
}
|
||||||
@ -396,8 +443,8 @@ public class AsyncBulkByScrollActionTests extends ESTestCase {
|
|||||||
public void testCancelBeforeStartNormalTermination() throws Exception {
|
public void testCancelBeforeStartNormalTermination() throws Exception {
|
||||||
// Refresh or not doesn't matter - we don't try to refresh.
|
// Refresh or not doesn't matter - we don't try to refresh.
|
||||||
mainRequest.setRefresh(usually());
|
mainRequest.setRefresh(usually());
|
||||||
cancelTaskCase((DummyAbstractAsyncBulkByScrollAction action) -> action.startNormalTermination(emptyList(), emptyList()));
|
cancelTaskCase((DummyAbstractAsyncBulkByScrollAction action) -> action.startNormalTermination(emptyList(), emptyList(), false));
|
||||||
// This wouldn't return if we called refresh - the action would hang waiting for the refresh that we haven't mocked.
|
assertNull("No refresh was attempted", client.lastRefreshRequest.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cancelTaskCase(Consumer<DummyAbstractAsyncBulkByScrollAction> testMe) throws Exception {
|
private void cancelTaskCase(Consumer<DummyAbstractAsyncBulkByScrollAction> testMe) throws Exception {
|
||||||
@ -430,8 +477,8 @@ public class AsyncBulkByScrollActionTests extends ESTestCase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BulkIndexByScrollResponse buildResponse(TimeValue took, List<Failure> indexingFailures,
|
protected BulkIndexByScrollResponse buildResponse(TimeValue took, List<Failure> indexingFailures,
|
||||||
List<ShardSearchFailure> searchFailures) {
|
List<ShardSearchFailure> searchFailures, boolean timedOut) {
|
||||||
return new BulkIndexByScrollResponse(took, task.getStatus(), indexingFailures, searchFailures);
|
return new BulkIndexByScrollResponse(took, task.getStatus(), indexingFailures, searchFailures, timedOut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -445,6 +492,7 @@ public class AsyncBulkByScrollActionTests extends ESTestCase {
|
|||||||
private static class MyMockClient extends FilterClient {
|
private static class MyMockClient extends FilterClient {
|
||||||
private final List<String> scrollsCleared = new ArrayList<>();
|
private final List<String> scrollsCleared = new ArrayList<>();
|
||||||
private final AtomicInteger bulksAttempts = new AtomicInteger();
|
private final AtomicInteger bulksAttempts = new AtomicInteger();
|
||||||
|
private final AtomicReference<RefreshRequest> lastRefreshRequest = new AtomicReference<>();
|
||||||
|
|
||||||
private int bulksToReject = 0;
|
private int bulksToReject = 0;
|
||||||
|
|
||||||
@ -457,6 +505,11 @@ public class AsyncBulkByScrollActionTests extends ESTestCase {
|
|||||||
protected <Request extends ActionRequest<Request>, Response extends ActionResponse,
|
protected <Request extends ActionRequest<Request>, Response extends ActionResponse,
|
||||||
RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder>> void doExecute(
|
RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder>> void doExecute(
|
||||||
Action<Request, Response, RequestBuilder> action, Request request, ActionListener<Response> listener) {
|
Action<Request, Response, RequestBuilder> action, Request request, ActionListener<Response> listener) {
|
||||||
|
if (request instanceof RefreshRequest) {
|
||||||
|
lastRefreshRequest.set((RefreshRequest) request);
|
||||||
|
listener.onResponse(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (request instanceof ClearScrollRequest) {
|
if (request instanceof ClearScrollRequest) {
|
||||||
ClearScrollRequest clearScroll = (ClearScrollRequest) request;
|
ClearScrollRequest clearScroll = (ClearScrollRequest) request;
|
||||||
scrollsCleared.addAll(clearScroll.getScrollIds());
|
scrollsCleared.addAll(clearScroll.getScrollIds());
|
||||||
|
@ -19,14 +19,12 @@
|
|||||||
|
|
||||||
package org.elasticsearch.index.reindex;
|
package org.elasticsearch.index.reindex;
|
||||||
|
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
|
|
||||||
import org.elasticsearch.action.index.IndexRequestBuilder;
|
import org.elasticsearch.action.index.IndexRequestBuilder;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
|
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
|
||||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
|
||||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
|
||||||
|
|
||||||
public class ReindexBasicTests extends ReindexTestCase {
|
public class ReindexBasicTests extends ReindexTestCase {
|
||||||
@ -84,40 +82,4 @@ public class ReindexBasicTests extends ReindexTestCase {
|
|||||||
assertThat(copy.get(), responseMatcher().created(half).batches(half, 5));
|
assertThat(copy.get(), responseMatcher().created(half).batches(half, 5));
|
||||||
assertHitCount(client().prepareSearch("dest").setTypes("half").setSize(0).get(), half);
|
assertHitCount(client().prepareSearch("dest").setTypes("half").setSize(0).get(), half);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRefreshIsFalseByDefault() throws Exception {
|
|
||||||
refreshTestCase(null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testRefreshFalseDoesntMakeVisible() throws Exception {
|
|
||||||
refreshTestCase(false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testRefreshTrueMakesVisible() throws Exception {
|
|
||||||
refreshTestCase(true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes a reindex into an index with -1 refresh_interval and checks that
|
|
||||||
* the documents are visible properly.
|
|
||||||
*/
|
|
||||||
private void refreshTestCase(Boolean refresh, boolean visible) throws Exception {
|
|
||||||
CreateIndexRequestBuilder create = client().admin().indices().prepareCreate("dest").setSettings("refresh_interval", -1);
|
|
||||||
assertAcked(create);
|
|
||||||
ensureYellow();
|
|
||||||
indexRandom(true, client().prepareIndex("source", "test", "1").setSource("foo", "a"),
|
|
||||||
client().prepareIndex("source", "test", "2").setSource("foo", "a"),
|
|
||||||
client().prepareIndex("source", "test", "3").setSource("foo", "b"),
|
|
||||||
client().prepareIndex("source", "test", "4").setSource("foo", "c"));
|
|
||||||
assertHitCount(client().prepareSearch("source").setSize(0).get(), 4);
|
|
||||||
|
|
||||||
// Copy all the docs
|
|
||||||
ReindexRequestBuilder copy = reindex().source("source").destination("dest", "all");
|
|
||||||
if (refresh != null) {
|
|
||||||
copy.refresh(refresh);
|
|
||||||
}
|
|
||||||
assertThat(copy.get(), responseMatcher().created(4));
|
|
||||||
|
|
||||||
assertHitCount(client().prepareSearch("dest").setTypes("all").setSize(0).get(), visible ? 4 : 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ public class RoundTripTests extends ESTestCase {
|
|||||||
|
|
||||||
public void testReindexResponse() throws IOException {
|
public void testReindexResponse() throws IOException {
|
||||||
ReindexResponse response = new ReindexResponse(timeValueMillis(randomPositiveLong()), randomStatus(), randomIndexingFailures(),
|
ReindexResponse response = new ReindexResponse(timeValueMillis(randomPositiveLong()), randomStatus(), randomIndexingFailures(),
|
||||||
randomSearchFailures());
|
randomSearchFailures(), randomBoolean());
|
||||||
ReindexResponse tripped = new ReindexResponse();
|
ReindexResponse tripped = new ReindexResponse();
|
||||||
roundTrip(response, tripped);
|
roundTrip(response, tripped);
|
||||||
assertResponseEquals(response, tripped);
|
assertResponseEquals(response, tripped);
|
||||||
@ -110,7 +110,7 @@ public class RoundTripTests extends ESTestCase {
|
|||||||
|
|
||||||
public void testBulkIndexByScrollResponse() throws IOException {
|
public void testBulkIndexByScrollResponse() throws IOException {
|
||||||
BulkIndexByScrollResponse response = new BulkIndexByScrollResponse(timeValueMillis(randomPositiveLong()), randomStatus(),
|
BulkIndexByScrollResponse response = new BulkIndexByScrollResponse(timeValueMillis(randomPositiveLong()), randomStatus(),
|
||||||
randomIndexingFailures(), randomSearchFailures());
|
randomIndexingFailures(), randomSearchFailures(), randomBoolean());
|
||||||
BulkIndexByScrollResponse tripped = new BulkIndexByScrollResponse();
|
BulkIndexByScrollResponse tripped = new BulkIndexByScrollResponse();
|
||||||
roundTrip(response, tripped);
|
roundTrip(response, tripped);
|
||||||
assertResponseEquals(response, tripped);
|
assertResponseEquals(response, tripped);
|
||||||
|
@ -19,12 +19,9 @@
|
|||||||
|
|
||||||
package org.elasticsearch.index.reindex;
|
package org.elasticsearch.index.reindex;
|
||||||
|
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
|
|
||||||
import org.elasticsearch.search.sort.SortOrder;
|
import org.elasticsearch.search.sort.SortOrder;
|
||||||
|
|
||||||
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
|
|
||||||
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
|
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
|
||||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
|
||||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
|
||||||
|
|
||||||
public class UpdateByQueryBasicTests extends UpdateByQueryTestCase {
|
public class UpdateByQueryBasicTests extends UpdateByQueryTestCase {
|
||||||
@ -64,44 +61,4 @@ public class UpdateByQueryBasicTests extends UpdateByQueryTestCase {
|
|||||||
assertEquals(3, client().prepareGet("test", "test", "3").get().getVersion());
|
assertEquals(3, client().prepareGet("test", "test", "3").get().getVersion());
|
||||||
assertEquals(2, client().prepareGet("test", "test", "4").get().getVersion());
|
assertEquals(2, client().prepareGet("test", "test", "4").get().getVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRefreshIsFalseByDefault() throws Exception {
|
|
||||||
refreshTestCase(null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testRefreshFalseDoesntMakeVisible() throws Exception {
|
|
||||||
refreshTestCase(false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testRefreshTrueMakesVisible() throws Exception {
|
|
||||||
refreshTestCase(true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes an update_by_query on an index with -1 refresh_interval and
|
|
||||||
* checks that the documents are visible properly.
|
|
||||||
*/
|
|
||||||
private void refreshTestCase(Boolean refresh, boolean visible) throws Exception {
|
|
||||||
CreateIndexRequestBuilder create = client().admin().indices().prepareCreate("test").setSettings("refresh_interval", -1);
|
|
||||||
create.addMapping("test", "{\"dynamic\": \"false\"}");
|
|
||||||
assertAcked(create);
|
|
||||||
ensureYellow();
|
|
||||||
indexRandom(true, client().prepareIndex("test", "test", "1").setSource("foo", "a"),
|
|
||||||
client().prepareIndex("test", "test", "2").setSource("foo", "a"),
|
|
||||||
client().prepareIndex("test", "test", "3").setSource("foo", "b"),
|
|
||||||
client().prepareIndex("test", "test", "4").setSource("foo", "c"));
|
|
||||||
assertHitCount(client().prepareSearch("test").setQuery(matchQuery("foo", "a")).setSize(0).get(), 0);
|
|
||||||
|
|
||||||
// Now make foo searchable
|
|
||||||
assertAcked(client().admin().indices().preparePutMapping("test").setType("test")
|
|
||||||
.setSource("{\"test\": {\"properties\":{\"foo\": {\"type\": \"text\"}}}}"));
|
|
||||||
UpdateByQueryRequestBuilder update = request().source("test");
|
|
||||||
if (refresh != null) {
|
|
||||||
update.refresh(refresh);
|
|
||||||
}
|
|
||||||
assertThat(update.get(), responseMatcher().updated(4));
|
|
||||||
|
|
||||||
assertHitCount(client().prepareSearch("test").setQuery(matchQuery("foo", "a")).setSize(0).get(), visible ? 2 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,7 @@
|
|||||||
index: source
|
index: source
|
||||||
dest:
|
dest:
|
||||||
index: dest
|
index: dest
|
||||||
|
- is_false: timed_out
|
||||||
- match: {task: '/.+:\d+/'}
|
- match: {task: '/.+:\d+/'}
|
||||||
- set: {task: task}
|
- set: {task: task}
|
||||||
- is_false: updated
|
- is_false: updated
|
||||||
@ -125,7 +126,8 @@
|
|||||||
- match: {failures.0.id: "1"}
|
- match: {failures.0.id: "1"}
|
||||||
- match: {failures.0.status: 409}
|
- match: {failures.0.status: 409}
|
||||||
- match: {failures.0.cause.type: version_conflict_engine_exception}
|
- match: {failures.0.cause.type: version_conflict_engine_exception}
|
||||||
- match: {failures.0.cause.reason: "[foo][1]: version conflict, document already exists (current version [1])"}
|
# Use a regex so we don't mind if the version isn't always 1. Sometimes it comes out 2.
|
||||||
|
- match: {failures.0.cause.reason: "/\\[foo\\]\\[1\\]:.version.conflict,.document.already.exists.\\(current.version.\\[\\d+\\]\\)/"}
|
||||||
- match: {failures.0.cause.shard: /\d+/}
|
- match: {failures.0.cause.shard: /\d+/}
|
||||||
- match: {failures.0.cause.index: dest}
|
- match: {failures.0.cause.index: dest}
|
||||||
- is_true: took
|
- is_true: took
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
- do:
|
- do:
|
||||||
update-by-query:
|
update-by-query:
|
||||||
index: test
|
index: test
|
||||||
|
- is_false: timed_out
|
||||||
- match: {updated: 1}
|
- match: {updated: 1}
|
||||||
- match: {version_conflicts: 0}
|
- match: {version_conflicts: 0}
|
||||||
- match: {batches: 1}
|
- match: {batches: 1}
|
||||||
@ -86,7 +87,8 @@
|
|||||||
- match: {failures.0.id: "1"}
|
- match: {failures.0.id: "1"}
|
||||||
- match: {failures.0.status: 409}
|
- match: {failures.0.status: 409}
|
||||||
- match: {failures.0.cause.type: version_conflict_engine_exception}
|
- match: {failures.0.cause.type: version_conflict_engine_exception}
|
||||||
- match: {failures.0.cause.reason: "[foo][1]: version conflict, current version [2] is different than the one provided [1]"}
|
# Use a regex so we don't mind if the current version isn't always 1. Sometimes it comes out 2.
|
||||||
|
- match: {failures.0.cause.reason: "/\\[foo\\]\\[1\\]:.version.conflict,.current.version.\\[\\d+\\].is.different.than.the.one.provided.\\[\\d+\\]/"}
|
||||||
- match: {failures.0.cause.shard: /\d+/}
|
- match: {failures.0.cause.shard: /\d+/}
|
||||||
- match: {failures.0.cause.index: test}
|
- match: {failures.0.cause.index: test}
|
||||||
- is_true: took
|
- is_true: took
|
||||||
|
@ -47,7 +47,7 @@ public class GceDiscoveryPlugin extends Plugin {
|
|||||||
static {
|
static {
|
||||||
/*
|
/*
|
||||||
* GCE's http client changes access levels because its silly and we
|
* GCE's http client changes access levels because its silly and we
|
||||||
* can't allow that on any old stack stack so we pull it here, up front,
|
* can't allow that on any old stack so we pull it here, up front,
|
||||||
* so we can cleanly check the permissions for it. Without this changing
|
* so we can cleanly check the permissions for it. Without this changing
|
||||||
* the permission can fail if any part of core is on the stack because
|
* the permission can fail if any part of core is on the stack because
|
||||||
* our plugin permissions don't allow core to "reach through" plugins to
|
* our plugin permissions don't allow core to "reach through" plugins to
|
||||||
|
@ -137,6 +137,8 @@ final class TikaImpl {
|
|||||||
perms.add(new SecurityPermission("putProviderProperty.BC"));
|
perms.add(new SecurityPermission("putProviderProperty.BC"));
|
||||||
perms.add(new SecurityPermission("insertProvider"));
|
perms.add(new SecurityPermission("insertProvider"));
|
||||||
perms.add(new ReflectPermission("suppressAccessChecks"));
|
perms.add(new ReflectPermission("suppressAccessChecks"));
|
||||||
|
// xmlbeans, use by POI, needs to get the context classloader
|
||||||
|
perms.add(new RuntimePermission("getClassLoader"));
|
||||||
perms.setReadOnly();
|
perms.setReadOnly();
|
||||||
return perms;
|
return perms;
|
||||||
}
|
}
|
||||||
|
@ -27,4 +27,6 @@ grant {
|
|||||||
permission java.security.SecurityPermission "insertProvider";
|
permission java.security.SecurityPermission "insertProvider";
|
||||||
// TODO: fix POI XWPF to not do this: https://bz.apache.org/bugzilla/show_bug.cgi?id=58597
|
// TODO: fix POI XWPF to not do this: https://bz.apache.org/bugzilla/show_bug.cgi?id=58597
|
||||||
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
|
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
|
||||||
|
// needed by xmlbeans, as part of POI for MS xml docs
|
||||||
|
permission java.lang.RuntimePermission "getClassLoader";
|
||||||
};
|
};
|
||||||
|
File diff suppressed because one or more lines are too long
@ -137,6 +137,8 @@ final class TikaImpl {
|
|||||||
perms.add(new SecurityPermission("putProviderProperty.BC"));
|
perms.add(new SecurityPermission("putProviderProperty.BC"));
|
||||||
perms.add(new SecurityPermission("insertProvider"));
|
perms.add(new SecurityPermission("insertProvider"));
|
||||||
perms.add(new ReflectPermission("suppressAccessChecks"));
|
perms.add(new ReflectPermission("suppressAccessChecks"));
|
||||||
|
// xmlbeans, use by POI, needs to get the context classloader
|
||||||
|
perms.add(new RuntimePermission("getClassLoader"));
|
||||||
perms.setReadOnly();
|
perms.setReadOnly();
|
||||||
return perms;
|
return perms;
|
||||||
}
|
}
|
||||||
|
@ -27,4 +27,6 @@ grant {
|
|||||||
permission java.security.SecurityPermission "insertProvider";
|
permission java.security.SecurityPermission "insertProvider";
|
||||||
// TODO: fix POI XWPF to not do this: https://bz.apache.org/bugzilla/show_bug.cgi?id=58597
|
// TODO: fix POI XWPF to not do this: https://bz.apache.org/bugzilla/show_bug.cgi?id=58597
|
||||||
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
|
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
|
||||||
|
// needed by xmlbeans, as part of POI for MS xml docs
|
||||||
|
permission java.lang.RuntimePermission "getClassLoader";
|
||||||
};
|
};
|
||||||
|
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user