| // Copyright 2015 The Bazel Authors. All rights reserved. |
| // |
| // Licensed 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 com.google.devtools.build.lib.worker; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Maps; |
| import com.google.devtools.build.lib.util.RamResourceConverter; |
| import com.google.devtools.build.lib.util.ResourceConverter; |
| import com.google.devtools.common.options.Converter; |
| import com.google.devtools.common.options.Converters; |
| import com.google.devtools.common.options.Converters.CommaSeparatedOptionSetConverter; |
| import com.google.devtools.common.options.Converters.DurationConverter; |
| import com.google.devtools.common.options.Option; |
| import com.google.devtools.common.options.OptionDocumentationCategory; |
| import com.google.devtools.common.options.OptionEffectTag; |
| import com.google.devtools.common.options.Options; |
| import com.google.devtools.common.options.OptionsBase; |
| import com.google.devtools.common.options.OptionsParsingException; |
| import java.time.Duration; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| |
| /** Options related to worker processes. */ |
| public class WorkerOptions extends OptionsBase { |
| public static final WorkerOptions DEFAULTS = Options.getDefaults(WorkerOptions.class); |
| |
| /** |
| * Defines a resource converter for named values in the form [name=]value, where the value is |
| * {@link ResourceConverter.FLAG_SYNTAX}. If no name is provided (used when setting a default), |
| * the empty string is used as the key. The default value for unspecified mnemonics is defined in |
| * {@link WorkerPoolImpl.createWorkerPools}. "auto" currently returns the default. |
| */ |
| public static class MultiResourceConverter extends Converter.Contextless<Entry<String, Integer>> { |
| |
| static final ResourceConverter valueConverter = |
| new ResourceConverter(() -> 0, 0, Integer.MAX_VALUE); |
| |
| @Override |
| public Map.Entry<String, Integer> convert(String input) throws OptionsParsingException { |
| // TODO(steinman): Make auto value return a reasonable multiplier of host capacity. |
| if (input == null || input.equals("null") || input.equals("auto")) { |
| return Maps.immutableEntry(null, null); |
| } |
| int pos = input.indexOf('='); |
| if (pos < 0) { |
| return Maps.immutableEntry("", valueConverter.convert(input, /*conversionContext=*/ null)); |
| } |
| String name = input.substring(0, pos); |
| String value = input.substring(pos + 1); |
| if (value.equals("auto")) { |
| return Maps.immutableEntry(name, null); |
| } |
| |
| return Maps.immutableEntry(name, valueConverter.convert(value, /*conversionContext=*/ null)); |
| } |
| |
| @Override |
| public String getTypeDescription() { |
| return "[name=]value, where value is " + ResourceConverter.FLAG_SYNTAX; |
| } |
| } |
| |
| @Option( |
| name = "worker_max_instances", |
| converter = MultiResourceConverter.class, |
| defaultValue = "null", |
| documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, |
| effectTags = {OptionEffectTag.EXECUTION, OptionEffectTag.HOST_MACHINE_RESOURCE_OPTIMIZATIONS}, |
| help = |
| "How many instances of each kind of persistent worker may be " |
| + "launched if you use the 'worker' strategy. May be specified as [name=value] to " |
| + "give a different value per mnemonic. The limit is based on worker keys, which are " |
| + "differentiated based on mnemonic, but also on startup flags and environment, so " |
| + "there can in some cases be more workers per mnemonic than this flag specifies. " |
| + "Takes " |
| + ResourceConverter.FLAG_SYNTAX |
| + ". 'auto' calculates a reasonable default based on machine capacity. " |
| + "\"=value\" sets a default for unspecified mnemonics.", |
| allowMultiple = true) |
| public List<Map.Entry<String, Integer>> workerMaxInstances; |
| |
| @Option( |
| name = "worker_max_multiplex_instances", |
| oldName = "experimental_worker_max_multiplex_instances", |
| converter = MultiResourceConverter.class, |
| defaultValue = "null", |
| documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, |
| effectTags = {OptionEffectTag.EXECUTION, OptionEffectTag.HOST_MACHINE_RESOURCE_OPTIMIZATIONS}, |
| help = |
| "How many WorkRequests a multiplex worker process may receive in parallel if you use the" |
| + " 'worker' strategy with --worker_multiplex. May be specified as " |
| + "[name=value] to give a different value per mnemonic. The limit is based on worker " |
| + "keys, which are differentiated based on mnemonic, but also on startup flags and " |
| + "environment, so there can in some cases be more workers per mnemonic than this " |
| + "flag specifies. Takes " |
| + ResourceConverter.FLAG_SYNTAX |
| + ". 'auto' calculates a reasonable default based on machine capacity. " |
| + "\"=value\" sets a default for unspecified mnemonics.", |
| allowMultiple = true) |
| public List<Map.Entry<String, Integer>> workerMaxMultiplexInstances; |
| |
| @Option( |
| name = "worker_quit_after_build", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, |
| effectTags = {OptionEffectTag.EXECUTION, OptionEffectTag.HOST_MACHINE_RESOURCE_OPTIMIZATIONS}, |
| help = "If enabled, all workers quit after a build is done.") |
| public boolean workerQuitAfterBuild; |
| |
| @Option( |
| name = "worker_verbose", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "If enabled, prints verbose messages when workers are started, shutdown, ...") |
| public boolean workerVerbose; |
| |
| @Option( |
| name = "worker_extra_flag", |
| converter = Converters.AssignmentConverter.class, |
| defaultValue = "null", |
| documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, |
| effectTags = {OptionEffectTag.EXECUTION, OptionEffectTag.HOST_MACHINE_RESOURCE_OPTIMIZATIONS}, |
| help = |
| "Extra command-flags that will be passed to worker processes in addition to " |
| + "--persistent_worker, keyed by mnemonic (e.g. --worker_extra_flag=Javac=--debug.", |
| allowMultiple = true) |
| public List<Map.Entry<String, String>> workerExtraFlags; |
| |
| @Option( |
| name = "worker_sandboxing", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, |
| effectTags = {OptionEffectTag.EXECUTION}, |
| help = "If enabled, workers will be executed in a sandboxed environment.") |
| public boolean workerSandboxing; |
| |
| @Option( |
| name = "worker_multiplex", |
| oldName = "experimental_worker_multiplex", |
| defaultValue = "true", |
| documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, |
| effectTags = {OptionEffectTag.EXECUTION, OptionEffectTag.HOST_MACHINE_RESOURCE_OPTIMIZATIONS}, |
| help = "If enabled, workers will use multiplexing if they support it. ") |
| public boolean workerMultiplex; |
| |
| @Option( |
| name = "experimental_worker_cancellation", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, |
| effectTags = {OptionEffectTag.EXECUTION}, |
| help = "If enabled, Bazel may send cancellation requests to workers that support them.") |
| public boolean workerCancellation; |
| |
| @Option( |
| name = "experimental_worker_multiplex_sandboxing", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, |
| effectTags = {OptionEffectTag.EXECUTION}, |
| help = |
| "If enabled, multiplex workers will be sandboxed, using a separate sandbox directory" |
| + " per work request. Only workers that have the 'supports-multiplex-sandboxing' " |
| + "execution requirement will be sandboxed.") |
| public boolean multiplexSandboxing; |
| |
| @Option( |
| name = "experimental_worker_strict_flagfiles", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, |
| effectTags = {OptionEffectTag.EXECUTION}, |
| help = |
| "If enabled, actions arguments for workers that do not follow the worker specification" |
| + " will cause an error. Worker arguments must have exactly one @flagfile argument" |
| + " as the last of its list of arguments.") |
| public boolean strictFlagfiles; |
| |
| @Option( |
| name = "experimental_total_worker_memory_limit_mb", |
| converter = RamResourceConverter.class, |
| defaultValue = "0", |
| documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, |
| effectTags = {OptionEffectTag.EXECUTION, OptionEffectTag.HOST_MACHINE_RESOURCE_OPTIMIZATIONS}, |
| help = |
| "If this limit is greater than zero idle workers might be killed if the total memory" |
| + " usage of all workers exceed the limit.") |
| public int totalWorkerMemoryLimitMb; |
| |
| @Option( |
| name = "experimental_worker_sandbox_hardening", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, |
| effectTags = {OptionEffectTag.EXECUTION}, |
| help = "If enabled, workers are run in a hardened sandbox, if the implementation allows it.") |
| public boolean sandboxHardening; |
| |
| @Option( |
| name = "experimental_shrink_worker_pool", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, |
| effectTags = {OptionEffectTag.EXECUTION, OptionEffectTag.HOST_MACHINE_RESOURCE_OPTIMIZATIONS}, |
| help = |
| "If enabled, could shrink worker pool if worker memory pressure is high. This flag works" |
| + " only when flag experimental_total_worker_memory_limit_mb is enabled.") |
| public boolean shrinkWorkerPool; |
| |
| @Option( |
| name = "experimental_worker_metrics_poll_interval", |
| converter = DurationConverter.class, |
| defaultValue = "5s", |
| documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, |
| effectTags = {OptionEffectTag.EXECUTION, OptionEffectTag.HOST_MACHINE_RESOURCE_OPTIMIZATIONS}, |
| help = |
| "The interval between collecting worker metrics and possibly attempting evictions. " |
| + "Cannot effectively be less than 1s for performance reasons.") |
| public Duration workerMetricsPollInterval; |
| |
| @Option( |
| name = "experimental_worker_memory_limit_mb", |
| converter = RamResourceConverter.class, |
| defaultValue = "0", |
| documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, |
| effectTags = {OptionEffectTag.EXECUTION, OptionEffectTag.HOST_MACHINE_RESOURCE_OPTIMIZATIONS}, |
| help = |
| "If this limit is greater than zero, workers might be killed if the memory usage of the " |
| + "worker exceeds the limit. If not used together with dynamic execution and " |
| + "`--experimental_dynamic_ignore_local_signals=9`, this may crash your build.") |
| public int workerMemoryLimitMb; |
| |
| @Option( |
| name = "experimental_worker_allowlist", |
| converter = CommaSeparatedOptionSetConverter.class, |
| defaultValue = "null", |
| documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, |
| effectTags = {OptionEffectTag.EXECUTION, OptionEffectTag.HOST_MACHINE_RESOURCE_OPTIMIZATIONS}, |
| help = |
| "If non-empty, only allow using persistent workers with the given worker key mnemonic.") |
| public ImmutableList<String> allowlist; |
| } |