summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHåkon Hallingstad <hakon@yahooinc.com>2024-05-21 13:49:43 +0200
committerHåkon Hallingstad <hakon@yahooinc.com>2024-05-21 13:49:43 +0200
commitf74add7754f01e98e4966d31219c05fb799c1147 (patch)
tree0573238a4941ac7722c40b7ccea0178b172975cb
parent2b08f380d52c7c5b5e5678f5f582fb93647c2529 (diff)
Demonstrate type-safe setting of flag dimensions
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java140
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flag.java52
2 files changed, 100 insertions, 92 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
index c8d3574182a..1c285270cb1 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
@@ -215,50 +215,50 @@ public class ModelContextImpl implements ModelContext {
private final Architecture adminClusterArchitecture;
public FeatureFlags(FlagSource source, ApplicationId appId, Version version) {
- this.defaultTermwiseLimit = flagValue(source, appId, version, Flags.DEFAULT_TERM_WISE_LIMIT);
- this.feedSequencer = flagValue(source, appId, version, Flags.FEED_SEQUENCER_TYPE);
- this.responseSequencer = flagValue(source, appId, version, Flags.RESPONSE_SEQUENCER_TYPE);
- this.numResponseThreads = flagValue(source, appId, version, Flags.RESPONSE_NUM_THREADS);
- this.useAsyncMessageHandlingOnSchedule = flagValue(source, appId, version, Flags.USE_ASYNC_MESSAGE_HANDLING_ON_SCHEDULE);
- this.feedConcurrency = flagValue(source, appId, version, Flags.FEED_CONCURRENCY);
- this.feedNiceness = flagValue(source, appId, version, Flags.FEED_NICENESS);
- this.mbus_network_threads = flagValue(source, appId, version, Flags.MBUS_NUM_NETWORK_THREADS);
- this.allowedAthenzProxyIdentities = flagValue(source, appId, version, Flags.ALLOWED_ATHENZ_PROXY_IDENTITIES);
- this.maxActivationInhibitedOutOfSyncGroups = flagValue(source, appId, version, Flags.MAX_ACTIVATION_INHIBITED_OUT_OF_SYNC_GROUPS);
- this.jvmOmitStackTraceInFastThrow = type -> flagValue(source, appId, version, type, PermanentFlags.JVM_OMIT_STACK_TRACE_IN_FAST_THROW);
- this.resourceLimitDisk = flagValue(source, appId, version, PermanentFlags.RESOURCE_LIMIT_DISK);
- this.resourceLimitMemory = flagValue(source, appId, version, PermanentFlags.RESOURCE_LIMIT_MEMORY);
- this.minNodeRatioPerGroup = flagValue(source, appId, version, Flags.MIN_NODE_RATIO_PER_GROUP);
- this.containerDumpHeapOnShutdownTimeout = flagValue(source, appId, version, Flags.CONTAINER_DUMP_HEAP_ON_SHUTDOWN_TIMEOUT);
- this.loadCodeAsHugePages = flagValue(source, appId, version, Flags.LOAD_CODE_AS_HUGEPAGES);
- this.containerShutdownTimeout = flagValue(source, appId, version, Flags.CONTAINER_SHUTDOWN_TIMEOUT);
- this.maxUnCommittedMemory = flagValue(source, appId, version, Flags.MAX_UNCOMMITTED_MEMORY);
- this.forwardIssuesAsErrors = flagValue(source, appId, version, PermanentFlags.FORWARD_ISSUES_AS_ERRORS);
- this.useV8GeoPositions = flagValue(source, appId, version, Flags.USE_V8_GEO_POSITIONS);
- this.maxCompactBuffers = flagValue(source, appId, version, Flags.MAX_COMPACT_BUFFERS);
- this.ignoredHttpUserAgents = flagValue(source, appId, version, PermanentFlags.IGNORED_HTTP_USER_AGENTS);
- this.enableProxyProtocolMixedMode = flagValue(source, appId, version, Flags.ENABLE_PROXY_PROTOCOL_MIXED_MODE);
- this.sharedStringRepoNoReclaim = flagValue(source, appId, version, Flags.SHARED_STRING_REPO_NO_RECLAIM);
- this.logFileCompressionAlgorithm = flagValue(source, appId, version, Flags.LOG_FILE_COMPRESSION_ALGORITHM);
- this.mbus_java_num_targets = flagValue(source, appId, version, Flags.MBUS_JAVA_NUM_TARGETS);
- this.mbus_java_events_before_wakeup = flagValue(source, appId, version, Flags.MBUS_JAVA_EVENTS_BEFORE_WAKEUP);
- this.mbus_cpp_num_targets = flagValue(source, appId, version, Flags.MBUS_CPP_NUM_TARGETS);
- this.mbus_cpp_events_before_wakeup = flagValue(source, appId, version, Flags.MBUS_CPP_EVENTS_BEFORE_WAKEUP);
- this.rpc_num_targets = flagValue(source, appId, version, Flags.RPC_NUM_TARGETS);
- this.rpc_events_before_wakeup = flagValue(source, appId, version, Flags.RPC_EVENTS_BEFORE_WAKEUP);
- this.queryDispatchPolicy = flagValue(source, appId, version, Flags.QUERY_DISPATCH_POLICY);
- this.queryDispatchWarmup = flagValue(source, appId, version, PermanentFlags.QUERY_DISPATCH_WARMUP);
- this.heapPercentage = flagValue(source, appId, version, PermanentFlags.HEAP_SIZE_PERCENTAGE);
- this.summaryDecodePolicy = flagValue(source, appId, version, Flags.SUMMARY_DECODE_POLICY);
- this.contentLayerMetadataFeatureLevel = flagValue(source, appId, version, Flags.CONTENT_LAYER_METADATA_FEATURE_LEVEL);
- this.unknownConfigDefinition = flagValue(source, appId, version, Flags.UNKNOWN_CONFIG_DEFINITION);
- this.searchHandlerThreadpool = flagValue(source, appId, version, Flags.SEARCH_HANDLER_THREADPOOL);
- this.alwaysMarkPhraseExpensive = flagValue(source, appId, version, Flags.ALWAYS_MARK_PHRASE_EXPENSIVE);
- this.sortBlueprintsByCost = flagValue(source, appId, version, Flags.SORT_BLUEPRINTS_BY_COST);
- this.persistenceThreadMaxFeedOpBatchSize = flagValue(source, appId, version, Flags.PERSISTENCE_THREAD_MAX_FEED_OP_BATCH_SIZE);
- this.logserverOtelCol = flagValue(source, appId, version, Flags.LOGSERVER_OTELCOL_AGENT);
- this.sharedHosts = flagValue(source, appId, version, PermanentFlags.SHARED_HOST);
- this.adminClusterArchitecture = Architecture.valueOf(flagValue(source, appId, version, PermanentFlags.ADMIN_CLUSTER_NODE_ARCHITECTURE));
+ this.defaultTermwiseLimit = Flags.DEFAULT_TERM_WISE_LIMIT.bindTo(source).with(appId).with(version).value();
+ this.feedSequencer = Flags.FEED_SEQUENCER_TYPE.bindTo(source).with(appId).with(version).value();
+ this.responseSequencer = Flags.RESPONSE_SEQUENCER_TYPE.bindTo(source).with(appId).with(version).value();
+ this.numResponseThreads = Flags.RESPONSE_NUM_THREADS.bindTo(source).with(appId).with(version).value();
+ this.useAsyncMessageHandlingOnSchedule = Flags.USE_ASYNC_MESSAGE_HANDLING_ON_SCHEDULE.bindTo(source).with(appId).with(version).value();
+ this.feedConcurrency = Flags.FEED_CONCURRENCY.bindTo(source).with(appId).with(version).value();
+ this.feedNiceness = Flags.FEED_NICENESS.bindTo(source).with(appId).with(version).value();
+ this.mbus_network_threads = Flags.MBUS_NUM_NETWORK_THREADS.bindTo(source).with(appId).with(version).value();
+ this.allowedAthenzProxyIdentities = Flags.ALLOWED_ATHENZ_PROXY_IDENTITIES.bindTo(source).with(appId).with(version).value();
+ this.maxActivationInhibitedOutOfSyncGroups = Flags.MAX_ACTIVATION_INHIBITED_OUT_OF_SYNC_GROUPS.bindTo(source).with(appId).with(version).value();
+ this.jvmOmitStackTraceInFastThrow = type -> PermanentFlags.JVM_OMIT_STACK_TRACE_IN_FAST_THROW.bindTo(source).with(appId).with(version).with(type).value();
+ this.resourceLimitDisk = PermanentFlags.RESOURCE_LIMIT_DISK.bindTo(source).with(appId).with(version).value();
+ this.resourceLimitMemory = PermanentFlags.RESOURCE_LIMIT_MEMORY.bindTo(source).with(appId).with(version).value();
+ this.minNodeRatioPerGroup = Flags.MIN_NODE_RATIO_PER_GROUP.bindTo(source).with(appId).with(version).value();
+ this.containerDumpHeapOnShutdownTimeout = Flags.CONTAINER_DUMP_HEAP_ON_SHUTDOWN_TIMEOUT.bindTo(source).with(appId).with(version).value();
+ this.loadCodeAsHugePages = Flags.LOAD_CODE_AS_HUGEPAGES.bindTo(source).with(appId).with(version).value();
+ this.containerShutdownTimeout = Flags.CONTAINER_SHUTDOWN_TIMEOUT.bindTo(source).with(appId).with(version).value();
+ this.maxUnCommittedMemory = Flags.MAX_UNCOMMITTED_MEMORY.bindTo(source).with(appId).with(version).value();
+ this.forwardIssuesAsErrors = PermanentFlags.FORWARD_ISSUES_AS_ERRORS.bindTo(source).with(appId).with(version).value();
+ this.useV8GeoPositions = Flags.USE_V8_GEO_POSITIONS.bindTo(source).with(appId).with(version).value();
+ this.maxCompactBuffers = Flags.MAX_COMPACT_BUFFERS.bindTo(source).with(appId).with(version).value();
+ this.ignoredHttpUserAgents = PermanentFlags.IGNORED_HTTP_USER_AGENTS.bindTo(source).with(appId).with(version).value();
+ this.enableProxyProtocolMixedMode = Flags.ENABLE_PROXY_PROTOCOL_MIXED_MODE.bindTo(source).with(appId).with(version).value();
+ this.sharedStringRepoNoReclaim = Flags.SHARED_STRING_REPO_NO_RECLAIM.bindTo(source).with(appId).with(version).value();
+ this.logFileCompressionAlgorithm = Flags.LOG_FILE_COMPRESSION_ALGORITHM.bindTo(source).with(appId).with(version).value();
+ this.mbus_java_num_targets = Flags.MBUS_JAVA_NUM_TARGETS.bindTo(source).with(appId).with(version).value();
+ this.mbus_java_events_before_wakeup = Flags.MBUS_JAVA_EVENTS_BEFORE_WAKEUP.bindTo(source).with(appId).with(version).value();
+ this.mbus_cpp_num_targets = Flags.MBUS_CPP_NUM_TARGETS.bindTo(source).with(appId).with(version).value();
+ this.mbus_cpp_events_before_wakeup = Flags.MBUS_CPP_EVENTS_BEFORE_WAKEUP.bindTo(source).with(appId).with(version).value();
+ this.rpc_num_targets = Flags.RPC_NUM_TARGETS.bindTo(source).with(appId).with(version).value();
+ this.rpc_events_before_wakeup = Flags.RPC_EVENTS_BEFORE_WAKEUP.bindTo(source).with(appId).with(version).value();
+ this.queryDispatchPolicy = Flags.QUERY_DISPATCH_POLICY.bindTo(source).with(appId).with(version).value();
+ this.queryDispatchWarmup = PermanentFlags.QUERY_DISPATCH_WARMUP.bindTo(source).with(appId).with(version).value();
+ this.heapPercentage = PermanentFlags.HEAP_SIZE_PERCENTAGE.bindTo(source).with(appId).with(version).value();
+ this.summaryDecodePolicy = Flags.SUMMARY_DECODE_POLICY.bindTo(source).with(appId).with(version).value();
+ this.contentLayerMetadataFeatureLevel = Flags.CONTENT_LAYER_METADATA_FEATURE_LEVEL.bindTo(source).with(appId).with(version).value();
+ this.unknownConfigDefinition = Flags.UNKNOWN_CONFIG_DEFINITION.bindTo(source).with(appId).with(version).value();
+ this.searchHandlerThreadpool = Flags.SEARCH_HANDLER_THREADPOOL.bindTo(source).with(appId).with(version).value();
+ this.alwaysMarkPhraseExpensive = Flags.ALWAYS_MARK_PHRASE_EXPENSIVE.bindTo(source).with(appId).with(version).value();
+ this.sortBlueprintsByCost = Flags.SORT_BLUEPRINTS_BY_COST.bindTo(source).with(appId).with(version).value();
+ this.persistenceThreadMaxFeedOpBatchSize = Flags.PERSISTENCE_THREAD_MAX_FEED_OP_BATCH_SIZE.bindTo(source).with(appId).with(version).value();
+ this.logserverOtelCol = Flags.LOGSERVER_OTELCOL_AGENT.bindTo(source).with(appId).with(version).value();
+ this.sharedHosts = PermanentFlags.SHARED_HOST.bindTo(source).with( appId).with(version).value();
+ this.adminClusterArchitecture = Architecture.valueOf(PermanentFlags.ADMIN_CLUSTER_NODE_ARCHITECTURE.bindTo(source).with(appId).with(version).value());
}
@Override public int heapSizePercentage() { return heapPercentage; }
@@ -314,41 +314,6 @@ public class ModelContextImpl implements ModelContext {
@Override public SharedHosts sharedHosts() { return sharedHosts; }
@Override public Architecture adminClusterArchitecture() { return adminClusterArchitecture; }
- private static <V> V flagValue(FlagSource source, ApplicationId appId, Version vespaVersion, UnboundFlag<? extends V, ?, ?> flag) {
- return flag.bindTo(source)
- .with(Dimension.INSTANCE_ID, appId.serializedForm())
- .with(Dimension.APPLICATION, appId.toSerializedFormWithoutInstance())
- .with(Dimension.VESPA_VERSION, vespaVersion.toFullString())
- .with(Dimension.TENANT_ID, appId.tenant().value())
- .boxedValue();
- }
-
- private static <V> V flagValue(FlagSource source,
- ApplicationId appId,
- Version vespaVersion,
- ClusterSpec.Type clusterType,
- UnboundFlag<? extends V, ?, ?> flag) {
- return flag.bindTo(source)
- .with(Dimension.INSTANCE_ID, appId.serializedForm())
- .with(Dimension.APPLICATION, appId.toSerializedFormWithoutInstance())
- .with(Dimension.CLUSTER_TYPE, clusterType.name())
- .with(Dimension.VESPA_VERSION, vespaVersion.toFullString())
- .boxedValue();
- }
-
- private static <V> V flagValue(FlagSource source,
- ApplicationId appId,
- Version vespaVersion,
- ClusterSpec.Id clusterId,
- UnboundFlag<? extends V, ?, ?> flag) {
- return flag.bindTo(source)
- .with(Dimension.INSTANCE_ID, appId.serializedForm())
- .with(Dimension.APPLICATION, appId.toSerializedFormWithoutInstance())
- .with(Dimension.CLUSTER_ID, clusterId.value())
- .with(Dimension.VESPA_VERSION, vespaVersion.toFullString())
- .boxedValue();
- }
-
private String translateJvmOmitStackTraceInFastThrowToString(Predicate<ClusterSpec.Type> function,
ClusterSpec.Type clusterType) {
return function.test(clusterType) ? "" : "-XX:-OmitStackTraceInFastThrow";
@@ -422,14 +387,14 @@ public class ModelContextImpl implements ModelContext {
this.jvmGCOptionsFlag = PermanentFlags.JVM_GC_OPTIONS.bindTo(flagSource)
.with(Dimension.INSTANCE_ID, applicationId.serializedForm())
.with(Dimension.APPLICATION, applicationId.toSerializedFormWithoutInstance());
- this.allowDisableMtls = flagValue(flagSource, applicationId, PermanentFlags.ALLOW_DISABLE_MTLS);
+ this.allowDisableMtls = PermanentFlags.ALLOW_DISABLE_MTLS.bindTo(flagSource).with(applicationId).value();
this.operatorCertificates = operatorCertificates;
- this.tlsCiphersOverride = flagValue(flagSource, applicationId, PermanentFlags.TLS_CIPHERS_OVERRIDE);
+ this.tlsCiphersOverride = PermanentFlags.TLS_CIPHERS_OVERRIDE.bindTo(flagSource).with(applicationId).value();
this.zoneDnsSuffixes = configserverConfig.zoneDnsSuffixes();
- this.environmentVariables = flagValue(flagSource, applicationId, PermanentFlags.ENVIRONMENT_VARIABLES);
+ this.environmentVariables = PermanentFlags.ENVIRONMENT_VARIABLES.bindTo(flagSource).with(applicationId).value();
this.cloudAccount = cloudAccount;
- this.allowUserFilters = flagValue(flagSource, applicationId, PermanentFlags.ALLOW_USER_FILTERS);
- this.endpointConnectionTtl = Duration.ofSeconds(flagValue(flagSource, applicationId, PermanentFlags.ENDPOINT_CONNECTION_TTL));
+ this.allowUserFilters = PermanentFlags.ALLOW_USER_FILTERS.bindTo(flagSource).with(applicationId).value();
+ this.endpointConnectionTtl = Duration.ofSeconds(PermanentFlags.ENDPOINT_CONNECTION_TTL.bindTo(flagSource).with(applicationId).value());
this.dataplaneTokens = dataplaneTokens;
}
@@ -529,11 +494,4 @@ public class ModelContextImpl implements ModelContext {
@Override public Duration endpointConnectionTtl() { return endpointConnectionTtl; }
}
-
- private static <V> V flagValue(FlagSource source, ApplicationId appId, UnboundFlag<? extends V, ?, ?> flag) {
- return flag.bindTo(source)
- .with(Dimension.INSTANCE_ID, appId.serializedForm())
- .with(Dimension.APPLICATION, appId.toSerializedFormWithoutInstance())
- .boxedValue();
- }
}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flag.java b/flags/src/main/java/com/yahoo/vespa/flags/Flag.java
index 7ca5066969f..e4a8a1cd439 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flag.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flag.java
@@ -1,6 +1,19 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.flags;
+import com.yahoo.component.Version;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.CloudName;
+import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.Environment;
+import com.yahoo.config.provision.NodeResources.Architecture;
+import com.yahoo.config.provision.NodeType;
+import com.yahoo.config.provision.SystemName;
+import com.yahoo.config.provision.TenantName;
+import com.yahoo.config.provision.Zone;
+import com.yahoo.config.provision.zone.ZoneApi;
+import com.yahoo.config.provision.zone.ZoneId;
+
import java.util.Optional;
/**
@@ -10,7 +23,7 @@ import java.util.Optional;
* @param <F> The concrete subclass type of the flag
* @author hakonhall
*/
-public interface Flag<T, F> {
+public interface Flag<T, F extends Flag<T, F>> {
/** The flag ID. */
FlagId id();
@@ -28,6 +41,43 @@ public interface Flag<T, F> {
return dimensionValue.map(value -> with(dimension, value)).orElse(self());
}
+ /** Sets the tenant, application, and instance dimensions. */
+ default F with(ApplicationId applicationId) {
+ return with(Dimension.TENANT_ID, applicationId.tenant().value())
+ .with(Dimension.APPLICATION, applicationId.toSerializedFormWithoutInstance())
+ .with(Dimension.INSTANCE_ID, applicationId.serializedForm());
+ }
+
+ /** architecture MUST NOT be 'any'. */
+ default F with(Architecture architecture) { return with(Dimension.ARCHITECTURE, architecture.name()); }
+ default F with(CloudName cloud) { return with(Dimension.CLOUD, cloud.value()); }
+ default F with(ClusterSpec.Id clusterId) { return with(Dimension.CLUSTER_ID, clusterId.value()); }
+ default F with(ClusterSpec.Type clusterType) { return with(Dimension.CLUSTER_TYPE, clusterType.name()); }
+ default F with(Environment environment) { return with(Dimension.ENVIRONMENT, environment.value()); }
+ default F with(NodeType nodeType) { return with(Dimension.NODE_TYPE, nodeType.name()); }
+ default F with(SystemName systemName) { return with(Dimension.SYSTEM, systemName.value()); }
+ default F with(TenantName tenantName) { return with(Dimension.TENANT_ID, tenantName.value()); }
+ default F with(Version vespaVersion) { return with(Dimension.VESPA_VERSION, vespaVersion.toFullString()); }
+ default F with(ZoneId zoneId) { return with(Dimension.ZONE_ID, zoneId.value()); }
+ default F with(Zone zone) { return with(Dimension.ZONE_ID, zone.systemLocalValue()); }
+ default F with(ZoneApi zoneApi) { return with(zoneApi.getVirtualId()); }
+
+ /** Sets the tenant, application, and instance dimensions. */
+ default F withApplicationId(Optional<ApplicationId> applicationId) { return applicationId.map(this::with).orElse(self()); }
+ /** architecture MUST NOT be 'any'. */
+ default F withArchitecture(Optional<Architecture> architecture) { return architecture.map(this::with).orElse(self()); }
+ default F withCloudName(Optional<CloudName> cloud) { return cloud.map(this::with).orElse(self()); }
+ default F withClusterId(Optional<ClusterSpec.Id> clusterId) { return clusterId.map(this::with).orElse(self()); }
+ default F withClusterType(Optional<ClusterSpec.Type> clusterType) { return clusterType.map(this::with).orElse(self()); }
+ default F withEnvironment(Optional<Environment> environment) { return environment.map(this::with).orElse(self()); }
+ default F withNodeType(Optional<NodeType> nodeType) { return nodeType.map(this::with).orElse(self()); }
+ default F withSystemName(Optional<SystemName> systemName) { return systemName.map(this::with).orElse(self()); }
+ default F withTenantName(Optional<TenantName> tenantName) { return tenantName.map(this::with).orElse(self()); }
+ default F withVersion(Optional<Version> vespaVersion) { return vespaVersion.map(this::with).orElse(self()); }
+ default F withZoneId(Optional<ZoneId> zoneId) { return zoneId.map(this::with).orElse(self()); }
+ default F withZone(Optional<Zone> zone) { return zone.map(this::with).orElse(self()); }
+ default F withZoneApi(Optional<ZoneApi> zoneApi) { return zoneApi.map(this::with).orElse(self()); }
+
/** Returns the value, boxed if the flag wraps a primitive type. */
T boxedValue();
}