Skip to content

Commit 1f0673c

Browse files
authored
Default max local storage nodes to one
This commit defaults the max local storage nodes to one. The motivation for this change is that a default value greather than one is dangerous as users sometimes end up unknowingly starting a second node and start thinking that they have encountered data loss. Relates #19964
1 parent c9722c4 commit 1f0673c

File tree

14 files changed

+65
-23
lines changed

14 files changed

+65
-23
lines changed

buildSrc/src/main/groovy/org/elasticsearch/gradle/test/ClusterFormationTasks.groovy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ class ClusterFormationTasks {
261261
'node.attr.testattr' : 'test',
262262
'repositories.url.allowed_urls': 'http://snapshot.test*'
263263
]
264+
esConfig['node.max_local_storage_nodes'] = node.config.numNodes
264265
esConfig['http.port'] = node.config.httpPort
265266
esConfig['transport.tcp.port'] = node.config.transportPort
266267
esConfig.putAll(node.config.settings)

core/src/main/java/org/elasticsearch/env/NodeEnvironment.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
import java.util.HashMap;
6969
import java.util.HashSet;
7070
import java.util.List;
71+
import java.util.Locale;
7172
import java.util.Map;
7273
import java.util.Random;
7374
import java.util.Set;
@@ -151,7 +152,7 @@ public String toString() {
151152
/**
152153
* Maximum number of data nodes that should run in an environment.
153154
*/
154-
public static final Setting<Integer> MAX_LOCAL_STORAGE_NODES_SETTING = Setting.intSetting("node.max_local_storage_nodes", 50, 1,
155+
public static final Setting<Integer> MAX_LOCAL_STORAGE_NODES_SETTING = Setting.intSetting("node.max_local_storage_nodes", 1, 1,
155156
Property.NodeScope);
156157

157158
/**
@@ -244,8 +245,15 @@ public NodeEnvironment(Settings settings, Environment environment) throws IOExce
244245
}
245246

246247
if (locks[0] == null) {
247-
throw new IllegalStateException("Failed to obtain node lock, is the following location writable?: "
248-
+ Arrays.toString(environment.dataWithClusterFiles()), lastException);
248+
final String message = String.format(
249+
Locale.ROOT,
250+
"failed to obtain node locks, tried [%s] with lock id%s;" +
251+
" maybe these locations are not writable or multiple nodes were started without increasing [%s] (was [%d])?",
252+
Arrays.toString(environment.dataWithClusterFiles()),
253+
maxLocalStorageNodes == 1 ? " [0]" : "s [0--" + (maxLocalStorageNodes - 1) + "]",
254+
MAX_LOCAL_STORAGE_NODES_SETTING.getKey(),
255+
maxLocalStorageNodes);
256+
throw new IllegalStateException(message, lastException);
249257
}
250258
this.nodeMetaData = loadOrCreateNodeMetaData(settings, startupTraceLogger, nodePaths);
251259
this.logger = Loggers.getLogger(getClass(), Node.addNodeNameIfNeeded(settings, this.nodeMetaData.nodeId()));

core/src/main/java/org/elasticsearch/tribe/TribeService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ public static Settings processSettings(Settings settings) {
117117
sb.put(Node.NODE_MASTER_SETTING.getKey(), false);
118118
sb.put(Node.NODE_DATA_SETTING.getKey(), false);
119119
sb.put(Node.NODE_INGEST_SETTING.getKey(), false);
120+
if (!NodeEnvironment.MAX_LOCAL_STORAGE_NODES_SETTING.exists(settings)) {
121+
sb.put(NodeEnvironment.MAX_LOCAL_STORAGE_NODES_SETTING.getKey(), nodesSettings.size());
122+
}
120123
sb.put(DiscoveryModule.DISCOVERY_TYPE_SETTING.getKey(), "local"); // a tribe node should not use zen discovery
121124
// nothing is going to be discovered, since no master will be elected
122125
sb.put(DiscoverySettings.INITIAL_STATE_TIMEOUT_SETTING.getKey(), 0);

core/src/test/java/org/elasticsearch/cluster/ClusterInfoServiceIT.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.elasticsearch.common.inject.Inject;
3939
import org.elasticsearch.common.settings.Settings;
4040
import org.elasticsearch.common.unit.TimeValue;
41+
import org.elasticsearch.env.NodeEnvironment;
4142
import org.elasticsearch.index.IndexService;
4243
import org.elasticsearch.index.shard.IndexShard;
4344
import org.elasticsearch.index.store.Store;
@@ -118,6 +119,7 @@ public void blockActions(String... actions) {
118119
protected Settings nodeSettings(int nodeOrdinal) {
119120
return Settings.builder()
120121
// manual collection or upon cluster forming.
122+
.put(NodeEnvironment.MAX_LOCAL_STORAGE_NODES_SETTING.getKey(), 2)
121123
.put(InternalClusterInfoService.INTERNAL_CLUSTER_INFO_TIMEOUT_SETTING.getKey(), "1s")
122124
.build();
123125
}

core/src/test/java/org/elasticsearch/discovery/DiscoveryWithServiceDisruptionsIT.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.elasticsearch.discovery.zen.ping.ZenPingService;
5656
import org.elasticsearch.discovery.zen.ping.unicast.UnicastZenPing;
5757
import org.elasticsearch.discovery.zen.publish.PublishClusterStateAction;
58+
import org.elasticsearch.env.NodeEnvironment;
5859
import org.elasticsearch.indices.store.IndicesStoreIntegrationIT;
5960
import org.elasticsearch.plugins.Plugin;
6061
import org.elasticsearch.test.ESIntegTestCase;
@@ -207,6 +208,7 @@ private void configureUnicastCluster(
207208
// TODO: Rarely use default settings form some of these
208209
Settings nodeSettings = Settings.builder()
209210
.put(settings)
211+
.put(NodeEnvironment.MAX_LOCAL_STORAGE_NODES_SETTING.getKey(), 4)
210212
.put(ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING.getKey(), minimumMasterNode)
211213
.build();
212214

core/src/test/java/org/elasticsearch/env/NodeEnvironmentTests.java

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,18 +74,14 @@ public void testNodeLockSillySettings() {
7474
}
7575

7676
public void testNodeLockSingleEnvironment() throws IOException {
77-
final Settings settings = buildEnvSettings(Settings.builder()
78-
.put(NodeEnvironment.MAX_LOCAL_STORAGE_NODES_SETTING.getKey(), 1).build());
77+
final Settings settings = buildEnvSettings(Settings.builder().put("node.max_local_storage_nodes", 1).build());
7978
NodeEnvironment env = newNodeEnvironment(settings);
8079
List<String> dataPaths = Environment.PATH_DATA_SETTING.get(settings);
8180

82-
try {
83-
// Reuse the same location and attempt to lock again
84-
new NodeEnvironment(settings, new Environment(settings));
85-
fail("env has already locked all the data directories it is allowed");
86-
} catch (IllegalStateException ex) {
87-
assertThat(ex.getMessage(), containsString("Failed to obtain node lock"));
88-
}
81+
// Reuse the same location and attempt to lock again
82+
IllegalStateException ex =
83+
expectThrows(IllegalStateException.class, () -> new NodeEnvironment(settings, new Environment(settings)));
84+
assertThat(ex.getMessage(), containsString("failed to obtain node lock"));
8985

9086
// Close the environment that holds the lock and make sure we can get the lock after release
9187
env.close();
@@ -121,7 +117,7 @@ public void testSegmentInfosTracing() {
121117
}
122118

123119
public void testNodeLockMultipleEnvironment() throws IOException {
124-
final Settings settings = buildEnvSettings(Settings.EMPTY);
120+
final Settings settings = buildEnvSettings(Settings.builder().put("node.max_local_storage_nodes", 2).build());
125121
final NodeEnvironment first = newNodeEnvironment(settings);
126122
List<String> dataPaths = Environment.PATH_DATA_SETTING.get(settings);
127123
NodeEnvironment second = new NodeEnvironment(settings, new Environment(settings));

core/src/test/java/org/elasticsearch/indices/memory/breaker/CircuitBreakerNoopIT.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.elasticsearch.action.index.IndexRequestBuilder;
2323
import org.elasticsearch.client.Client;
2424
import org.elasticsearch.common.settings.Settings;
25+
import org.elasticsearch.env.NodeEnvironment;
2526
import org.elasticsearch.indices.breaker.HierarchyCircuitBreakerService;
2627
import org.elasticsearch.search.sort.SortOrder;
2728
import org.elasticsearch.test.ESIntegTestCase;
@@ -40,6 +41,7 @@ public class CircuitBreakerNoopIT extends ESIntegTestCase {
4041
@Override
4142
protected Settings nodeSettings(int nodeOrdinal) {
4243
return Settings.builder()
44+
.put(NodeEnvironment.MAX_LOCAL_STORAGE_NODES_SETTING.getKey(), 2)
4345
.put(HierarchyCircuitBreakerService.FIELDDATA_CIRCUIT_BREAKER_TYPE_SETTING.getKey(), "noop")
4446
// This is set low, because if the "noop" is not a noop, it will break
4547
.put(HierarchyCircuitBreakerService.FIELDDATA_CIRCUIT_BREAKER_LIMIT_SETTING.getKey(), "10b")

core/src/test/java/org/elasticsearch/tribe/TribeIT.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.elasticsearch.discovery.DiscoveryModule;
4040
import org.elasticsearch.discovery.MasterNotDiscoveredException;
4141
import org.elasticsearch.discovery.zen.ping.unicast.UnicastZenPing;
42+
import org.elasticsearch.env.NodeEnvironment;
4243
import org.elasticsearch.node.Node;
4344
import org.elasticsearch.test.ESIntegTestCase;
4445
import org.elasticsearch.test.InternalTestCluster;
@@ -128,6 +129,7 @@ private void setupTribeNode(Settings settings) {
128129
tribe1Defaults.putArray("tribe.t2." + UnicastZenPing.DISCOVERY_ZEN_PING_UNICAST_HOSTS_SETTING.getKey(), getUnicastHosts(cluster2.client()));
129130

130131
Settings merged = Settings.builder()
132+
.put(internalCluster().getDefaultSettings())
131133
.put("tribe.t1.cluster.name", internalCluster().getClusterName())
132134
.put("tribe.t2.cluster.name", cluster2.getClusterName())
133135
.put("tribe.t1.transport.type", "local")
@@ -142,7 +144,6 @@ private void setupTribeNode(Settings settings) {
142144

143145
.put(tribe1Defaults.build())
144146
.put(tribe2Defaults.build())
145-
.put(internalCluster().getDefaultSettings())
146147
.put("node.name", "tribe_node") // make sure we can identify threads from this node
147148
.build();
148149

docs/reference/migration/migrate_5_0/settings.asciidoc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,3 +310,14 @@ The unit 'w' representing weeks is no longer supported.
310310

311311
Fractional time values (e.g., 0.5s) are no longer supported. For example, this means when setting
312312
timeouts "0.5s" will be rejected and should instead be input as "500ms".
313+
314+
==== Node max local storage nodes
315+
316+
Previous versions of Elasticsearch defaulted to allowing multiple nodes to share the same data
317+
directory (up to 50). This can be confusing where users accidentally startup multiple nodes and end
318+
up thinking that they've lost data because the second node will start with an empty data directory.
319+
While the default of allowing multiple nodes is friendly to playing with forming a small cluster on
320+
a laptop, and end-users do sometimes run multiple nodes on the same host, this tends to be the
321+
exception. Keeping with Elasticsearch's continual movement towards safer out-of-the-box defaults,
322+
and optimizing for the norm instead of the exception, the default for
323+
`node.max_local_storage_nodes` is now one.

qa/evil-tests/src/test/java/org/elasticsearch/tribe/TribeUnitTests.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ public static void createTribes() {
6060
.put(NetworkModule.HTTP_ENABLED.getKey(), false)
6161
.put("transport.type", "local")
6262
.put(DiscoveryModule.DISCOVERY_TYPE_SETTING.getKey(), "local")
63-
.put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()).build();
63+
.put(Environment.PATH_HOME_SETTING.getKey(), createTempDir())
64+
.put(NodeEnvironment.MAX_LOCAL_STORAGE_NODES_SETTING.getKey(), 2)
65+
.build();
6466

6567
tribe1 = new TribeClientNode(
6668
Settings.builder()

0 commit comments

Comments
 (0)