mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-25 17:38:44 +00:00
Squashed commit of the following: commit 20835037c98e7d2fac4206c372717a05a27c4790 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 18 15:27:17 2015 -0700 Use Enum for "_primary" preference commit 325acbe4585179190a959ba3101ee63b99f1931a Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 18 14:32:41 2015 -0700 Use ?preference=_primary automatically for realtime GET operations commit edd49434af5de7e55928f27a1c9ed0fddb1fb133 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 18 14:32:06 2015 -0700 Move engine creation into protected createNewEngine method commit 67a797a9235d4aa376ff4af16f3944d907df4577 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 18 13:14:01 2015 -0700 Factor out AssertingSearcher so it can be used by mock Engines commit 62b0c28df8c23cc0b8205b33f7595c68ff940e2b Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 18 11:43:17 2015 -0700 Use IndexMetaData.isIndexUsingShadowReplicas helper commit 1a0d45629457578a60ae5bccbeba05acf5d79ddd Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 18 09:59:31 2015 -0700 Rename usesSharedFilesystem -> isOnSharedFilesystem commit 73c62df4fc7da8a5ed557620a83910d89b313aa1 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 18 09:58:02 2015 -0700 Add MockShadowEngine and hook it up to be used commit c8e8db473830fce1bdca3c4df80a685e782383bc Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 18 09:45:50 2015 -0700 Clarify comment about pre-defined mappings commit 60a4d5374af5262bd415f4ef40f635278ed12a03 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 18 09:18:22 2015 -0700 Add a test for shadow replicas that uses field data commit 7346f9f382f83a21cd2445b3386fe67472bc3184 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 18 08:37:14 2015 -0700 Revert changes to RecoveryTarget.java commit d90d6980c9b737bd8c0f4339613a5373b1645e95 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 18 08:35:44 2015 -0700 Rename `ownsShard` to `canDeleteShardContent` commit 23001af834d66278ac84d9a72c37b5d1f3a10a7b Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 18 08:35:25 2015 -0700 Remove ShadowEngineFactory, add .newReadOnlyEngine method in EngineFactory commit b64fef1d2c5e167713e869b22d388ff479252173 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 18 08:25:19 2015 -0700 Add warning that predefined mappings should be used commit a1b8b8cf0db49d1bd1aeb84e51491f7f0de43b59 Author: Lee Hinman <lee@writequit.org> Date: Tue Feb 17 14:31:50 2015 -0700 Remove unused import and fix index creation example in docs commit 0b1b852365ceafc0df86866ac3a4ffb6988b08e4 Merge: b9d1fed a22bd49 Author: Lee Hinman <lee@writequit.org> Date: Tue Feb 17 10:56:02 2015 -0700 Merge remote-tracking branch 'refs/remotes/origin/master' into shadow-replicas commit b9d1fed25ae472a9dce1904eb806702fba4d9786 Merge: 4473e63 41fd4d8 Author: Lee Hinman <lee@writequit.org> Date: Tue Feb 17 09:02:27 2015 -0700 Merge remote-tracking branch 'refs/remotes/origin/master' into shadow-replicas commit 4473e630460e2f0ca2a2e2478f3712f39a64c919 Author: Lee Hinman <lee@writequit.org> Date: Tue Feb 17 09:00:39 2015 -0700 Add asciidoc documentation for shadow replicas commit eb699c19f04965952ae45e2caf107124837c4654 Author: Simon Willnauer <simonw@apache.org> Date: Tue Feb 17 16:15:39 2015 +0100 remove last nocommit commit c5ece6d16d423fbdd36f5d789bd8daa5724d77b0 Author: Simon Willnauer <simonw@apache.org> Date: Tue Feb 17 16:13:12 2015 +0100 simplify shadow engine commit 45cd34a12a442080477da3ef14ab2fe7947ea97e Author: Simon Willnauer <simonw@apache.org> Date: Tue Feb 17 11:32:57 2015 +0100 fix tests commit 744f228c192602a6737051571e040731d413ba8b Author: Simon Willnauer <simonw@apache.org> Date: Tue Feb 17 11:28:12 2015 +0100 revert changes to IndexShardGateway - these are leftovers from previous iterations commit 11886b7653dabc23655ec76d112f291301f98f4a Author: Simon Willnauer <simonw@apache.org> Date: Tue Feb 17 11:26:48 2015 +0100 Back out non-shared FS code. this will go in in a second iteration commit 77fba571f150a0ca7fb340603669522c3ed65363 Merge: e8ad614 2e3c6a9 Author: Simon Willnauer <simonw@apache.org> Date: Tue Feb 17 11:16:46 2015 +0100 Merge branch 'master' into shadow-replicas Conflicts: src/main/java/org/elasticsearch/index/engine/Engine.java commit e8ad61467304e6d175257e389b8406d2a6cf8dba Merge: 48a700d 1b8d8da Author: Simon Willnauer <simonw@apache.org> Date: Tue Feb 17 10:54:20 2015 +0100 Merge branch 'master' into shadow-replicas commit 48a700d23cff117b8e4851d4008364f92b8272a0 Author: Simon Willnauer <simonw@apache.org> Date: Tue Feb 17 10:50:59 2015 +0100 add test for failing shadow engine / remove nocommit commit d77414c5e7b2cde830a8e3f70fe463ccc904d4d0 Author: Simon Willnauer <simonw@apache.org> Date: Tue Feb 17 10:27:56 2015 +0100 remove nocommits in IndexMetaData commit abb696563a9e418d3f842a790fcb832f91150be2 Author: Simon Willnauer <simonw@apache.org> Date: Mon Feb 16 17:05:02 2015 +0100 remove nocommit and simplify delete logic commit 82b9f0449108cd4741568d9b4495bf6c10a5b019 Author: Simon Willnauer <simonw@apache.org> Date: Mon Feb 16 16:45:27 2015 +0100 reduce the changes compared to master commit 28f069b6d99a65e285ac8c821e6a332a1d8eb315 Author: Simon Willnauer <simonw@apache.org> Date: Mon Feb 16 16:43:46 2015 +0100 fix primary relocation commit c4c999dd61a44a7a0db9798275a622f2b85b1039 Merge: 2ae80f9 455a85d Author: Simon Willnauer <simonw@apache.org> Date: Mon Feb 16 15:04:26 2015 +0100 Merge branch 'master' into shadow-replicas commit 2ae80f9689346f8fd346a0d3775a6341874d8bef Author: Lee Hinman <lee@writequit.org> Date: Fri Feb 13 16:25:34 2015 -0700 throw UnsupportedOperationException on write operations in ShadowEngine commit 740c28dd9ef987bf56b670fa1a8bcc6de2845819 Merge: e5bc047 305ba33 Author: Lee Hinman <lee@writequit.org> Date: Fri Feb 13 15:38:39 2015 -0700 Merge branch 'master' into shadow-replicas commit e5bc047d7c872ae960d397b1ae7b4b78d6a1ea10 Author: Lee Hinman <lee@writequit.org> Date: Fri Feb 13 11:38:09 2015 -0700 Don't replicate document request when using shadow replicas commit 213292e0679d8ae1492ea11861178236f4abd8ea Author: Simon Willnauer <simonw@apache.org> Date: Fri Feb 13 13:58:05 2015 +0100 add one more nocommit commit 83d171cf632f9b77cca9de58505f7db8fcda5599 Merge: aea9692 09eb8d1 Author: Simon Willnauer <simonw@apache.org> Date: Fri Feb 13 13:52:29 2015 +0100 Merge branch 'master' into shadow-replicas commit aea96920d995dacef294e48e719ba18f1ecf5860 Author: Simon Willnauer <simonw@apache.org> Date: Fri Feb 13 09:56:41 2015 +0100 revert unneeded changes on Store commit ea4e3e58dc6959a92c06d5990276268d586735f3 Author: Lee Hinman <lee@writequit.org> Date: Thu Feb 12 14:26:30 2015 -0700 Add documentation to ShadowIndexShard, remove nocommit commit 4f71c8d9f706a0c1c39aa3a370efb1604559d928 Author: Lee Hinman <lee@writequit.org> Date: Thu Feb 12 14:17:22 2015 -0700 Add documentation to ShadowEngine commit 28a9d1842722acba7ea69e0fa65200444532a30c Author: Lee Hinman <lee@writequit.org> Date: Thu Feb 12 14:08:25 2015 -0700 Remove nocommit, document canDeleteIndexContents commit d8d59dbf6d0525cd823d97268d035820e5727ac9 Author: Lee Hinman <lee@writequit.org> Date: Thu Feb 12 10:34:32 2015 -0700 Refactor more shared methods into the abstract Engine commit a7eb53c1e8b8fbfd9281b43ae39eacbe3cd1a0a6 Author: Simon Willnauer <simonw@apache.org> Date: Thu Feb 12 17:38:59 2015 +0100 Simplify shared filesystem recovery by using a dedicated recovery handler that skip most phases and enforces shard closing on the soruce before the target opens it's engine commit a62b9a70adad87d7492c526f4daf868cb05018d9 Author: Simon Willnauer <simonw@apache.org> Date: Thu Feb 12 15:59:54 2015 +0100 fix compile error after upstream changes commit abda7807bc3328a89fd783ca7ad8c6deac35f16f Merge: f229719 35f6496 Author: Simon Willnauer <simonw@apache.org> Date: Thu Feb 12 15:57:28 2015 +0100 Merge branch 'master' into shadow-replicas Conflicts: src/main/java/org/elasticsearch/index/engine/Engine.java commit f2297199b7dd5d3f9f1f109d0ddf3dd83390b0d1 Author: Simon Willnauer <simonw@apache.org> Date: Thu Feb 12 12:41:32 2015 +0100 first cut at catchup from primary make flush to a refresh factor our ShadowIndexShard to have IndexShard be idential to the master and least intrusive cleanup abstractions commit 4a367c07505b84b452807a58890f1cbe21711f27 Author: Simon Willnauer <simonw@apache.org> Date: Thu Feb 12 09:50:36 2015 +0100 fix primary promotion commit cf2fb807e7e243f1ad603a79bc9d5f31a499b769 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 11 16:45:41 2015 -0700 Make assertPathHasBeenCleared recursive commit 5689b7d2f84ca1c41e4459030af56cb9c0151eff Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 11 15:58:19 2015 -0700 Add testShadowReplicaNaturalRelocation commit fdbe4133537eaeb768747c2200cfc91878afeb97 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 11 15:28:57 2015 -0700 Use check for shared filesystem in primary -> primary relocation Also adds a nocommit commit 06e2eb4496762130af87ce68a47d360962091697 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 11 15:21:32 2015 -0700 Add a test checking that indices with shadow replicas clean up after themselves commit e4dbfb09a689b449f0edf6ee24222d7eaba2a215 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 11 15:08:18 2015 -0700 Fix segment info for ShadowEngine, remove test nocommit commit 80cf0e884c66eda7d59ac5d59235e1ce215af8f5 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 11 14:30:13 2015 -0700 Remove nocommit in ShadowEngineTests#testFailStart() commit 5e33eeaca971807b342f9be51a6a566eee005251 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 11 14:22:59 2015 -0700 Remove overly-complex test commit 2378fbb917b467e79c0262d7a41c23321bbeb147 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 11 13:45:44 2015 -0700 Fix missing import commit 52e9cd1b8334a5dd228d5d68bd03fd0040e9c8e9 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 11 13:45:05 2015 -0700 Add a test for replica -> primary promotion commit a95adbeded426d7f69f6ddc4cbd6712b6f6380b4 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 11 12:54:14 2015 -0700 Remove tests that don't apply to ShadowEngine commit 1896feda9de69e4f9cf774ef6748a5c50e953946 Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 11 10:29:12 2015 -0700 Add testShadowEngineIgnoresWriteOperations and testSearchResultRelease commit 67d7df41eac5e10a1dd63ddb31de74e326e9d38b Author: Lee Hinman <lee@writequit.org> Date: Wed Feb 11 10:06:05 2015 -0700 Add start of ShadowEngine unit tests commit ca9beb2d93d9b5af9aa6c75dbc0ead4ef57e220d Merge: 2d42736 57a4646 Author: Simon Willnauer <simonw@apache.org> Date: Wed Feb 11 18:03:53 2015 +0100 Merge branch 'master' into shadow-replicas commit 2d42736fed3ed8afda7e4aff10b65d292e1c6f92 Author: Simon Willnauer <simonw@apache.org> Date: Wed Feb 11 17:51:22 2015 +0100 shortcut recovery if we are on a shared FS - no need to compare files etc. commit 24d36c92dd82adce650e7ac8e9f0b43c83b2dc53 Author: Simon Willnauer <simonw@apache.org> Date: Wed Feb 11 17:08:08 2015 +0100 utilize the new delete code commit 2a2eed10f58825aae29ffe4cf01aefa5743a97c7 Merge: 343dc0b 173cfc1 Author: Simon Willnauer <simonw@apache.org> Date: Wed Feb 11 16:07:41 2015 +0100 Merge branch 'master' into shadow-replicas Conflicts: src/main/java/org/elasticsearch/gateway/GatewayMetaState.java commit 343dc0b527a7052acdc783ac5abcaad1ef78dbda Author: Simon Willnauer <simonw@apache.org> Date: Wed Feb 11 16:05:28 2015 +0100 long adder is not available in java7 commit be02cabfeebaea74b51b212957a2a466cfbfb716 Author: Lee Hinman <lee@writequit.org> Date: Tue Feb 10 22:04:24 2015 -0700 Add test that restarts nodes to ensure shadow replicas recover commit 7fcb373f0617050ca1a5a577b8cf32e32dc612b0 Author: Simon Willnauer <simonw@apache.org> Date: Tue Feb 10 23:19:21 2015 +0100 make test more evil commit 38135af0c1991b88f168ece0efb72ffe9498ff59 Author: Simon Willnauer <simonw@apache.org> Date: Tue Feb 10 22:25:11 2015 +0100 make tests pass commit 05975af69e6db63cb95f3e40d25bfa7174e006ea Author: Lee Hinman <lee@writequit.org> Date: Mon Jan 12 18:44:29 2015 +0100 Add ShadowEngine
552 lines
27 KiB
Java
552 lines
27 KiB
Java
/*
|
|
* 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.gateway;
|
|
|
|
import com.google.common.collect.Lists;
|
|
import com.google.common.collect.Maps;
|
|
import org.apache.lucene.util.IOUtils;
|
|
import org.elasticsearch.ElasticsearchException;
|
|
import org.elasticsearch.ElasticsearchIllegalArgumentException;
|
|
import org.elasticsearch.ElasticsearchIllegalStateException;
|
|
import org.elasticsearch.Version;
|
|
import org.elasticsearch.cluster.ClusterChangedEvent;
|
|
import org.elasticsearch.cluster.ClusterState;
|
|
import org.elasticsearch.cluster.ClusterStateListener;
|
|
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
|
import org.elasticsearch.cluster.metadata.MetaData;
|
|
import org.elasticsearch.cluster.node.DiscoveryNode;
|
|
import org.elasticsearch.cluster.routing.DjbHashFunction;
|
|
import org.elasticsearch.cluster.routing.HashFunction;
|
|
import org.elasticsearch.cluster.routing.SimpleHashFunction;
|
|
import org.elasticsearch.common.Nullable;
|
|
import org.elasticsearch.common.component.AbstractComponent;
|
|
import org.elasticsearch.common.inject.Inject;
|
|
import org.elasticsearch.common.settings.ImmutableSettings;
|
|
import org.elasticsearch.common.settings.Settings;
|
|
import org.elasticsearch.common.unit.TimeValue;
|
|
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
|
|
import org.elasticsearch.common.util.concurrent.FutureUtils;
|
|
import org.elasticsearch.common.xcontent.ToXContent;
|
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
|
import org.elasticsearch.common.xcontent.XContentParser;
|
|
import org.elasticsearch.common.xcontent.XContentType;
|
|
import org.elasticsearch.env.NodeEnvironment;
|
|
import org.elasticsearch.env.ShardLock;
|
|
import org.elasticsearch.index.Index;
|
|
import org.elasticsearch.indices.IndicesService;
|
|
import org.elasticsearch.threadpool.ThreadPool;
|
|
|
|
import java.io.IOException;
|
|
import java.nio.file.DirectoryStream;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.concurrent.ScheduledFuture;
|
|
import java.util.regex.Pattern;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public class GatewayMetaState extends AbstractComponent implements ClusterStateListener {
|
|
|
|
static final String GLOBAL_STATE_FILE_PREFIX = "global-";
|
|
private static final String INDEX_STATE_FILE_PREFIX = "state-";
|
|
static final Pattern GLOBAL_STATE_FILE_PATTERN = Pattern.compile(GLOBAL_STATE_FILE_PREFIX + "(\\d+)(" + MetaDataStateFormat.STATE_FILE_EXTENSION + ")?");
|
|
static final Pattern INDEX_STATE_FILE_PATTERN = Pattern.compile(INDEX_STATE_FILE_PREFIX + "(\\d+)(" + MetaDataStateFormat.STATE_FILE_EXTENSION + ")?");
|
|
private static final String GLOBAL_STATE_LOG_TYPE = "[_global]";
|
|
private static final String DEPRECATED_SETTING_ROUTING_HASH_FUNCTION = "cluster.routing.operation.hash.type";
|
|
private static final String DEPRECATED_SETTING_ROUTING_USE_TYPE = "cluster.routing.operation.use_type";
|
|
public static final String GATEWAY_DANGLING_TIMEOUT = "gateway.dangling_timeout";
|
|
public static final String GATEWAY_DELETE_TIMEOUT = "gateway.delete_timeout";
|
|
public static final String GATEWAY_AUTO_IMPORT_DANGLED = "gateway.auto_import_dangled";
|
|
// legacy - this used to be in a different package
|
|
private static final String GATEWAY_LOCAL_DANGLING_TIMEOUT = "gateway.local.dangling_timeout";
|
|
private static final String GATEWAY_LOCAL_AUTO_IMPORT_DANGLED = "gateway.local.auto_import_dangled";
|
|
|
|
static enum AutoImportDangledState {
|
|
NO() {
|
|
@Override
|
|
public boolean shouldImport() {
|
|
return false;
|
|
}
|
|
},
|
|
YES() {
|
|
@Override
|
|
public boolean shouldImport() {
|
|
return true;
|
|
}
|
|
},
|
|
CLOSED() {
|
|
@Override
|
|
public boolean shouldImport() {
|
|
return true;
|
|
}
|
|
};
|
|
|
|
public abstract boolean shouldImport();
|
|
|
|
public static AutoImportDangledState fromString(String value) {
|
|
if ("no".equalsIgnoreCase(value)) {
|
|
return NO;
|
|
} else if ("yes".equalsIgnoreCase(value)) {
|
|
return YES;
|
|
} else if ("closed".equalsIgnoreCase(value)) {
|
|
return CLOSED;
|
|
} else {
|
|
throw new ElasticsearchIllegalArgumentException("failed to parse [" + value + "], not a valid auto dangling import type");
|
|
}
|
|
}
|
|
}
|
|
|
|
private final NodeEnvironment nodeEnv;
|
|
private final ThreadPool threadPool;
|
|
|
|
private final LocalAllocateDangledIndices allocateDangledIndices;
|
|
|
|
@Nullable
|
|
private volatile MetaData currentMetaData;
|
|
|
|
private final XContentType format;
|
|
private final ToXContent.Params formatParams;
|
|
private final ToXContent.Params gatewayModeFormatParams;
|
|
|
|
|
|
private final AutoImportDangledState autoImportDangled;
|
|
private final TimeValue danglingTimeout;
|
|
private final TimeValue deleteTimeout;
|
|
private final Map<String, DanglingIndex> danglingIndices = ConcurrentCollections.newConcurrentMap();
|
|
private final Object danglingMutex = new Object();
|
|
private final IndicesService indicesService;
|
|
|
|
@Inject
|
|
public GatewayMetaState(Settings settings, ThreadPool threadPool, NodeEnvironment nodeEnv,
|
|
TransportNodesListGatewayMetaState nodesListGatewayMetaState, LocalAllocateDangledIndices allocateDangledIndices,
|
|
IndicesService indicesService) throws Exception {
|
|
super(settings);
|
|
this.nodeEnv = nodeEnv;
|
|
this.threadPool = threadPool;
|
|
this.format = XContentType.fromRestContentType(settings.get("format", "smile"));
|
|
this.allocateDangledIndices = allocateDangledIndices;
|
|
nodesListGatewayMetaState.init(this);
|
|
|
|
if (this.format == XContentType.SMILE) {
|
|
Map<String, String> params = Maps.newHashMap();
|
|
params.put("binary", "true");
|
|
formatParams = new ToXContent.MapParams(params);
|
|
Map<String, String> gatewayModeParams = Maps.newHashMap();
|
|
gatewayModeParams.put("binary", "true");
|
|
gatewayModeParams.put(MetaData.CONTEXT_MODE_PARAM, MetaData.CONTEXT_MODE_GATEWAY);
|
|
gatewayModeFormatParams = new ToXContent.MapParams(gatewayModeParams);
|
|
} else {
|
|
formatParams = ToXContent.EMPTY_PARAMS;
|
|
Map<String, String> gatewayModeParams = Maps.newHashMap();
|
|
gatewayModeParams.put(MetaData.CONTEXT_MODE_PARAM, MetaData.CONTEXT_MODE_GATEWAY);
|
|
gatewayModeFormatParams = new ToXContent.MapParams(gatewayModeParams);
|
|
}
|
|
|
|
this.autoImportDangled = AutoImportDangledState.fromString(settings.get(GATEWAY_AUTO_IMPORT_DANGLED, settings.get(GATEWAY_LOCAL_AUTO_IMPORT_DANGLED, AutoImportDangledState.YES.toString())));
|
|
this.danglingTimeout = settings.getAsTime(GATEWAY_DANGLING_TIMEOUT, settings.getAsTime(GATEWAY_LOCAL_DANGLING_TIMEOUT, TimeValue.timeValueHours(2)));
|
|
this.deleteTimeout = settings.getAsTime(GATEWAY_DELETE_TIMEOUT, TimeValue.timeValueSeconds(30));
|
|
|
|
logger.debug("using {} [{}], {} [{}], with {} [{}]",
|
|
GATEWAY_AUTO_IMPORT_DANGLED, this.autoImportDangled,
|
|
GATEWAY_DELETE_TIMEOUT, this.deleteTimeout,
|
|
GATEWAY_DANGLING_TIMEOUT, this.danglingTimeout);
|
|
if (DiscoveryNode.masterNode(settings) || DiscoveryNode.dataNode(settings)) {
|
|
nodeEnv.ensureAtomicMoveSupported();
|
|
}
|
|
if (DiscoveryNode.masterNode(settings)) {
|
|
try {
|
|
ensureNoPre019State();
|
|
pre20Upgrade();
|
|
long start = System.currentTimeMillis();
|
|
loadState();
|
|
logger.debug("took {} to load state", TimeValue.timeValueMillis(System.currentTimeMillis() - start));
|
|
} catch (Exception e) {
|
|
logger.error("failed to read local state, exiting...", e);
|
|
throw e;
|
|
}
|
|
}
|
|
this.indicesService = indicesService;
|
|
}
|
|
|
|
public MetaData loadMetaState() throws Exception {
|
|
return loadState();
|
|
}
|
|
|
|
@Override
|
|
public void clusterChanged(ClusterChangedEvent event) {
|
|
final ClusterState state = event.state();
|
|
if (state.blocks().disableStatePersistence()) {
|
|
// reset the current metadata, we need to start fresh...
|
|
this.currentMetaData = null;
|
|
return;
|
|
}
|
|
|
|
MetaData newMetaData = state.metaData();
|
|
// we don't check if metaData changed, since we might be called several times and we need to check dangling...
|
|
|
|
boolean success = true;
|
|
// only applied to master node, writing the global and index level states
|
|
if (state.nodes().localNode().masterNode()) {
|
|
// check if the global state changed?
|
|
if (currentMetaData == null || !MetaData.isGlobalStateEquals(currentMetaData, newMetaData)) {
|
|
try {
|
|
writeGlobalState("changed", newMetaData);
|
|
} catch (Throwable e) {
|
|
success = false;
|
|
}
|
|
}
|
|
|
|
// check and write changes in indices
|
|
for (IndexMetaData indexMetaData : newMetaData) {
|
|
String writeReason = null;
|
|
IndexMetaData currentIndexMetaData;
|
|
if (currentMetaData == null) {
|
|
// a new event..., check from the state stored
|
|
try {
|
|
currentIndexMetaData = loadIndexState(indexMetaData.index());
|
|
} catch (IOException ex) {
|
|
throw new ElasticsearchException("failed to load index state", ex);
|
|
}
|
|
} else {
|
|
currentIndexMetaData = currentMetaData.index(indexMetaData.index());
|
|
}
|
|
if (currentIndexMetaData == null) {
|
|
writeReason = "freshly created";
|
|
} else if (currentIndexMetaData.version() != indexMetaData.version()) {
|
|
writeReason = "version changed from [" + currentIndexMetaData.version() + "] to [" + indexMetaData.version() + "]";
|
|
}
|
|
|
|
// we update the writeReason only if we really need to write it
|
|
if (writeReason == null) {
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
writeIndex(writeReason, indexMetaData, currentIndexMetaData);
|
|
} catch (Throwable e) {
|
|
success = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// handle dangling indices, we handle those for all nodes that have a node file (data or master)
|
|
if (nodeEnv.hasNodeFile()) {
|
|
if (danglingTimeout.millis() >= 0) {
|
|
synchronized (danglingMutex) {
|
|
for (String danglingIndex : danglingIndices.keySet()) {
|
|
if (newMetaData.hasIndex(danglingIndex)) {
|
|
logger.debug("[{}] no longer dangling (created), removing", danglingIndex);
|
|
DanglingIndex removed = danglingIndices.remove(danglingIndex);
|
|
FutureUtils.cancel(removed.future);
|
|
}
|
|
}
|
|
// delete indices that are no longer part of the metadata
|
|
try {
|
|
for (String indexName : nodeEnv.findAllIndices()) {
|
|
// if we have the index on the metadata, don't delete it
|
|
if (newMetaData.hasIndex(indexName)) {
|
|
continue;
|
|
}
|
|
if (danglingIndices.containsKey(indexName)) {
|
|
// already dangling, continue
|
|
continue;
|
|
}
|
|
final IndexMetaData indexMetaData = loadIndexState(indexName);
|
|
if (indexMetaData != null) {
|
|
if(autoImportDangled.shouldImport()){
|
|
logger.info("[{}] dangling index, exists on local file system, but not in cluster metadata, auto import to cluster state [{}]", indexName, autoImportDangled);
|
|
danglingIndices.put(indexName, new DanglingIndex(indexName, null));
|
|
} else if (danglingTimeout.millis() == 0) {
|
|
logger.info("[{}] dangling index, exists on local file system, but not in cluster metadata, timeout set to 0, deleting now", indexName);
|
|
indicesService.deleteIndexStore("dangling index with timeout set to 0", indexMetaData);
|
|
} else {
|
|
logger.info("[{}] dangling index, exists on local file system, but not in cluster metadata, scheduling to delete in [{}], auto import to cluster state [{}]", indexName, danglingTimeout, autoImportDangled);
|
|
danglingIndices.put(indexName,
|
|
new DanglingIndex(indexName,
|
|
threadPool.schedule(danglingTimeout,
|
|
ThreadPool.Names.SAME,
|
|
new RemoveDanglingIndex(indexMetaData))));
|
|
}
|
|
}
|
|
}
|
|
} catch (Throwable e) {
|
|
logger.warn("failed to find dangling indices", e);
|
|
}
|
|
}
|
|
}
|
|
if (autoImportDangled.shouldImport() && !danglingIndices.isEmpty()) {
|
|
final List<IndexMetaData> dangled = Lists.newArrayList();
|
|
for (String indexName : danglingIndices.keySet()) {
|
|
IndexMetaData indexMetaData;
|
|
try {
|
|
indexMetaData = loadIndexState(indexName);
|
|
} catch (IOException ex) {
|
|
throw new ElasticsearchException("failed to load index state", ex);
|
|
}
|
|
if (indexMetaData == null) {
|
|
logger.debug("failed to find state for dangling index [{}]", indexName);
|
|
continue;
|
|
}
|
|
// we might have someone copying over an index, renaming the directory, handle that
|
|
if (!indexMetaData.index().equals(indexName)) {
|
|
logger.info("dangled index directory name is [{}], state name is [{}], renaming to directory name", indexName, indexMetaData.index());
|
|
indexMetaData = IndexMetaData.builder(indexMetaData).index(indexName).build();
|
|
}
|
|
if (autoImportDangled == AutoImportDangledState.CLOSED) {
|
|
indexMetaData = IndexMetaData.builder(indexMetaData).state(IndexMetaData.State.CLOSE).build();
|
|
}
|
|
if (indexMetaData != null) {
|
|
dangled.add(indexMetaData);
|
|
}
|
|
}
|
|
IndexMetaData[] dangledIndices = dangled.toArray(new IndexMetaData[dangled.size()]);
|
|
try {
|
|
allocateDangledIndices.allocateDangled(dangledIndices, new LocalAllocateDangledIndices.Listener() {
|
|
@Override
|
|
public void onResponse(LocalAllocateDangledIndices.AllocateDangledResponse response) {
|
|
logger.trace("allocated dangled");
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(Throwable e) {
|
|
logger.info("failed to send allocated dangled", e);
|
|
}
|
|
});
|
|
} catch (Throwable e) {
|
|
logger.warn("failed to send allocate dangled", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (success) {
|
|
currentMetaData = newMetaData;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a StateFormat that can read and write {@link MetaData}
|
|
*/
|
|
static MetaDataStateFormat<MetaData> globalStateFormat(XContentType format, final ToXContent.Params formatParams, final boolean deleteOldFiles) {
|
|
return new MetaDataStateFormat<MetaData>(format, deleteOldFiles) {
|
|
|
|
@Override
|
|
public void toXContent(XContentBuilder builder, MetaData state) throws IOException {
|
|
MetaData.Builder.toXContent(state, builder, formatParams);
|
|
}
|
|
|
|
@Override
|
|
public MetaData fromXContent(XContentParser parser) throws IOException {
|
|
return MetaData.Builder.fromXContent(parser);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Returns a StateFormat that can read and write {@link IndexMetaData}
|
|
*/
|
|
static MetaDataStateFormat<IndexMetaData> indexStateFormat(XContentType format, final ToXContent.Params formatParams, boolean deleteOldFiles) {
|
|
return new MetaDataStateFormat<IndexMetaData>(format, deleteOldFiles) {
|
|
|
|
@Override
|
|
public void toXContent(XContentBuilder builder, IndexMetaData state) throws IOException {
|
|
IndexMetaData.Builder.toXContent(state, builder, formatParams); }
|
|
|
|
@Override
|
|
public IndexMetaData fromXContent(XContentParser parser) throws IOException {
|
|
return IndexMetaData.Builder.fromXContent(parser);
|
|
}
|
|
};
|
|
}
|
|
|
|
private void writeIndex(String reason, IndexMetaData indexMetaData, @Nullable IndexMetaData previousIndexMetaData) throws Exception {
|
|
logger.trace("[{}] writing state, reason [{}]", indexMetaData.index(), reason);
|
|
final boolean deleteOldFiles = previousIndexMetaData != null && previousIndexMetaData.version() != indexMetaData.version();
|
|
final MetaDataStateFormat<IndexMetaData> writer = indexStateFormat(format, formatParams, deleteOldFiles);
|
|
try {
|
|
writer.write(indexMetaData, INDEX_STATE_FILE_PREFIX, indexMetaData.version(),
|
|
nodeEnv.indexPaths(new Index(indexMetaData.index())));
|
|
} catch (Throwable ex) {
|
|
logger.warn("[{}]: failed to write index state", ex, indexMetaData.index());
|
|
throw new IOException("failed to write state for [" + indexMetaData.index() + "]", ex);
|
|
}
|
|
}
|
|
|
|
private void writeGlobalState(String reason, MetaData metaData) throws Exception {
|
|
logger.trace("{} writing state, reason [{}]", GLOBAL_STATE_LOG_TYPE, reason);
|
|
final MetaDataStateFormat<MetaData> writer = globalStateFormat(format, gatewayModeFormatParams, true);
|
|
try {
|
|
writer.write(metaData, GLOBAL_STATE_FILE_PREFIX, metaData.version(), nodeEnv.nodeDataPaths());
|
|
} catch (Throwable ex) {
|
|
logger.warn("{}: failed to write global state", ex, GLOBAL_STATE_LOG_TYPE);
|
|
throw new IOException("failed to write global state", ex);
|
|
}
|
|
}
|
|
|
|
private MetaData loadState() throws Exception {
|
|
MetaData globalMetaData = loadGlobalState();
|
|
MetaData.Builder metaDataBuilder;
|
|
if (globalMetaData != null) {
|
|
metaDataBuilder = MetaData.builder(globalMetaData);
|
|
} else {
|
|
metaDataBuilder = MetaData.builder();
|
|
}
|
|
|
|
final Set<String> indices = nodeEnv.findAllIndices();
|
|
for (String index : indices) {
|
|
IndexMetaData indexMetaData = loadIndexState(index);
|
|
if (indexMetaData == null) {
|
|
logger.debug("[{}] failed to find metadata for existing index location", index);
|
|
} else {
|
|
metaDataBuilder.put(indexMetaData, false);
|
|
}
|
|
}
|
|
return metaDataBuilder.build();
|
|
}
|
|
|
|
@Nullable
|
|
private IndexMetaData loadIndexState(String index) throws IOException {
|
|
return MetaDataStateFormat.loadLatestState(logger, indexStateFormat(format, formatParams, true),
|
|
INDEX_STATE_FILE_PATTERN, "[" + index + "]", nodeEnv.indexPaths(new Index(index)));
|
|
}
|
|
|
|
private MetaData loadGlobalState() throws IOException {
|
|
return MetaDataStateFormat.loadLatestState(logger, globalStateFormat(format, gatewayModeFormatParams, true), GLOBAL_STATE_FILE_PATTERN, GLOBAL_STATE_LOG_TYPE, nodeEnv.nodeDataPaths());
|
|
}
|
|
|
|
|
|
/**
|
|
* Throws an IAE if a pre 0.19 state is detected
|
|
*/
|
|
private void ensureNoPre019State() throws Exception {
|
|
for (Path dataLocation : nodeEnv.nodeDataPaths()) {
|
|
final Path stateLocation = dataLocation.resolve(MetaDataStateFormat.STATE_DIR_NAME);
|
|
if (!Files.exists(stateLocation)) {
|
|
continue;
|
|
}
|
|
try (DirectoryStream<Path> stream = Files.newDirectoryStream(stateLocation)) {
|
|
for (Path stateFile : stream) {
|
|
if (logger.isTraceEnabled()) {
|
|
logger.trace("[upgrade]: processing [" + stateFile.getFileName() + "]");
|
|
}
|
|
final String name = stateFile.getFileName().toString();
|
|
if (name.startsWith("metadata-")) {
|
|
throw new ElasticsearchIllegalStateException("Detected pre 0.19 metadata file please upgrade to a version before "
|
|
+ Version.CURRENT.minimumCompatibilityVersion()
|
|
+ " first to upgrade state structures - metadata found: [" + stateFile.getParent().toAbsolutePath());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Elasticsearch 2.0 deprecated custom routing hash functions. So what we do here is that for old indices, we
|
|
* move this old & deprecated node setting to an index setting so that we can keep things backward compatible.
|
|
*/
|
|
private void pre20Upgrade() throws Exception {
|
|
final Class<? extends HashFunction> pre20HashFunction;
|
|
final String pre20HashFunctionName = settings.get(DEPRECATED_SETTING_ROUTING_HASH_FUNCTION, null);
|
|
final boolean hasCustomPre20HashFunction = pre20HashFunctionName != null;
|
|
// the hash function package has changed we replace the two hash functions if their fully qualified name is used.
|
|
if (hasCustomPre20HashFunction) {
|
|
switch (pre20HashFunctionName) {
|
|
case "org.elasticsearch.cluster.routing.operation.hash.simple.SimpleHashFunction":
|
|
pre20HashFunction = SimpleHashFunction.class;
|
|
break;
|
|
case "org.elasticsearch.cluster.routing.operation.hash.djb.DjbHashFunction":
|
|
pre20HashFunction = DjbHashFunction.class;
|
|
break;
|
|
default:
|
|
pre20HashFunction = settings.getAsClass(DEPRECATED_SETTING_ROUTING_HASH_FUNCTION, DjbHashFunction.class, "org.elasticsearch.cluster.routing.", "HashFunction");
|
|
}
|
|
} else {
|
|
pre20HashFunction = DjbHashFunction.class;
|
|
}
|
|
final Boolean pre20UseType = settings.getAsBoolean(DEPRECATED_SETTING_ROUTING_USE_TYPE, null);
|
|
MetaData metaData = loadMetaState();
|
|
for (IndexMetaData indexMetaData : metaData) {
|
|
if (indexMetaData.settings().get(IndexMetaData.SETTING_LEGACY_ROUTING_HASH_FUNCTION) == null
|
|
&& indexMetaData.getCreationVersion().before(Version.V_2_0_0)) {
|
|
// these settings need an upgrade
|
|
Settings indexSettings = ImmutableSettings.builder().put(indexMetaData.settings())
|
|
.put(IndexMetaData.SETTING_LEGACY_ROUTING_HASH_FUNCTION, pre20HashFunction)
|
|
.put(IndexMetaData.SETTING_LEGACY_ROUTING_USE_TYPE, pre20UseType == null ? false : pre20UseType)
|
|
.build();
|
|
IndexMetaData newMetaData = IndexMetaData.builder(indexMetaData)
|
|
.version(indexMetaData.version())
|
|
.settings(indexSettings)
|
|
.build();
|
|
writeIndex("upgrade", newMetaData, null);
|
|
} else if (indexMetaData.getCreationVersion().onOrAfter(Version.V_2_0_0)) {
|
|
if (indexMetaData.getSettings().get(IndexMetaData.SETTING_LEGACY_ROUTING_HASH_FUNCTION) != null
|
|
|| indexMetaData.getSettings().get(IndexMetaData.SETTING_LEGACY_ROUTING_USE_TYPE) != null) {
|
|
throw new ElasticsearchIllegalStateException("Indices created on or after 2.0 should NOT contain [" + IndexMetaData.SETTING_LEGACY_ROUTING_HASH_FUNCTION
|
|
+ "] + or [" + IndexMetaData.SETTING_LEGACY_ROUTING_USE_TYPE + "] in their index settings");
|
|
}
|
|
}
|
|
}
|
|
if (hasCustomPre20HashFunction|| pre20UseType != null) {
|
|
logger.warn("Settings [{}] and [{}] are deprecated. Index settings from your old indices have been updated to record the fact that they "
|
|
+ "used some custom routing logic, you can now remove these settings from your `elasticsearch.yml` file", DEPRECATED_SETTING_ROUTING_HASH_FUNCTION, DEPRECATED_SETTING_ROUTING_USE_TYPE);
|
|
}
|
|
}
|
|
|
|
class RemoveDanglingIndex implements Runnable {
|
|
|
|
private final IndexMetaData metaData;
|
|
|
|
RemoveDanglingIndex(IndexMetaData metaData) {
|
|
this.metaData = metaData;
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
synchronized (danglingMutex) {
|
|
DanglingIndex remove = danglingIndices.remove(metaData.index());
|
|
// no longer there...
|
|
if (remove == null) {
|
|
return;
|
|
}
|
|
logger.warn("[{}] deleting dangling index", metaData.index());
|
|
try {
|
|
indicesService.deleteIndexStore("deleting dangling index", metaData);
|
|
} catch (Exception ex) {
|
|
logger.debug("failed to delete dangling index", ex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static class DanglingIndex {
|
|
public final String index;
|
|
public final ScheduledFuture future;
|
|
|
|
DanglingIndex(String index, ScheduledFuture future) {
|
|
this.index = index;
|
|
this.future = future;
|
|
}
|
|
}
|
|
|
|
}
|