From d7e524fcf9835f4b31369dd2cd0ef8da4994c9a3 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Fri, 26 Apr 2024 08:08:24 -0700 Subject: [PATCH] Make auto heap configuration configurable in server cli subclasses (#107919) This commit makes auto heap configuration extendible so that serverless can tweak the configuration based on project settings. --- .../server/cli/JvmOptionsParser.java | 20 +- .../server/cli/MachineDependentHeap.java | 228 ++++++++---------- .../elasticsearch/server/cli/ServerCli.java | 2 +- .../server/cli/MachineDependentHeapTests.java | 97 +++----- .../server/cli/NodeRoleParserTests.java | 103 -------- .../windows/service/WindowsServiceDaemon.java | 3 +- 6 files changed, 143 insertions(+), 310 deletions(-) delete mode 100644 distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/NodeRoleParserTests.java diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java index 35f3f62122f0c..0bfa0f211807d 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java @@ -72,13 +72,18 @@ SortedMap invalidLines() { * @param args the start-up arguments * @param processInfo information about the CLI process. * @param tmpDir the directory that should be passed to {@code -Djava.io.tmpdir} + * @param machineDependentHeap the heap configurator to use * @return the list of options to put on the Java command line * @throws InterruptedException if the java subprocess is interrupted * @throws IOException if there is a problem reading any of the files * @throws UserException if there is a problem parsing the `jvm.options` file or `jvm.options.d` files */ - public static List determineJvmOptions(ServerArgs args, ProcessInfo processInfo, Path tmpDir) throws InterruptedException, - IOException, UserException { + public static List determineJvmOptions( + ServerArgs args, + ProcessInfo processInfo, + Path tmpDir, + MachineDependentHeap machineDependentHeap + ) throws InterruptedException, IOException, UserException { final JvmOptionsParser parser = new JvmOptionsParser(); final Map substitutions = new HashMap<>(); @@ -89,7 +94,7 @@ public static List determineJvmOptions(ServerArgs args, ProcessInfo proc try { return Collections.unmodifiableList( - parser.jvmOptions(args, args.configDir(), tmpDir, envOptions, substitutions, processInfo.sysprops()) + parser.jvmOptions(args, args.configDir(), tmpDir, envOptions, substitutions, processInfo.sysprops(), machineDependentHeap) ); } catch (final JvmOptionsFileParserException e) { final String errorMessage = String.format( @@ -125,7 +130,8 @@ private List jvmOptions( Path tmpDir, final String esJavaOpts, final Map substitutions, - final Map cliSysprops + final Map cliSysprops, + final MachineDependentHeap machineDependentHeap ) throws InterruptedException, IOException, JvmOptionsFileParserException, UserException { final List jvmOptions = readJvmOptionsFiles(config); @@ -135,10 +141,8 @@ private List jvmOptions( } final List substitutedJvmOptions = substitutePlaceholders(jvmOptions, Collections.unmodifiableMap(substitutions)); - final MachineDependentHeap machineDependentHeap = new MachineDependentHeap( - new OverridableSystemMemoryInfo(substitutedJvmOptions, new DefaultSystemMemoryInfo()) - ); - substitutedJvmOptions.addAll(machineDependentHeap.determineHeapSettings(config, substitutedJvmOptions)); + final SystemMemoryInfo memoryInfo = new OverridableSystemMemoryInfo(substitutedJvmOptions, new DefaultSystemMemoryInfo()); + substitutedJvmOptions.addAll(machineDependentHeap.determineHeapSettings(args.nodeSettings(), memoryInfo, substitutedJvmOptions)); final List ergonomicJvmOptions = JvmErgonomics.choose(substitutedJvmOptions, args.nodeSettings()); final List systemJvmOptions = SystemJvmOptions.systemJvmOptions(args.nodeSettings(), cliSysprops); diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/MachineDependentHeap.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/MachineDependentHeap.java index 87c4883ca3073..b7ef9e46a758d 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/MachineDependentHeap.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/MachineDependentHeap.java @@ -8,24 +8,22 @@ package org.elasticsearch.server.cli; -import org.elasticsearch.common.ParsingException; +import org.elasticsearch.cluster.node.DiscoveryNodeRole; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.xcontent.XContentParserConfiguration; -import org.elasticsearch.xcontent.yaml.YamlXContent; +import org.elasticsearch.node.NodeRoleSettings; import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.function.Function; import static java.lang.Math.max; import static java.lang.Math.min; +import static org.elasticsearch.cluster.node.DiscoveryNodeRole.MASTER_ROLE; +import static org.elasticsearch.cluster.node.DiscoveryNodeRole.ML_ROLE; +import static org.elasticsearch.cluster.node.DiscoveryNodeRole.REMOTE_CLUSTER_CLIENT_ROLE; import static org.elasticsearch.server.cli.JvmOption.isInitialHeapSpecified; import static org.elasticsearch.server.cli.JvmOption.isMaxHeapSpecified; import static org.elasticsearch.server.cli.JvmOption.isMinHeapSpecified; @@ -33,28 +31,26 @@ /** * Determines optimal default heap settings based on available system memory and assigned node roles. */ -public final class MachineDependentHeap { +public class MachineDependentHeap { private static final long GB = 1024L * 1024L * 1024L; // 1GB private static final long MAX_HEAP_SIZE = GB * 31; // 31GB private static final long MIN_HEAP_SIZE = 1024 * 1024 * 128; // 128MB - private static final int DEFAULT_HEAP_SIZE_MB = 1024; - private static final String ELASTICSEARCH_YML = "elasticsearch.yml"; - private final SystemMemoryInfo systemMemoryInfo; - - public MachineDependentHeap(SystemMemoryInfo systemMemoryInfo) { - this.systemMemoryInfo = systemMemoryInfo; - } + public MachineDependentHeap() {} /** * Calculate heap options. * - * @param configDir path to config directory + * @param nodeSettings the settings for the node * @param userDefinedJvmOptions JVM arguments provided by the user * @return final heap options, or an empty collection if user provided heap options are to be used * @throws IOException if unable to load elasticsearch.yml */ - public List determineHeapSettings(Path configDir, List userDefinedJvmOptions) throws IOException, InterruptedException { + public final List determineHeapSettings( + Settings nodeSettings, + SystemMemoryInfo systemMemoryInfo, + List userDefinedJvmOptions + ) throws IOException, InterruptedException { // TODO: this could be more efficient, to only parse final options once final Map finalJvmOptions = JvmOption.findFinalOptions(userDefinedJvmOptions); if (isMaxHeapSpecified(finalJvmOptions) || isMinHeapSpecified(finalJvmOptions) || isInitialHeapSpecified(finalJvmOptions)) { @@ -62,139 +58,103 @@ public List determineHeapSettings(Path configDir, List userDefin return Collections.emptyList(); } - Path config = configDir.resolve(ELASTICSEARCH_YML); - try (InputStream in = Files.newInputStream(config)) { - return determineHeapSettings(in); - } - } - - List determineHeapSettings(InputStream config) { - MachineNodeRole nodeRole = NodeRoleParser.parse(config); + List roles = NodeRoleSettings.NODE_ROLES_SETTING.get(nodeSettings); long availableSystemMemory = systemMemoryInfo.availableSystemMemory(); - return options(nodeRole.heap(availableSystemMemory)); + MachineNodeRole nodeRole = mapNodeRole(roles); + return options(getHeapSizeMb(nodeSettings, nodeRole, availableSystemMemory)); } - private static List options(int heapSize) { - return List.of("-Xms" + heapSize + "m", "-Xmx" + heapSize + "m"); - } - - /** - * Parses role information from elasticsearch.yml and determines machine node role. - */ - static class NodeRoleParser { - - @SuppressWarnings("unchecked") - public static MachineNodeRole parse(InputStream config) { - final Settings settings; - try (var parser = YamlXContent.yamlXContent.createParser(XContentParserConfiguration.EMPTY, config)) { - if (parser.currentToken() == null && parser.nextToken() == null) { - settings = null; + protected int getHeapSizeMb(Settings nodeSettings, MachineNodeRole role, long availableMemory) { + return switch (role) { + /* + * Master-only node. + * + *

Heap is computed as 60% of total system memory up to a maximum of 31 gigabytes. + */ + case MASTER_ONLY -> mb(min((long) (availableMemory * .6), MAX_HEAP_SIZE)); + /* + * Machine learning only node. + * + *

Heap is computed as: + *

    + *
  • 40% of total system memory when total system memory 16 gigabytes or less.
  • + *
  • 40% of the first 16 gigabytes plus 10% of memory above that when total system memory is more than 16 gigabytes.
  • + *
  • The absolute maximum heap size is 31 gigabytes.
  • + *
+ * + * In all cases the result is rounded down to the next whole multiple of 4 megabytes. + * The reason for doing this is that Java will round requested heap sizes to a multiple + * of 4 megabytes (certainly versions 11 to 18 do this), so by doing this ourselves we + * are more likely to actually get the amount we request. This is worthwhile for ML where + * the ML autoscaling code needs to be able to calculate the JVM size for different sizes + * of ML node, and if Java is also rounding then this causes a discrepancy. It's possible + * that a future version of Java could round to an even bigger number of megabytes, which + * would cause a discrepancy for people using that version of Java. But there's no harm + * in a bit of extra rounding here - it can only reduce discrepancies. + * + * If this formula is changed then corresponding changes must be made to the {@code NativeMemoryCalculator} and + * {@code MlAutoscalingDeciderServiceTests} classes in the ML plugin code. Failure to keep the logic synchronized + * could result in repeated autoscaling up and down. + */ + case ML_ONLY -> { + if (availableMemory <= (GB * 16)) { + yield mb((long) (availableMemory * .4), 4); } else { - settings = Settings.fromXContent(parser); + yield mb((long) min((GB * 16) * .4 + (availableMemory - GB * 16) * .1, MAX_HEAP_SIZE), 4); } - } catch (IOException | ParsingException ex) { - // Strangely formatted config, so just return defaults and let startup settings validation catch the problem - return MachineNodeRole.UNKNOWN; } - - if (settings != null && settings.isEmpty() == false) { - List roles = settings.getAsList("node.roles"); - - if (roles.isEmpty()) { - // If roles are missing or empty (coordinating node) assume defaults and consider this a data node - return MachineNodeRole.DATA; - } else if (containsOnly(roles, "master")) { - return MachineNodeRole.MASTER_ONLY; - } else if (roles.contains("ml") && containsOnly(roles, "ml", "remote_cluster_client")) { - return MachineNodeRole.ML_ONLY; + /* + * Data node. Essentially any node that isn't a master or ML only node. + * + *

Heap is computed as: + *

    + *
  • 40% of total system memory when less than 1 gigabyte with a minimum of 128 megabytes.
  • + *
  • 50% of total system memory when greater than 1 gigabyte up to a maximum of 31 gigabytes.
  • + *
+ */ + case DATA -> { + if (availableMemory < GB) { + yield mb(max((long) (availableMemory * .4), MIN_HEAP_SIZE)); } else { - return MachineNodeRole.DATA; + yield mb(min((long) (availableMemory * .5), MAX_HEAP_SIZE)); } - } else { // if the config is completely empty, then assume defaults and consider this a data node - return MachineNodeRole.DATA; } - } - - @SuppressWarnings("unchecked") - private static boolean containsOnly(Collection collection, T... items) { - return Arrays.asList(items).containsAll(collection); - } + }; } - enum MachineNodeRole { - /** - * Master-only node. - * - *

Heap is computed as 60% of total system memory up to a maximum of 31 gigabytes. - */ - MASTER_ONLY(m -> mb(min((long) (m * .6), MAX_HEAP_SIZE))), - - /** - * Machine learning only node. - * - *

Heap is computed as: - *

    - *
  • 40% of total system memory when total system memory 16 gigabytes or less.
  • - *
  • 40% of the first 16 gigabytes plus 10% of memory above that when total system memory is more than 16 gigabytes.
  • - *
  • The absolute maximum heap size is 31 gigabytes.
  • - *
- * - * In all cases the result is rounded down to the next whole multiple of 4 megabytes. - * The reason for doing this is that Java will round requested heap sizes to a multiple - * of 4 megabytes (certainly versions 11 to 18 do this), so by doing this ourselves we - * are more likely to actually get the amount we request. This is worthwhile for ML where - * the ML autoscaling code needs to be able to calculate the JVM size for different sizes - * of ML node, and if Java is also rounding then this causes a discrepancy. It's possible - * that a future version of Java could round to an even bigger number of megabytes, which - * would cause a discrepancy for people using that version of Java. But there's no harm - * in a bit of extra rounding here - it can only reduce discrepancies. - * - * If this formula is changed then corresponding changes must be made to the {@code NativeMemoryCalculator} and - * {@code MlAutoscalingDeciderServiceTests} classes in the ML plugin code. Failure to keep the logic synchronized - * could result in repeated autoscaling up and down. - */ - ML_ONLY(m -> mb(m <= (GB * 16) ? (long) (m * .4) : (long) min((GB * 16) * .4 + (m - GB * 16) * .1, MAX_HEAP_SIZE), 4)), - - /** - * Data node. Essentially any node that isn't a master or ML only node. - * - *

Heap is computed as: - *

    - *
  • 40% of total system memory when less than 1 gigabyte with a minimum of 128 megabytes.
  • - *
  • 50% of total system memory when greater than 1 gigabyte up to a maximum of 31 gigabytes.
  • - *
- */ - DATA(m -> mb(m < GB ? max((long) (m * .4), MIN_HEAP_SIZE) : min((long) (m * .5), MAX_HEAP_SIZE))), - - /** - * Unknown role node. - * - *

Hard-code heap to a default of 1 gigabyte. - */ - UNKNOWN(m -> DEFAULT_HEAP_SIZE_MB); + protected static int mb(long bytes) { + return (int) (bytes / (1024 * 1024)); + } - private final Function formula; + protected static int mb(long bytes, int toLowerMultipleOfMb) { + return toLowerMultipleOfMb * (int) (bytes / (1024 * 1024 * toLowerMultipleOfMb)); + } - MachineNodeRole(Function formula) { - this.formula = formula; + private static MachineNodeRole mapNodeRole(List roles) { + if (roles.isEmpty()) { + // If roles are missing or empty (coordinating node) assume defaults and consider this a data node + return MachineNodeRole.DATA; + } else if (containsOnly(roles, MASTER_ROLE)) { + return MachineNodeRole.MASTER_ONLY; + } else if (roles.contains(ML_ROLE) && containsOnly(roles, ML_ROLE, REMOTE_CLUSTER_CLIENT_ROLE)) { + return MachineNodeRole.ML_ONLY; + } else { + return MachineNodeRole.DATA; } + } - /** - * Determine the appropriate heap size for the given role and available system memory. - * - * @param systemMemory total available system memory in bytes - * @return recommended heap size in megabytes - */ - public int heap(long systemMemory) { - return formula.apply(systemMemory); - } + @SuppressWarnings("unchecked") + private static boolean containsOnly(Collection collection, T... items) { + return Arrays.asList(items).containsAll(collection); + } - private static int mb(long bytes) { - return (int) (bytes / (1024 * 1024)); - } + private static List options(int heapSize) { + return List.of("-Xms" + heapSize + "m", "-Xmx" + heapSize + "m"); + } - private static int mb(long bytes, int toLowerMultipleOfMb) { - return toLowerMultipleOfMb * (int) (bytes / (1024 * 1024 * toLowerMultipleOfMb)); - } + protected enum MachineNodeRole { + MASTER_ONLY, + ML_ONLY, + DATA; } } diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 6dbff2fbfff9c..0505ab86127cf 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -250,7 +250,7 @@ protected Command loadTool(String toolname, String libs) { // protected to allow tests to override protected ServerProcess startServer(Terminal terminal, ProcessInfo processInfo, ServerArgs args) throws Exception { var tempDir = ServerProcessUtils.setupTempDir(processInfo); - var jvmOptions = JvmOptionsParser.determineJvmOptions(args, processInfo, tempDir); + var jvmOptions = JvmOptionsParser.determineJvmOptions(args, processInfo, tempDir, new MachineDependentHeap()); var serverProcessBuilder = new ServerProcessBuilder().withTerminal(terminal) .withProcessInfo(processInfo) .withServerArgs(args) diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/MachineDependentHeapTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/MachineDependentHeapTests.java index 5b30c2246c624..0774773cbfa0b 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/MachineDependentHeapTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/MachineDependentHeapTests.java @@ -8,16 +8,13 @@ package org.elasticsearch.server.cli; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase.WithoutSecurityManager; +import org.hamcrest.Matcher; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.UncheckedIOException; import java.net.URISyntaxException; import java.net.URL; -import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; @@ -25,95 +22,69 @@ import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; -import static org.junit.Assert.assertThat; // TODO: rework these tests to mock jvm option finder so they can run with security manager, no forking needed @WithoutSecurityManager public class MachineDependentHeapTests extends ESTestCase { public void testDefaultHeapSize() throws Exception { - MachineDependentHeap heap = new MachineDependentHeap(systemMemoryInGigabytes(8)); - List options = heap.determineHeapSettings(configPath(), Collections.emptyList()); + MachineDependentHeap heap = new MachineDependentHeap(); + List options = heap.determineHeapSettings(Settings.EMPTY, systemMemoryInGigabytes(8), Collections.emptyList()); assertThat(options, containsInAnyOrder("-Xmx4096m", "-Xms4096m")); } public void testUserPassedHeapArgs() throws Exception { - MachineDependentHeap heap = new MachineDependentHeap(systemMemoryInGigabytes(8)); - List options = heap.determineHeapSettings(configPath(), List.of("-Xmx4g")); + var systemMemoryInfo = systemMemoryInGigabytes(8); + MachineDependentHeap heap = new MachineDependentHeap(); + List options = heap.determineHeapSettings(Settings.EMPTY, systemMemoryInfo, List.of("-Xmx4g")); assertThat(options, empty()); - options = heap.determineHeapSettings(configPath(), List.of("-Xms4g")); + options = heap.determineHeapSettings(Settings.EMPTY, systemMemoryInfo, List.of("-Xms4g")); assertThat(options, empty()); } // Explicitly test odd heap sizes // See: https://github.com/elastic/elasticsearch/issues/86431 public void testOddUserPassedHeapArgs() throws Exception { - MachineDependentHeap heap = new MachineDependentHeap(systemMemoryInGigabytes(8)); - List options = heap.determineHeapSettings(configPath(), List.of("-Xmx409m")); + var systemMemoryInfo = systemMemoryInGigabytes(8); + MachineDependentHeap heap = new MachineDependentHeap(); + List options = heap.determineHeapSettings(Settings.EMPTY, systemMemoryInfo, List.of("-Xmx409m")); assertThat(options, empty()); - options = heap.determineHeapSettings(configPath(), List.of("-Xms409m")); + options = heap.determineHeapSettings(Settings.EMPTY, systemMemoryInfo, List.of("-Xms409m")); assertThat(options, empty()); } - public void testMasterOnlyOptions() { - List options = calculateHeap(16, "master"); - assertThat(options, containsInAnyOrder("-Xmx9830m", "-Xms9830m")); - - options = calculateHeap(64, "master"); - assertThat(options, containsInAnyOrder("-Xmx31744m", "-Xms31744m")); + public void testMasterOnlyOptions() throws Exception { + assertHeapOptions(16, containsInAnyOrder("-Xmx9830m", "-Xms9830m"), "master"); + assertHeapOptions(64, containsInAnyOrder("-Xmx31744m", "-Xms31744m"), "master"); } - public void testMlOnlyOptions() { - List options = calculateHeap(1, "ml"); - assertThat(options, containsInAnyOrder("-Xmx408m", "-Xms408m")); - - options = calculateHeap(4, "ml"); - assertThat(options, containsInAnyOrder("-Xmx1636m", "-Xms1636m")); - - options = calculateHeap(32, "ml"); - assertThat(options, containsInAnyOrder("-Xmx8192m", "-Xms8192m")); - - options = calculateHeap(64, "ml"); - assertThat(options, containsInAnyOrder("-Xmx11468m", "-Xms11468m")); - + public void testMlOnlyOptions() throws Exception { + assertHeapOptions(1, containsInAnyOrder("-Xmx408m", "-Xms408m"), "ml"); + assertHeapOptions(4, containsInAnyOrder("-Xmx1636m", "-Xms1636m"), "ml"); + assertHeapOptions(32, containsInAnyOrder("-Xmx8192m", "-Xms8192m"), "ml"); + assertHeapOptions(64, containsInAnyOrder("-Xmx11468m", "-Xms11468m"), "ml"); // We'd never see a node this big in Cloud, but this assertion proves that the 31GB absolute maximum // eventually kicks in (because 0.4 * 16 + 0.1 * (263 - 16) > 31) - options = calculateHeap(263, "ml"); - assertThat(options, containsInAnyOrder("-Xmx31744m", "-Xms31744m")); - - } - - public void testDataNodeOptions() { - List options = calculateHeap(1, "data"); - assertThat(options, containsInAnyOrder("-Xmx512m", "-Xms512m")); - - options = calculateHeap(8, "data"); - assertThat(options, containsInAnyOrder("-Xmx4096m", "-Xms4096m")); - - options = calculateHeap(64, "data"); - assertThat(options, containsInAnyOrder("-Xmx31744m", "-Xms31744m")); - - options = calculateHeap(0.5, "data"); - assertThat(options, containsInAnyOrder("-Xmx204m", "-Xms204m")); - - options = calculateHeap(0.2, "data"); - assertThat(options, containsInAnyOrder("-Xmx128m", "-Xms128m")); + assertHeapOptions(263, containsInAnyOrder("-Xmx31744m", "-Xms31744m"), "ml"); } - private static List calculateHeap(double memoryInGigabytes, String... roles) { - MachineDependentHeap machineDependentHeap = new MachineDependentHeap(systemMemoryInGigabytes(memoryInGigabytes)); - String configYaml = "node.roles: [" + String.join(",", roles) + "]"; - return calculateHeap(machineDependentHeap, configYaml); + public void testDataNodeOptions() throws Exception { + assertHeapOptions(1, containsInAnyOrder("-Xmx512m", "-Xms512m"), "data"); + assertHeapOptions(8, containsInAnyOrder("-Xmx4096m", "-Xms4096m"), "data"); + assertHeapOptions(64, containsInAnyOrder("-Xmx31744m", "-Xms31744m"), "data"); + assertHeapOptions(0.5, containsInAnyOrder("-Xmx204m", "-Xms204m"), "data"); + assertHeapOptions(0.2, containsInAnyOrder("-Xmx128m", "-Xms128m"), "data"); } - private static List calculateHeap(MachineDependentHeap machineDependentHeap, String configYaml) { - try (InputStream in = new ByteArrayInputStream(configYaml.getBytes(StandardCharsets.UTF_8))) { - return machineDependentHeap.determineHeapSettings(in); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + private void assertHeapOptions(double memoryInGigabytes, Matcher> optionsMatcher, String... roles) + throws Exception { + SystemMemoryInfo systemMemoryInfo = systemMemoryInGigabytes(memoryInGigabytes); + MachineDependentHeap machineDependentHeap = new MachineDependentHeap(); + Settings nodeSettings = Settings.builder().putList("node.roles", roles).build(); + List heapOptions = machineDependentHeap.determineHeapSettings(nodeSettings, systemMemoryInfo, Collections.emptyList()); + assertThat(heapOptions, optionsMatcher); } private static SystemMemoryInfo systemMemoryInGigabytes(double gigabytes) { diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/NodeRoleParserTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/NodeRoleParserTests.java deleted file mode 100644 index 4d501c1116732..0000000000000 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/NodeRoleParserTests.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -package org.elasticsearch.server.cli; - -import org.elasticsearch.test.ESTestCase; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.function.Consumer; - -import static org.elasticsearch.server.cli.MachineDependentHeap.MachineNodeRole.DATA; -import static org.elasticsearch.server.cli.MachineDependentHeap.MachineNodeRole.MASTER_ONLY; -import static org.elasticsearch.server.cli.MachineDependentHeap.MachineNodeRole.ML_ONLY; -import static org.elasticsearch.server.cli.MachineDependentHeap.MachineNodeRole.UNKNOWN; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; - -public class NodeRoleParserTests extends ESTestCase { - - public void testMasterOnlyNode() throws IOException { - MachineDependentHeap.MachineNodeRole nodeRole = parseConfig(sb -> sb.append("node.roles: [master]")); - assertThat(nodeRole, equalTo(MASTER_ONLY)); - - nodeRole = parseConfig(sb -> sb.append("node.roles: [master, some_other_role]")); - assertThat(nodeRole, not(equalTo(MASTER_ONLY))); - } - - public void testMlOnlyNode() throws IOException { - MachineDependentHeap.MachineNodeRole nodeRole = parseConfig(sb -> sb.append("node.roles: [ml]")); - assertThat(nodeRole, equalTo(ML_ONLY)); - - nodeRole = parseConfig(sb -> sb.append("node.roles: [ml, remote_cluster_client]")); - assertThat(nodeRole, equalTo(ML_ONLY)); - - nodeRole = parseConfig(sb -> sb.append("node.roles: [remote_cluster_client, ml]")); - assertThat(nodeRole, equalTo(ML_ONLY)); - - nodeRole = parseConfig(sb -> sb.append("node.roles: [remote_cluster_client]")); - assertThat(nodeRole, not(equalTo(ML_ONLY))); - - nodeRole = parseConfig(sb -> sb.append("node.roles: [ml, some_other_role]")); - assertThat(nodeRole, not(equalTo(ML_ONLY))); - } - - public void testDataNode() throws IOException { - MachineDependentHeap.MachineNodeRole nodeRole = parseConfig(sb -> {}); - assertThat(nodeRole, equalTo(DATA)); - - nodeRole = parseConfig(sb -> sb.append("node.roles: []")); - assertThat(nodeRole, equalTo(DATA)); - - nodeRole = parseConfig(sb -> sb.append("node.roles: [some_unknown_role]")); - assertThat(nodeRole, equalTo(DATA)); - - nodeRole = parseConfig(sb -> sb.append("node.roles: [master, ingest]")); - assertThat(nodeRole, equalTo(DATA)); - - nodeRole = parseConfig(sb -> sb.append("node.roles: [ml, master]")); - assertThat(nodeRole, equalTo(DATA)); - } - - public void testYamlSyntax() throws IOException { - MachineDependentHeap.MachineNodeRole nodeRole = parseConfig(sb -> sb.append(""" - node: - roles: - - master""")); - assertThat(nodeRole, equalTo(MASTER_ONLY)); - - nodeRole = parseConfig(sb -> sb.append(""" - node: - roles: [ml]""")); - assertThat(nodeRole, equalTo(ML_ONLY)); - } - - public void testInvalidYaml() throws IOException { - MachineDependentHeap.MachineNodeRole nodeRole = parseConfig(sb -> sb.append("notyaml")); - assertThat(nodeRole, equalTo(UNKNOWN)); - } - - public void testInvalidRoleSyntax() throws IOException { - MachineDependentHeap.MachineNodeRole nodeRole = parseConfig(sb -> sb.append("node.roles: foo")); - // roles we don't know about are considered data, but will fail validation when ES starts up - assertThat(nodeRole, equalTo(DATA)); - } - - private static MachineDependentHeap.MachineNodeRole parseConfig(Consumer action) throws IOException { - StringBuilder sb = new StringBuilder(); - action.accept(sb); - - try (InputStream config = new ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8))) { - return MachineDependentHeap.NodeRoleParser.parse(config); - } - } -} diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceDaemon.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceDaemon.java index 2c42dcf5cb2f5..22474e63ab0df 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceDaemon.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceDaemon.java @@ -18,6 +18,7 @@ import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.env.Environment; import org.elasticsearch.server.cli.JvmOptionsParser; +import org.elasticsearch.server.cli.MachineDependentHeap; import org.elasticsearch.server.cli.ServerProcess; import org.elasticsearch.server.cli.ServerProcessBuilder; import org.elasticsearch.server.cli.ServerProcessUtils; @@ -42,7 +43,7 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce try (var loadedSecrets = KeyStoreWrapper.bootstrap(env.configFile(), () -> new SecureString(new char[0]))) { var args = new ServerArgs(false, true, null, loadedSecrets, env.settings(), env.configFile(), env.logsFile()); var tempDir = ServerProcessUtils.setupTempDir(processInfo); - var jvmOptions = JvmOptionsParser.determineJvmOptions(args, processInfo, tempDir); + var jvmOptions = JvmOptionsParser.determineJvmOptions(args, processInfo, tempDir, new MachineDependentHeap()); var serverProcessBuilder = new ServerProcessBuilder().withTerminal(terminal) .withProcessInfo(processInfo) .withServerArgs(args)