diff --git a/.buildkite/pipelines/periodic.template.yml b/.buildkite/pipelines/periodic.template.yml
index afde6bdf8e65d..4ebe8460c72d1 100644
--- a/.buildkite/pipelines/periodic.template.yml
+++ b/.buildkite/pipelines/periodic.template.yml
@@ -86,6 +86,7 @@ steps:
ES_RUNTIME_JAVA:
- openjdk21
- openjdk23
+ - openjdk24
GRADLE_TASK:
- checkPart1
- checkPart2
diff --git a/.buildkite/pipelines/periodic.yml b/.buildkite/pipelines/periodic.yml
index d925f7e2bffbf..ff771061950e7 100644
--- a/.buildkite/pipelines/periodic.yml
+++ b/.buildkite/pipelines/periodic.yml
@@ -505,6 +505,7 @@ steps:
ES_RUNTIME_JAVA:
- openjdk21
- openjdk23
+ - openjdk24
GRADLE_TASK:
- checkPart1
- checkPart2
diff --git a/docs/changelog/120957.yaml b/docs/changelog/120957.yaml
new file mode 100644
index 0000000000000..841ef945ce7ef
--- /dev/null
+++ b/docs/changelog/120957.yaml
@@ -0,0 +1,5 @@
+pr: 120957
+summary: Introduce `AllocationBalancingRoundSummaryService`
+area: Allocation
+type: enhancement
+issues: []
diff --git a/docs/internal/Versioning.md b/docs/internal/Versioning.md
index f0f730f618259..474278e873922 100644
--- a/docs/internal/Versioning.md
+++ b/docs/internal/Versioning.md
@@ -35,19 +35,19 @@ Every change to the transport protocol is represented by a new transport version
higher than all previous transport versions, which then becomes the highest version
recognized by that build of Elasticsearch. The version ids are stored
as constants in the `TransportVersions` class.
-Each id has a standard pattern `M_NNN_SS_P`, where:
+Each id has a standard pattern `M_NNN_S_PP`, where:
* `M` is the major version
* `NNN` is an incrementing id
-* `SS` is used in subsidiary repos amending the default transport protocol
-* `P` is used for patches and backports
+* `S` is used in subsidiary repos amending the default transport protocol
+* `PP` is used for patches and backports
When you make a change to the serialization form of any object,
you need to create a new sequential constant in `TransportVersions`,
introduced in the same PR that adds the change, that increments
the `NNN` component from the previous highest version,
with other components set to zero.
-For example, if the previous version number is `8_413_00_1`,
-the next version number should be `8_414_00_0`.
+For example, if the previous version number is `8_413_0_01`,
+the next version number should be `8_414_0_00`.
Once you have defined your constant, you then need to use it
in serialization code. If the transport version is at or above the new id,
@@ -166,7 +166,7 @@ also has that change, and knows about the patch backport ids and what they mean.
Index version is a single incrementing version number for the index data format,
metadata, and associated mappings. It is declared the same way as the
-transport version - with the pattern `M_NNN_SS_P`, for the major version, version id,
+transport version - with the pattern `M_NNN_S_PP`, for the major version, version id,
subsidiary version id, and patch number respectively.
Index version is stored in index metadata when an index is created,
diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java
index 092f5ce8455cb..da2191f601110 100644
--- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java
+++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java
@@ -398,7 +398,9 @@ private ModuleEntitlements computeEntitlements(Class> requestingClass) {
var pluginName = pluginResolver.apply(requestingClass);
if (pluginName != null) {
var pluginEntitlements = pluginsEntitlements.get(pluginName);
- if (pluginEntitlements != null) {
+ if (pluginEntitlements == null) {
+ return ModuleEntitlements.NONE;
+ } else {
final String scopeName;
if (requestingModule.isNamed() == false) {
scopeName = ALL_UNNAMED;
diff --git a/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/PolicyManagerTests.java b/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/PolicyManagerTests.java
index 24be0f6f43a4c..f6dca079f9202 100644
--- a/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/PolicyManagerTests.java
+++ b/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/PolicyManagerTests.java
@@ -271,7 +271,7 @@ public void testAgentsEntitlements() throws IOException, ClassNotFoundException
createEmptyTestServerPolicy(),
List.of(new CreateClassLoaderEntitlement()),
Map.of(),
- c -> "test",
+ c -> c.getPackageName().startsWith(TEST_AGENTS_PACKAGE_NAME) ? null : "test",
TEST_AGENTS_PACKAGE_NAME,
NO_ENTITLEMENTS_MODULE
);
@@ -357,6 +357,22 @@ public void testDuplicateFlagEntitlements() {
);
}
+ /**
+ * If the plugin resolver tells us a class is in a plugin, don't conclude that it's in an agent.
+ */
+ public void testPluginResolverOverridesAgents() {
+ var policyManager = new PolicyManager(
+ createEmptyTestServerPolicy(),
+ List.of(new CreateClassLoaderEntitlement()),
+ Map.of(),
+ c -> "test", // Insist that the class is in a plugin
+ TEST_AGENTS_PACKAGE_NAME,
+ NO_ENTITLEMENTS_MODULE
+ );
+ ModuleEntitlements notAgentsEntitlements = policyManager.getEntitlements(TestAgent.class);
+ assertThat(notAgentsEntitlements.hasEntitlement(CreateClassLoaderEntitlement.class), is(false));
+ }
+
private static Class> makeClassInItsOwnModule() throws IOException, ClassNotFoundException {
final Path home = createTempDir();
Path jar = createMockPluginJar(home);
diff --git a/muted-tests.yml b/muted-tests.yml
index 134e58e6f005d..ccea465bc68f2 100644
--- a/muted-tests.yml
+++ b/muted-tests.yml
@@ -143,9 +143,6 @@ tests:
- class: org.elasticsearch.datastreams.DataStreamsClientYamlTestSuiteIT
method: test {p0=data_stream/120_data_streams_stats/Multiple data stream}
issue: https://github.com/elastic/elasticsearch/issues/118217
-- class: org.elasticsearch.action.search.SearchQueryThenFetchAsyncActionTests
- method: testBottomFieldSort
- issue: https://github.com/elastic/elasticsearch/issues/118214
- class: org.elasticsearch.xpack.searchablesnapshots.RetrySearchIntegTests
method: testSearcherId
issue: https://github.com/elastic/elasticsearch/issues/118374
@@ -335,8 +332,6 @@ tests:
- class: org.elasticsearch.upgrades.VectorSearchIT
method: testBBQVectorSearch {upgradedNodes=0}
issue: https://github.com/elastic/elasticsearch/issues/121253
-- class: org.elasticsearch.lucene.FullClusterRestartLuceneIndexCompatibilityIT
- issue: https://github.com/elastic/elasticsearch/issues/121257
- class: org.elasticsearch.upgrades.VectorSearchIT
method: testBBQVectorSearch {upgradedNodes=1}
issue: https://github.com/elastic/elasticsearch/issues/121271
@@ -392,8 +387,6 @@ tests:
- class: org.elasticsearch.xpack.ml.integration.ClassificationIT
method: testDependentVariableIsAliasToNested
issue: https://github.com/elastic/elasticsearch/issues/121415
-- class: org.elasticsearch.datastreams.TSDBPassthroughIndexingIT
- issue: https://github.com/elastic/elasticsearch/issues/121464
- class: org.elasticsearch.xpack.esql.heap_attack.HeapAttackIT
method: testLookupExplosionBigStringManyMatches
issue: https://github.com/elastic/elasticsearch/issues/121465
@@ -409,6 +402,56 @@ tests:
- class: org.elasticsearch.xpack.esql.heap_attack.HeapAttackIT
method: testLookupExplosionManyMatches
issue: https://github.com/elastic/elasticsearch/issues/121481
+- class: org.elasticsearch.xpack.security.profile.ProfileIntegTests
+ method: testGetUsersWithProfileUid
+ issue: https://github.com/elastic/elasticsearch/issues/121483
+- class: org.elasticsearch.xpack.security.CoreWithSecurityClientYamlTestSuiteIT
+ method: test {yaml=cat.aliases/10_basic/Empty cluster}
+ issue: https://github.com/elastic/elasticsearch/issues/121484
+- class: org.elasticsearch.xpack.transform.checkpoint.TransformCCSCanMatchIT
+ method: testTransformLifecycle_RangeQueryThatMatchesNoShards
+ issue: https://github.com/elastic/elasticsearch/issues/121480
+- class: org.elasticsearch.xpack.esql.action.CrossClusterAsyncQueryIT
+ method: testStopQueryLocal
+ issue: https://github.com/elastic/elasticsearch/issues/121487
+- class: org.elasticsearch.xpack.esql.action.CrossClusterAsyncQueryIT
+ method: testSuccessfulPathways
+ issue: https://github.com/elastic/elasticsearch/issues/121488
+- class: org.elasticsearch.xpack.esql.action.CrossClusterAsyncQueryIT
+ method: testAsyncQueriesWithLimit0
+ issue: https://github.com/elastic/elasticsearch/issues/121489
+- class: org.elasticsearch.xpack.security.profile.ProfileIntegTests
+ method: testSuggestProfilesWithHint
+ issue: https://github.com/elastic/elasticsearch/issues/121116
+- class: org.elasticsearch.xpack.sql.qa.single_node.JdbcDocCsvSpecIT
+ method: test {docs.testFilterToday}
+ issue: https://github.com/elastic/elasticsearch/issues/121474
+- class: org.elasticsearch.xpack.security.profile.ProfileIntegTests
+ method: testSuggestProfileWithData
+ issue: https://github.com/elastic/elasticsearch/issues/121258
+- class: org.elasticsearch.ingest.geoip.FullClusterRestartIT
+ method: testGeoIpSystemFeaturesMigration {cluster=UPGRADED}
+ issue: https://github.com/elastic/elasticsearch/issues/121115
+- class: org.elasticsearch.xpack.core.ilm.SetSingleNodeAllocateStepTests
+ method: testPerformActionSomeShardsOnlyOnNewNodesButNewNodesInvalidAttrs
+ issue: https://github.com/elastic/elasticsearch/issues/121495
+- class: org.elasticsearch.xpack.security.CoreWithSecurityClientYamlTestSuiteIT
+ method: test {yaml=cat.aliases/40_hidden/Test cat aliases output with a visible index with a hidden alias}
+ issue: https://github.com/elastic/elasticsearch/issues/121128
+- class: org.elasticsearch.backwards.MixedClusterClientYamlTestSuiteIT
+ method: test {p0=search.vectors/42_knn_search_int4_flat/Vector similarity with filter only}
+ issue: https://github.com/elastic/elasticsearch/issues/121412
+- class: org.elasticsearch.xpack.inference.common.InferenceServiceNodeLocalRateLimitCalculatorTests
+ issue: https://github.com/elastic/elasticsearch/issues/121294
+- class: org.elasticsearch.xpack.ml.integration.ClassificationIT
+ method: testDependentVariableIsAliasToKeyword
+ issue: https://github.com/elastic/elasticsearch/issues/121492
+- class: org.elasticsearch.xpack.security.CoreWithSecurityClientYamlTestSuiteIT
+ method: test {yaml=cat.aliases/10_basic/Complex alias}
+ issue: https://github.com/elastic/elasticsearch/issues/121513
+- class: org.elasticsearch.xpack.security.CoreWithSecurityClientYamlTestSuiteIT
+ method: test {yaml=snapshot.create/10_basic/Create a snapshot for missing index}
+ issue: https://github.com/elastic/elasticsearch/issues/121536
# Examples:
#
diff --git a/qa/lucene-index-compatibility/src/javaRestTest/java/org/elasticsearch/lucene/FullClusterRestartLuceneIndexCompatibilityIT.java b/qa/lucene-index-compatibility/src/javaRestTest/java/org/elasticsearch/lucene/FullClusterRestartLuceneIndexCompatibilityIT.java
index f37fca16a4b78..501a46deca9d1 100644
--- a/qa/lucene-index-compatibility/src/javaRestTest/java/org/elasticsearch/lucene/FullClusterRestartLuceneIndexCompatibilityIT.java
+++ b/qa/lucene-index-compatibility/src/javaRestTest/java/org/elasticsearch/lucene/FullClusterRestartLuceneIndexCompatibilityIT.java
@@ -11,8 +11,6 @@
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.index.IndexSettings;
-import org.elasticsearch.index.translog.Translog;
import org.elasticsearch.repositories.fs.FsRepository;
import org.elasticsearch.test.cluster.util.Version;
@@ -184,7 +182,6 @@ public void testClosedIndexUpgrade() throws Exception {
Settings.builder()
.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, randomInt(2))
- .put(IndexSettings.INDEX_TRANSLOG_DURABILITY_SETTING.getKey(), randomFrom(Translog.Durability.values()))
.build()
);
indexDocs(index, numDocs);
diff --git a/qa/lucene-index-compatibility/src/javaRestTest/java/org/elasticsearch/lucene/RollingUpgradeLuceneIndexCompatibilityTestCase.java b/qa/lucene-index-compatibility/src/javaRestTest/java/org/elasticsearch/lucene/RollingUpgradeLuceneIndexCompatibilityTestCase.java
index 12374cf623a8c..7b9e2d64bbae4 100644
--- a/qa/lucene-index-compatibility/src/javaRestTest/java/org/elasticsearch/lucene/RollingUpgradeLuceneIndexCompatibilityTestCase.java
+++ b/qa/lucene-index-compatibility/src/javaRestTest/java/org/elasticsearch/lucene/RollingUpgradeLuceneIndexCompatibilityTestCase.java
@@ -13,8 +13,6 @@
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.index.IndexSettings;
-import org.elasticsearch.index.translog.Translog;
import org.elasticsearch.repositories.fs.FsRepository;
import org.elasticsearch.test.cluster.util.Version;
@@ -189,11 +187,7 @@ public void testClosedIndexUpgrade() throws Exception {
createIndex(
client(),
index,
- Settings.builder()
- .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
- .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
- .put(IndexSettings.INDEX_TRANSLOG_DURABILITY_SETTING.getKey(), randomFrom(Translog.Durability.values()))
- .build()
+ Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0).build()
);
indexDocs(index, numDocs);
return;
diff --git a/server/src/main/java/org/elasticsearch/ReleaseVersions.java b/server/src/main/java/org/elasticsearch/ReleaseVersions.java
index 22cd18c7b4ac3..5e6986a5bf924 100644
--- a/server/src/main/java/org/elasticsearch/ReleaseVersions.java
+++ b/server/src/main/java/org/elasticsearch/ReleaseVersions.java
@@ -78,10 +78,10 @@ public static IntFunction generateVersionsLookup(Class> versionContain
// replace all version lists with the smallest & greatest versions
versions.replaceAll((k, v) -> {
if (v.size() == 1) {
- return List.of(v.get(0));
+ return List.of(v.getFirst());
} else {
v.sort(Comparator.naturalOrder());
- return List.of(v.get(0), v.get(v.size() - 1));
+ return List.of(v.getFirst(), v.getLast());
}
});
@@ -100,14 +100,14 @@ private static IntFunction lookupFunction(NavigableMap lookupFunction(NavigableMap lookupFunction(NavigableMap T lastItem(List list) {
- return list.get(list.size() - 1);
- }
-
private static Version nextVersion(Version version) {
return new Version(version.id + 100); // +1 to revision
}
diff --git a/server/src/main/java/org/elasticsearch/TransportVersion.java b/server/src/main/java/org/elasticsearch/TransportVersion.java
index 64d1c0535a561..032b10f0a30d6 100644
--- a/server/src/main/java/org/elasticsearch/TransportVersion.java
+++ b/server/src/main/java/org/elasticsearch/TransportVersion.java
@@ -130,20 +130,20 @@ public static TransportVersion fromString(String str) {
* When a patch version of an existing transport version is created, {@code transportVersion.isPatchFrom(patchVersion)}
* will match any transport version at or above {@code patchVersion} that is also of the same base version.
*
- * For example, {@code version.isPatchFrom(8_800_00_4)} will return the following for the given {@code version}:
+ * For example, {@code version.isPatchFrom(8_800_0_04)} will return the following for the given {@code version}:
*
*/
public boolean isPatchFrom(TransportVersion version) {
- return onOrAfter(version) && id < version.id + 10 - (version.id % 10);
+ return onOrAfter(version) && id < version.id + 100 - (version.id % 100);
}
/**
diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java
index 1144f94795713..efcebbec31c92 100644
--- a/server/src/main/java/org/elasticsearch/TransportVersions.java
+++ b/server/src/main/java/org/elasticsearch/TransportVersions.java
@@ -90,88 +90,89 @@ static TransportVersion def(int id) {
*/
public static final TransportVersion V_8_9_X = def(8_500_020);
public static final TransportVersion V_8_10_X = def(8_500_061);
- public static final TransportVersion V_8_11_X = def(8_512_00_1);
- public static final TransportVersion V_8_12_0 = def(8_560_00_0);
- public static final TransportVersion V_8_12_1 = def(8_560_00_1);
- public static final TransportVersion V_8_13_0 = def(8_595_00_0);
- public static final TransportVersion V_8_13_4 = def(8_595_00_1);
- public static final TransportVersion V_8_14_0 = def(8_636_00_1);
- public static final TransportVersion V_8_15_0 = def(8_702_00_2);
- public static final TransportVersion V_8_15_2 = def(8_702_00_3);
- public static final TransportVersion V_8_16_0 = def(8_772_00_1);
- public static final TransportVersion ADD_COMPATIBILITY_VERSIONS_TO_NODE_INFO_BACKPORT_8_16 = def(8_772_00_2);
- public static final TransportVersion SKIP_INNER_HITS_SEARCH_SOURCE_BACKPORT_8_16 = def(8_772_00_3);
- public static final TransportVersion QUERY_RULES_LIST_INCLUDES_TYPES_BACKPORT_8_16 = def(8_772_00_4);
- public static final TransportVersion REMOVE_MIN_COMPATIBLE_SHARD_NODE = def(8_773_00_0);
- public static final TransportVersion REVERT_REMOVE_MIN_COMPATIBLE_SHARD_NODE = def(8_774_00_0);
- public static final TransportVersion ESQL_FIELD_ATTRIBUTE_PARENT_SIMPLIFIED = def(8_775_00_0);
- public static final TransportVersion INFERENCE_DONT_PERSIST_ON_READ = def(8_776_00_0);
- public static final TransportVersion SIMULATE_MAPPING_ADDITION = def(8_777_00_0);
- public static final TransportVersion INTRODUCE_ALL_APPLICABLE_SELECTOR = def(8_778_00_0);
- public static final TransportVersion INDEX_MODE_LOOKUP = def(8_779_00_0);
- public static final TransportVersion INDEX_REQUEST_REMOVE_METERING = def(8_780_00_0);
- public static final TransportVersion CPU_STAT_STRING_PARSING = def(8_781_00_0);
- public static final TransportVersion QUERY_RULES_RETRIEVER = def(8_782_00_0);
- public static final TransportVersion ESQL_CCS_EXEC_INFO_WITH_FAILURES = def(8_783_00_0);
- public static final TransportVersion LOGSDB_TELEMETRY = def(8_784_00_0);
- public static final TransportVersion LOGSDB_TELEMETRY_STATS = def(8_785_00_0);
- public static final TransportVersion KQL_QUERY_ADDED = def(8_786_00_0);
- public static final TransportVersion ROLE_MONITOR_STATS = def(8_787_00_0);
- public static final TransportVersion DATA_STREAM_INDEX_VERSION_DEPRECATION_CHECK = def(8_788_00_0);
- public static final TransportVersion ADD_COMPATIBILITY_VERSIONS_TO_NODE_INFO = def(8_789_00_0);
- public static final TransportVersion VERTEX_AI_INPUT_TYPE_ADDED = def(8_790_00_0);
- public static final TransportVersion SKIP_INNER_HITS_SEARCH_SOURCE = def(8_791_00_0);
- public static final TransportVersion QUERY_RULES_LIST_INCLUDES_TYPES = def(8_792_00_0);
- public static final TransportVersion INDEX_STATS_ADDITIONAL_FIELDS = def(8_793_00_0);
- public static final TransportVersion INDEX_STATS_ADDITIONAL_FIELDS_REVERT = def(8_794_00_0);
- public static final TransportVersion FAST_REFRESH_RCO_2 = def(8_795_00_0);
- public static final TransportVersion ESQL_ENRICH_RUNTIME_WARNINGS = def(8_796_00_0);
- public static final TransportVersion INGEST_PIPELINE_CONFIGURATION_AS_MAP = def(8_797_00_0);
- public static final TransportVersion LOGSDB_TELEMETRY_CUSTOM_CUTOFF_DATE_FIX_8_17 = def(8_797_00_1);
- public static final TransportVersion SOURCE_MODE_TELEMETRY_FIX_8_17 = def(8_797_00_2);
- public static final TransportVersion INDEXING_PRESSURE_THROTTLING_STATS = def(8_798_00_0);
- public static final TransportVersion REINDEX_DATA_STREAMS = def(8_799_00_0);
- public static final TransportVersion ESQL_REMOVE_NODE_LEVEL_PLAN = def(8_800_00_0);
- public static final TransportVersion LOGSDB_TELEMETRY_CUSTOM_CUTOFF_DATE = def(8_801_00_0);
- public static final TransportVersion SOURCE_MODE_TELEMETRY = def(8_802_00_0);
- public static final TransportVersion NEW_REFRESH_CLUSTER_BLOCK = def(8_803_00_0);
- public static final TransportVersion RETRIES_AND_OPERATIONS_IN_BLOBSTORE_STATS = def(8_804_00_0);
- public static final TransportVersion ADD_DATA_STREAM_OPTIONS_TO_TEMPLATES = def(8_805_00_0);
- public static final TransportVersion KNN_QUERY_RESCORE_OVERSAMPLE = def(8_806_00_0);
- public static final TransportVersion SEMANTIC_QUERY_LENIENT = def(8_807_00_0);
- public static final TransportVersion ESQL_QUERY_BUILDER_IN_SEARCH_FUNCTIONS = def(8_808_00_0);
- public static final TransportVersion EQL_ALLOW_PARTIAL_SEARCH_RESULTS = def(8_809_00_0);
- public static final TransportVersion NODE_VERSION_INFORMATION_WITH_MIN_READ_ONLY_INDEX_VERSION = def(8_810_00_0);
- public static final TransportVersion ERROR_TRACE_IN_TRANSPORT_HEADER = def(8_811_00_0);
- public static final TransportVersion FAILURE_STORE_ENABLED_BY_CLUSTER_SETTING = def(8_812_00_0);
- public static final TransportVersion SIMULATE_IGNORED_FIELDS = def(8_813_00_0);
- public static final TransportVersion TRANSFORMS_UPGRADE_MODE = def(8_814_00_0);
- public static final TransportVersion NODE_SHUTDOWN_EPHEMERAL_ID_ADDED = def(8_815_00_0);
- public static final TransportVersion ESQL_CCS_TELEMETRY_STATS = def(8_816_00_0);
- public static final TransportVersion TEXT_EMBEDDING_QUERY_VECTOR_BUILDER_INFER_MODEL_ID = def(8_817_00_0);
- public static final TransportVersion ESQL_ENABLE_NODE_LEVEL_REDUCTION = def(8_818_00_0);
- public static final TransportVersion JINA_AI_INTEGRATION_ADDED = def(8_819_00_0);
- public static final TransportVersion TRACK_INDEX_FAILED_DUE_TO_VERSION_CONFLICT_METRIC = def(8_820_00_0);
- public static final TransportVersion REPLACE_FAILURE_STORE_OPTIONS_WITH_SELECTOR_SYNTAX = def(8_821_00_0);
- public static final TransportVersion ELASTIC_INFERENCE_SERVICE_UNIFIED_CHAT_COMPLETIONS_INTEGRATION = def(8_822_00_0);
- public static final TransportVersion KQL_QUERY_TECH_PREVIEW = def(8_823_00_0);
- public static final TransportVersion ESQL_PROFILE_ROWS_PROCESSED = def(8_824_00_0);
- public static final TransportVersion BYTE_SIZE_VALUE_ALWAYS_USES_BYTES_1 = def(8_825_00_0);
- public static final TransportVersion REVERT_BYTE_SIZE_VALUE_ALWAYS_USES_BYTES_1 = def(8_826_00_0);
- public static final TransportVersion ESQL_SKIP_ES_INDEX_SERIALIZATION = def(8_827_00_0);
- public static final TransportVersion ADD_INDEX_BLOCK_TWO_PHASE = def(8_828_00_0);
- public static final TransportVersion RESOLVE_CLUSTER_NO_INDEX_EXPRESSION = def(8_829_00_0);
- public static final TransportVersion ML_ROLLOVER_LEGACY_INDICES = def(8_830_00_0);
- public static final TransportVersion ADD_INCLUDE_FAILURE_INDICES_OPTION = def(8_831_00_0);
- public static final TransportVersion ESQL_RESPONSE_PARTIAL = def(8_832_00_0);
- public static final TransportVersion RANK_DOC_OPTIONAL_METADATA_FOR_EXPLAIN = def(8_833_00_0);
- public static final TransportVersion ILM_ADD_SEARCHABLE_SNAPSHOT_ADD_REPLICATE_FOR = def(8_834_00_0);
- public static final TransportVersion INGEST_REQUEST_INCLUDE_SOURCE_ON_ERROR = def(8_835_00_0);
- public static final TransportVersion RESOURCE_DEPRECATION_CHECKS = def(8_836_00_0);
- public static final TransportVersion LINEAR_RETRIEVER_SUPPORT = def(8_837_00_0);
- public static final TransportVersion TIMEOUT_GET_PARAM_FOR_RESOLVE_CLUSTER = def(8_838_00_0);
- public static final TransportVersion INFERENCE_REQUEST_ADAPTIVE_RATE_LIMITING = def(8_839_00_0);
- public static final TransportVersion ML_INFERENCE_IBM_WATSONX_RERANK_ADDED = def(8_840_00_0);
+ public static final TransportVersion V_8_11_X = def(8_512_0_01);
+ public static final TransportVersion V_8_12_0 = def(8_560_0_00);
+ public static final TransportVersion V_8_12_1 = def(8_560_0_01);
+ public static final TransportVersion V_8_13_0 = def(8_595_0_00);
+ public static final TransportVersion V_8_13_4 = def(8_595_0_01);
+ public static final TransportVersion V_8_14_0 = def(8_636_0_01);
+ public static final TransportVersion V_8_15_0 = def(8_702_0_02);
+ public static final TransportVersion V_8_15_2 = def(8_702_0_03);
+ public static final TransportVersion V_8_16_0 = def(8_772_0_01);
+ public static final TransportVersion ADD_COMPATIBILITY_VERSIONS_TO_NODE_INFO_BACKPORT_8_16 = def(8_772_0_02);
+ public static final TransportVersion SKIP_INNER_HITS_SEARCH_SOURCE_BACKPORT_8_16 = def(8_772_0_03);
+ public static final TransportVersion QUERY_RULES_LIST_INCLUDES_TYPES_BACKPORT_8_16 = def(8_772_0_04);
+ public static final TransportVersion REMOVE_MIN_COMPATIBLE_SHARD_NODE = def(8_773_0_00);
+ public static final TransportVersion REVERT_REMOVE_MIN_COMPATIBLE_SHARD_NODE = def(8_774_0_00);
+ public static final TransportVersion ESQL_FIELD_ATTRIBUTE_PARENT_SIMPLIFIED = def(8_775_0_00);
+ public static final TransportVersion INFERENCE_DONT_PERSIST_ON_READ = def(8_776_0_00);
+ public static final TransportVersion SIMULATE_MAPPING_ADDITION = def(8_777_0_00);
+ public static final TransportVersion INTRODUCE_ALL_APPLICABLE_SELECTOR = def(8_778_0_00);
+ public static final TransportVersion INDEX_MODE_LOOKUP = def(8_779_0_00);
+ public static final TransportVersion INDEX_REQUEST_REMOVE_METERING = def(8_780_0_00);
+ public static final TransportVersion CPU_STAT_STRING_PARSING = def(8_781_0_00);
+ public static final TransportVersion QUERY_RULES_RETRIEVER = def(8_782_0_00);
+ public static final TransportVersion ESQL_CCS_EXEC_INFO_WITH_FAILURES = def(8_783_0_00);
+ public static final TransportVersion LOGSDB_TELEMETRY = def(8_784_0_00);
+ public static final TransportVersion LOGSDB_TELEMETRY_STATS = def(8_785_0_00);
+ public static final TransportVersion KQL_QUERY_ADDED = def(8_786_0_00);
+ public static final TransportVersion ROLE_MONITOR_STATS = def(8_787_0_00);
+ public static final TransportVersion DATA_STREAM_INDEX_VERSION_DEPRECATION_CHECK = def(8_788_0_00);
+ public static final TransportVersion ADD_COMPATIBILITY_VERSIONS_TO_NODE_INFO = def(8_789_0_00);
+ public static final TransportVersion VERTEX_AI_INPUT_TYPE_ADDED = def(8_790_0_00);
+ public static final TransportVersion SKIP_INNER_HITS_SEARCH_SOURCE = def(8_791_0_00);
+ public static final TransportVersion QUERY_RULES_LIST_INCLUDES_TYPES = def(8_792_0_00);
+ public static final TransportVersion INDEX_STATS_ADDITIONAL_FIELDS = def(8_793_0_00);
+ public static final TransportVersion INDEX_STATS_ADDITIONAL_FIELDS_REVERT = def(8_794_0_00);
+ public static final TransportVersion FAST_REFRESH_RCO_2 = def(8_795_0_00);
+ public static final TransportVersion ESQL_ENRICH_RUNTIME_WARNINGS = def(8_796_0_00);
+ public static final TransportVersion INGEST_PIPELINE_CONFIGURATION_AS_MAP = def(8_797_0_00);
+ public static final TransportVersion LOGSDB_TELEMETRY_CUSTOM_CUTOFF_DATE_FIX_8_17 = def(8_797_0_01);
+ public static final TransportVersion SOURCE_MODE_TELEMETRY_FIX_8_17 = def(8_797_0_02);
+ public static final TransportVersion INDEXING_PRESSURE_THROTTLING_STATS = def(8_798_0_00);
+ public static final TransportVersion REINDEX_DATA_STREAMS = def(8_799_0_00);
+ public static final TransportVersion ESQL_REMOVE_NODE_LEVEL_PLAN = def(8_800_0_00);
+ public static final TransportVersion LOGSDB_TELEMETRY_CUSTOM_CUTOFF_DATE = def(8_801_0_00);
+ public static final TransportVersion SOURCE_MODE_TELEMETRY = def(8_802_0_00);
+ public static final TransportVersion NEW_REFRESH_CLUSTER_BLOCK = def(8_803_0_00);
+ public static final TransportVersion RETRIES_AND_OPERATIONS_IN_BLOBSTORE_STATS = def(8_804_0_00);
+ public static final TransportVersion ADD_DATA_STREAM_OPTIONS_TO_TEMPLATES = def(8_805_0_00);
+ public static final TransportVersion KNN_QUERY_RESCORE_OVERSAMPLE = def(8_806_0_00);
+ public static final TransportVersion SEMANTIC_QUERY_LENIENT = def(8_807_0_00);
+ public static final TransportVersion ESQL_QUERY_BUILDER_IN_SEARCH_FUNCTIONS = def(8_808_0_00);
+ public static final TransportVersion EQL_ALLOW_PARTIAL_SEARCH_RESULTS = def(8_809_0_00);
+ public static final TransportVersion NODE_VERSION_INFORMATION_WITH_MIN_READ_ONLY_INDEX_VERSION = def(8_810_0_00);
+ public static final TransportVersion ERROR_TRACE_IN_TRANSPORT_HEADER = def(8_811_0_00);
+ public static final TransportVersion FAILURE_STORE_ENABLED_BY_CLUSTER_SETTING = def(8_812_0_00);
+ public static final TransportVersion SIMULATE_IGNORED_FIELDS = def(8_813_0_00);
+ public static final TransportVersion TRANSFORMS_UPGRADE_MODE = def(8_814_0_00);
+ public static final TransportVersion NODE_SHUTDOWN_EPHEMERAL_ID_ADDED = def(8_815_0_00);
+ public static final TransportVersion ESQL_CCS_TELEMETRY_STATS = def(8_816_0_00);
+ public static final TransportVersion TEXT_EMBEDDING_QUERY_VECTOR_BUILDER_INFER_MODEL_ID = def(8_817_0_00);
+ public static final TransportVersion ESQL_ENABLE_NODE_LEVEL_REDUCTION = def(8_818_0_00);
+ public static final TransportVersion JINA_AI_INTEGRATION_ADDED = def(8_819_0_00);
+ public static final TransportVersion TRACK_INDEX_FAILED_DUE_TO_VERSION_CONFLICT_METRIC = def(8_820_0_00);
+ public static final TransportVersion REPLACE_FAILURE_STORE_OPTIONS_WITH_SELECTOR_SYNTAX = def(8_821_0_00);
+ public static final TransportVersion ELASTIC_INFERENCE_SERVICE_UNIFIED_CHAT_COMPLETIONS_INTEGRATION = def(8_822_0_00);
+ public static final TransportVersion KQL_QUERY_TECH_PREVIEW = def(8_823_0_00);
+ public static final TransportVersion ESQL_PROFILE_ROWS_PROCESSED = def(8_824_0_00);
+ public static final TransportVersion BYTE_SIZE_VALUE_ALWAYS_USES_BYTES_1 = def(8_825_0_00);
+ public static final TransportVersion REVERT_BYTE_SIZE_VALUE_ALWAYS_USES_BYTES_1 = def(8_826_0_00);
+ public static final TransportVersion ESQL_SKIP_ES_INDEX_SERIALIZATION = def(8_827_0_00);
+ public static final TransportVersion ADD_INDEX_BLOCK_TWO_PHASE = def(8_828_0_00);
+ public static final TransportVersion RESOLVE_CLUSTER_NO_INDEX_EXPRESSION = def(8_829_0_00);
+ public static final TransportVersion ML_ROLLOVER_LEGACY_INDICES = def(8_830_0_00);
+ public static final TransportVersion ADD_INCLUDE_FAILURE_INDICES_OPTION = def(8_831_0_00);
+ public static final TransportVersion ESQL_RESPONSE_PARTIAL = def(8_832_0_00);
+ public static final TransportVersion RANK_DOC_OPTIONAL_METADATA_FOR_EXPLAIN = def(8_833_0_00);
+ public static final TransportVersion ILM_ADD_SEARCHABLE_SNAPSHOT_ADD_REPLICATE_FOR = def(8_834_0_00);
+ public static final TransportVersion INGEST_REQUEST_INCLUDE_SOURCE_ON_ERROR = def(8_835_0_00);
+ public static final TransportVersion RESOURCE_DEPRECATION_CHECKS = def(8_836_0_00);
+ public static final TransportVersion LINEAR_RETRIEVER_SUPPORT = def(8_837_0_00);
+ public static final TransportVersion TIMEOUT_GET_PARAM_FOR_RESOLVE_CLUSTER = def(8_838_0_00);
+ public static final TransportVersion INFERENCE_REQUEST_ADAPTIVE_RATE_LIMITING = def(8_839_0_00);
+ public static final TransportVersion ML_INFERENCE_IBM_WATSONX_RERANK_ADDED = def(8_840_0_00);
+ public static final TransportVersion ELASTICSEARCH_9_0 = def(9_000_0_00);
/*
* STOP! READ THIS FIRST! No, really,
@@ -188,17 +189,17 @@ static TransportVersion def(int id) {
* To add a new transport version, add a new constant at the bottom of the list, above this comment. Don't add other lines,
* comments, etc. The version id has the following layout:
*
- * M_NNN_SS_P
+ * M_NNN_S_PP
*
* M - The major version of Elasticsearch
* NNN - The server version part
- * SS - The serverless version part. It should always be 00 here, it is used by serverless only.
- * P - The patch version part
+ * S - The subsidiary version part. It should always be 0 here, it is only used in subsidiary repositories.
+ * PP - The patch version part
*
* To determine the id of the next TransportVersion constant, do the following:
* - Use the same major version, unless bumping majors
* - Bump the server version part by 1, unless creating a patch version
- * - Leave the serverless part as 00
+ * - Leave the subsidiary part as 0
* - Bump the patch part if creating a patch version
*
* If a patch version is created, it should be placed sorted among the other existing constants.
@@ -230,15 +231,13 @@ static TransportVersion def(int id) {
* Reference to the earliest compatible transport version to this version of the codebase.
* This should be the transport version used by the highest minor version of the previous major.
*/
- @UpdateForV9(owner = UpdateForV9.Owner.CORE_INFRA)
- // This needs to be bumped to the 8.last
- public static final TransportVersion MINIMUM_COMPATIBLE = V_7_17_0;
+ public static final TransportVersion MINIMUM_COMPATIBLE = BYTE_SIZE_VALUE_ALWAYS_USES_BYTES_1;
/**
* Reference to the minimum transport version that can be used with CCS.
* This should be the transport version used by the previous minor release.
*/
- public static final TransportVersion MINIMUM_CCS_VERSION = V_8_15_0;
+ public static final TransportVersion MINIMUM_CCS_VERSION = BYTE_SIZE_VALUE_ALWAYS_USES_BYTES_1;
/**
* Sorted list of all versions defined in this class
diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/AllocationBalancingRoundSummaryService.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/AllocationBalancingRoundSummaryService.java
new file mode 100644
index 0000000000000..2e45938f3d2c0
--- /dev/null
+++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/AllocationBalancingRoundSummaryService.java
@@ -0,0 +1,194 @@
+/*
+ * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+package org.elasticsearch.cluster.routing.allocation.allocator;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.elasticsearch.common.settings.ClusterSettings;
+import org.elasticsearch.common.settings.Setting;
+import org.elasticsearch.core.TimeValue;
+import org.elasticsearch.threadpool.Scheduler;
+import org.elasticsearch.threadpool.ThreadPool;
+
+import java.util.ArrayList;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Manages the lifecycle of a series of {@link BalancingRoundSummary} results from allocation balancing rounds and creates reports thereof.
+ * Reporting balancer round summary results will provide information with which to do cost-benefit analyses of the work that shard
+ * allocation rebalancing executes.
+ *
+ * Any successfully added summary via {@link #addBalancerRoundSummary(BalancingRoundSummary)} will eventually be collected/drained and
+ * reported. This should still be done in the event of the node stepping down from master, on the assumption that all summaries are only
+ * added while master and should be drained for reporting. There is no need to start/stop this service with master election/stepdown because
+ * balancer rounds will no longer be supplied when not master. It will simply drain the last summaries and then have nothing more to do.
+ * This does have the tradeoff that non-master nodes will run a task to check for summaries to report every
+ * {@link #BALANCER_ROUND_SUMMARIES_LOG_INTERVAL_SETTING} seconds.
+ */
+public class AllocationBalancingRoundSummaryService {
+
+ /** Turns on or off balancing round summary reporting. */
+ public static final Setting ENABLE_BALANCER_ROUND_SUMMARIES_SETTING = Setting.boolSetting(
+ "cluster.routing.allocation.desired_balance.enable_balancer_round_summaries",
+ false,
+ Setting.Property.NodeScope,
+ Setting.Property.Dynamic
+ );
+
+ /** Controls how frequently in time balancer round summaries are logged. */
+ public static final Setting BALANCER_ROUND_SUMMARIES_LOG_INTERVAL_SETTING = Setting.timeSetting(
+ "cluster.routing.allocation.desired_balance.balanace_round_summaries_interval",
+ TimeValue.timeValueSeconds(10),
+ TimeValue.ZERO,
+ Setting.Property.NodeScope,
+ Setting.Property.Dynamic
+ );
+
+ private static final Logger logger = LogManager.getLogger(AllocationBalancingRoundSummaryService.class);
+ private final ThreadPool threadPool;
+ private volatile boolean enableBalancerRoundSummaries;
+ private volatile TimeValue summaryReportInterval;
+
+ /**
+ * A concurrency-safe list of balancing round summaries. Balancer rounds are run and added here serially, so the queue will naturally
+ * progress from newer to older results.
+ */
+ private final ConcurrentLinkedQueue summaries = new ConcurrentLinkedQueue<>();
+
+ /** This reference is set when reporting is scheduled. If it is null, then reporting is inactive. */
+ private final AtomicReference scheduledReportFuture = new AtomicReference<>();
+
+ public AllocationBalancingRoundSummaryService(ThreadPool threadPool, ClusterSettings clusterSettings) {
+ this.threadPool = threadPool;
+ // Initialize the local setting values to avoid a null access when ClusterSettings#initializeAndWatch is called on each setting:
+ // updating enableBalancerRoundSummaries accesses summaryReportInterval.
+ this.enableBalancerRoundSummaries = clusterSettings.get(ENABLE_BALANCER_ROUND_SUMMARIES_SETTING);
+ this.summaryReportInterval = clusterSettings.get(BALANCER_ROUND_SUMMARIES_LOG_INTERVAL_SETTING);
+
+ clusterSettings.initializeAndWatch(ENABLE_BALANCER_ROUND_SUMMARIES_SETTING, value -> {
+ this.enableBalancerRoundSummaries = value;
+ updateBalancingRoundSummaryReporting();
+ });
+ clusterSettings.initializeAndWatch(BALANCER_ROUND_SUMMARIES_LOG_INTERVAL_SETTING, value -> {
+ // The new value will get picked up the next time that the summary report task reschedules itself on the thread pool.
+ this.summaryReportInterval = value;
+ });
+ }
+
+ /**
+ * Adds the summary of a balancing round. If summaries are enabled, this will eventually be reported (logging, etc.). If balancer round
+ * summaries are not enabled in the cluster, then the summary is immediately discarded (so as not to fill up a data structure that will
+ * never be drained).
+ */
+ public void addBalancerRoundSummary(BalancingRoundSummary summary) {
+ if (enableBalancerRoundSummaries == false) {
+ return;
+ }
+
+ summaries.add(summary);
+ }
+
+ /**
+ * Reports on all the balancer round summaries added since the last call to this method, if there are any. Then reschedules itself per
+ * the {@link #ENABLE_BALANCER_ROUND_SUMMARIES_SETTING} and {@link #BALANCER_ROUND_SUMMARIES_LOG_INTERVAL_SETTING} settings.
+ */
+ private void reportSummariesAndThenReschedule() {
+ drainAndReportSummaries();
+ rescheduleReporting();
+ }
+
+ /**
+ * Drains all the waiting balancer round summaries (if there are any) and reports them.
+ */
+ private void drainAndReportSummaries() {
+ var combinedSummaries = drainSummaries();
+ if (combinedSummaries == CombinedBalancingRoundSummary.EMPTY_RESULTS) {
+ return;
+ }
+
+ logger.info("Balancing round summaries: " + combinedSummaries);
+ }
+
+ /**
+ * Returns a combined summary of all unreported allocation round summaries: may summarize a single balancer round, multiple, or none.
+ *
+ * @return {@link CombinedBalancingRoundSummary#EMPTY_RESULTS} if there are no balancing round summaries waiting to be reported.
+ */
+ private CombinedBalancingRoundSummary drainSummaries() {
+ ArrayList batchOfSummaries = new ArrayList<>();
+ while (summaries.isEmpty() == false) {
+ batchOfSummaries.add(summaries.poll());
+ }
+ return CombinedBalancingRoundSummary.combine(batchOfSummaries);
+ }
+
+ /**
+ * Schedules a periodic task to drain and report the latest balancer round summaries, or cancels the already running task, if the latest
+ * setting values dictate a change to enable or disable reporting. A change to {@link #BALANCER_ROUND_SUMMARIES_LOG_INTERVAL_SETTING}
+ * will only take effect when the periodic task completes and reschedules itself.
+ */
+ private void updateBalancingRoundSummaryReporting() {
+ if (this.enableBalancerRoundSummaries) {
+ startReporting(this.summaryReportInterval);
+ } else {
+ cancelReporting();
+ // Clear the data structure so that we don't retain unnecessary memory.
+ drainSummaries();
+ }
+ }
+
+ /**
+ * Schedules a reporting task, if one is not already scheduled. The reporting task will reschedule itself going forward.
+ */
+ private void startReporting(TimeValue intervalValue) {
+ if (scheduledReportFuture.get() == null) {
+ scheduleReporting(intervalValue);
+ }
+ }
+
+ /**
+ * Cancels the future reporting task and resets {@link #scheduledReportFuture} to null.
+ *
+ * Note that this is best-effort: cancellation can race with {@link #rescheduleReporting}. But that is okay because the subsequent
+ * {@link #rescheduleReporting} will use the latest settings and choose to cancel reporting if appropriate.
+ */
+ private void cancelReporting() {
+ var future = scheduledReportFuture.getAndSet(null);
+ if (future != null) {
+ future.cancel();
+ }
+ }
+
+ private void scheduleReporting(TimeValue intervalValue) {
+ scheduledReportFuture.set(
+ threadPool.schedule(this::reportSummariesAndThenReschedule, intervalValue, threadPool.executor(ThreadPool.Names.GENERIC))
+ );
+ }
+
+ /**
+ * Looks at the given setting values and decides whether to schedule another reporting task or cancel reporting now.
+ */
+ private void rescheduleReporting() {
+ if (this.enableBalancerRoundSummaries) {
+ // It's possible that this races with a concurrent call to cancel reporting, but that's okay. The next rescheduleReporting call
+ // will check the latest settings and cancel.
+ scheduleReporting(this.summaryReportInterval);
+ } else {
+ cancelReporting();
+ }
+ }
+
+ // @VisibleForTesting
+ protected void verifyNumberOfSummaries(int numberOfSummaries) {
+ assert numberOfSummaries == summaries.size();
+ }
+
+}
diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancingRoundSummary.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancingRoundSummary.java
new file mode 100644
index 0000000000000..2662825eff48e
--- /dev/null
+++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancingRoundSummary.java
@@ -0,0 +1,24 @@
+/*
+ * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+package org.elasticsearch.cluster.routing.allocation.allocator;
+
+/**
+ * Summarizes the impact to the cluster as a result of a rebalancing round.
+ *
+ * @param numberOfShardsToMove The number of shard moves required to move from the previous desired balance to the new one.
+ */
+public record BalancingRoundSummary(long numberOfShardsToMove) {
+
+ @Override
+ public String toString() {
+ return "BalancingRoundSummary{" + "numberOfShardsToMove=" + numberOfShardsToMove + '}';
+ }
+
+}
diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/CombinedBalancingRoundSummary.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/CombinedBalancingRoundSummary.java
new file mode 100644
index 0000000000000..78fa1f6c5f5f5
--- /dev/null
+++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/CombinedBalancingRoundSummary.java
@@ -0,0 +1,45 @@
+/*
+ * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+package org.elasticsearch.cluster.routing.allocation.allocator;
+
+import java.util.List;
+
+/**
+ * Holds combined {@link BalancingRoundSummary} results. Essentially holds a list of the balancing events and the summed up changes
+ * across all those events: what allocation work was done across some period of time.
+ * TODO: WIP ES-10341
+ *
+ * Note that each balancing round summary is the difference between, at the time, latest desired balance and the previous desired balance.
+ * Each summary represents a step towards the next desired balance, which is based on presuming the previous desired balance is reached. So
+ * combining them is roughly the difference between the first summary's previous desired balance and the last summary's latest desired
+ * balance.
+ *
+ * @param numberOfBalancingRounds How many balancing round summaries are combined in this report.
+ * @param numberOfShardMoves The sum of shard moves for each balancing round being combined into a single summary.
+ */
+public record CombinedBalancingRoundSummary(int numberOfBalancingRounds, long numberOfShardMoves) {
+
+ public static final CombinedBalancingRoundSummary EMPTY_RESULTS = new CombinedBalancingRoundSummary(0, 0);
+
+ public static CombinedBalancingRoundSummary combine(List summaries) {
+ if (summaries.isEmpty()) {
+ return EMPTY_RESULTS;
+ }
+
+ int numSummaries = 0;
+ long numberOfShardMoves = 0;
+ for (BalancingRoundSummary summary : summaries) {
+ ++numSummaries;
+ numberOfShardMoves += summary.numberOfShardsToMove();
+ }
+ return new CombinedBalancingRoundSummary(numSummaries, numberOfShardMoves);
+ }
+
+}
diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/DesiredBalance.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/DesiredBalance.java
index 16cbf41ee1bfa..202582839f1d9 100644
--- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/DesiredBalance.java
+++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/DesiredBalance.java
@@ -24,8 +24,7 @@
* strictly increasing sequence number. A new master term restarts the index values from zero. The balancer,
* which runs async to reroute, uses the latest request's data to compute the desired balance.
* @param assignments a set of the (persistent) node IDs to which each {@link ShardId} should be allocated
- * @param weightsPerNode The node weights calculated based on
- * {@link org.elasticsearch.cluster.routing.allocation.allocator.WeightFunction#calculateNodeWeight}
+ * @param weightsPerNode The node weights calculated based on {@link WeightFunction#calculateNodeWeight}
*/
public record DesiredBalance(
long lastConvergedIndex,
diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/DesiredBalanceShardsAllocator.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/DesiredBalanceShardsAllocator.java
index 2c73a27ad3418..d9fba492fb9d0 100644
--- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/DesiredBalanceShardsAllocator.java
+++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/DesiredBalanceShardsAllocator.java
@@ -88,6 +88,10 @@ public class DesiredBalanceShardsAllocator implements ShardsAllocator {
private volatile boolean resetCurrentDesiredBalance = false;
private final Set processedNodeShutdowns = new HashSet<>();
private final DesiredBalanceMetrics desiredBalanceMetrics;
+ /**
+ * Manages balancer round results in order to report on the balancer activity in a configurable manner.
+ */
+ private final AllocationBalancingRoundSummaryService balancerRoundSummaryService;
// stats
protected final CounterMetric computationsSubmitted = new CounterMetric();
@@ -132,6 +136,7 @@ public DesiredBalanceShardsAllocator(
NodeAllocationStatsAndWeightsCalculator nodeAllocationStatsAndWeightsCalculator
) {
this.desiredBalanceMetrics = new DesiredBalanceMetrics(telemetryProvider.getMeterRegistry());
+ this.balancerRoundSummaryService = new AllocationBalancingRoundSummaryService(threadPool, clusterService.getClusterSettings());
this.delegateAllocator = delegateAllocator;
this.threadPool = threadPool;
this.reconciler = reconciler;
@@ -320,6 +325,7 @@ private void setCurrentDesiredBalance(DesiredBalance newDesiredBalance) {
}
if (currentDesiredBalanceRef.compareAndSet(oldDesiredBalance, newDesiredBalance)) {
+ balancerRoundSummaryService.addBalancerRoundSummary(calculateBalancingRoundSummary(oldDesiredBalance, newDesiredBalance));
if (logger.isTraceEnabled()) {
var diff = DesiredBalance.hasChanges(oldDesiredBalance, newDesiredBalance)
? "Diff: " + DesiredBalance.humanReadableDiff(oldDesiredBalance, newDesiredBalance)
@@ -334,6 +340,13 @@ private void setCurrentDesiredBalance(DesiredBalance newDesiredBalance) {
}
}
+ /**
+ * Summarizes the work required to move from an old to new desired balance shard allocation.
+ */
+ private BalancingRoundSummary calculateBalancingRoundSummary(DesiredBalance oldDesiredBalance, DesiredBalance newDesiredBalance) {
+ return new BalancingRoundSummary(DesiredBalance.shardMovements(oldDesiredBalance, newDesiredBalance));
+ }
+
protected void submitReconcileTask(DesiredBalance desiredBalance) {
masterServiceTaskQueue.submitTask("reconcile-desired-balance", new ReconcileDesiredBalanceTask(desiredBalance), null);
}
diff --git a/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java b/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java
index e9b9a5ea4ab9e..7397382866388 100644
--- a/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java
+++ b/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java
@@ -46,6 +46,7 @@
import org.elasticsearch.cluster.routing.OperationRouting;
import org.elasticsearch.cluster.routing.allocation.DataTier;
import org.elasticsearch.cluster.routing.allocation.DiskThresholdSettings;
+import org.elasticsearch.cluster.routing.allocation.allocator.AllocationBalancingRoundSummaryService;
import org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator;
import org.elasticsearch.cluster.routing.allocation.allocator.DesiredBalanceComputer;
import org.elasticsearch.cluster.routing.allocation.allocator.DesiredBalanceReconciler;
@@ -212,6 +213,8 @@ public void apply(Settings value, Settings current, Settings previous) {
}
public static final Set> BUILT_IN_CLUSTER_SETTINGS = Stream.of(
+ AllocationBalancingRoundSummaryService.ENABLE_BALANCER_ROUND_SUMMARIES_SETTING,
+ AllocationBalancingRoundSummaryService.BALANCER_ROUND_SUMMARIES_LOG_INTERVAL_SETTING,
AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING,
AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_FORCE_GROUP_SETTING,
BalancedShardsAllocator.INDEX_BALANCE_FACTOR_SETTING,
diff --git a/server/src/main/java/org/elasticsearch/index/IndexVersions.java b/server/src/main/java/org/elasticsearch/index/IndexVersions.java
index 2470bfb7e5c56..3b173ace0ac7b 100644
--- a/server/src/main/java/org/elasticsearch/index/IndexVersions.java
+++ b/server/src/main/java/org/elasticsearch/index/IndexVersions.java
@@ -24,7 +24,6 @@
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.IntFunction;
-import java.util.stream.Collectors;
@SuppressWarnings("deprecation")
public class IndexVersions {
@@ -108,43 +107,43 @@ private static Version parseUnchecked(String version) {
public static final IndexVersion UPGRADE_LUCENE_9_9_1 = def(8_500_008, Version.LUCENE_9_9_1);
public static final IndexVersion ES_VERSION_8_12_1 = def(8_500_009, Version.LUCENE_9_9_1);
public static final IndexVersion UPGRADE_8_12_1_LUCENE_9_9_2 = def(8_500_010, Version.LUCENE_9_9_2);
- public static final IndexVersion NEW_INDEXVERSION_FORMAT = def(8_501_00_0, Version.LUCENE_9_9_1);
- public static final IndexVersion UPGRADE_LUCENE_9_9_2 = def(8_502_00_0, Version.LUCENE_9_9_2);
- public static final IndexVersion TIME_SERIES_ID_HASHING = def(8_502_00_1, Version.LUCENE_9_9_2);
- public static final IndexVersion UPGRADE_TO_LUCENE_9_10 = def(8_503_00_0, Version.LUCENE_9_10_0);
- public static final IndexVersion TIME_SERIES_ROUTING_HASH_IN_ID = def(8_504_00_0, Version.LUCENE_9_10_0);
- public static final IndexVersion DEFAULT_DENSE_VECTOR_TO_INT8_HNSW = def(8_505_00_0, Version.LUCENE_9_10_0);
- public static final IndexVersion DOC_VALUES_FOR_IGNORED_META_FIELD = def(8_505_00_1, Version.LUCENE_9_10_0);
- public static final IndexVersion SOURCE_MAPPER_LOSSY_PARAMS_CHECK = def(8_506_00_0, Version.LUCENE_9_10_0);
- public static final IndexVersion SEMANTIC_TEXT_FIELD_TYPE = def(8_507_00_0, Version.LUCENE_9_10_0);
- public static final IndexVersion UPGRADE_TO_LUCENE_9_11 = def(8_508_00_0, Version.LUCENE_9_11_0);
- public static final IndexVersion UNIQUE_TOKEN_FILTER_POS_FIX = def(8_509_00_0, Version.LUCENE_9_11_0);
- public static final IndexVersion ADD_SECURITY_MIGRATION = def(8_510_00_0, Version.LUCENE_9_11_0);
- public static final IndexVersion UPGRADE_TO_LUCENE_9_11_1 = def(8_511_00_0, Version.LUCENE_9_11_1);
- public static final IndexVersion INDEX_SORTING_ON_NESTED = def(8_512_00_0, Version.LUCENE_9_11_1);
- public static final IndexVersion LENIENT_UPDATEABLE_SYNONYMS = def(8_513_00_0, Version.LUCENE_9_11_1);
- public static final IndexVersion ENABLE_IGNORE_MALFORMED_LOGSDB = def(8_514_00_0, Version.LUCENE_9_11_1);
- public static final IndexVersion MERGE_ON_RECOVERY_VERSION = def(8_515_00_0, Version.LUCENE_9_11_1);
- public static final IndexVersion UPGRADE_TO_LUCENE_9_12 = def(8_516_00_0, Version.LUCENE_9_12_0);
- public static final IndexVersion ENABLE_IGNORE_ABOVE_LOGSDB = def(8_517_00_0, Version.LUCENE_9_12_0);
- public static final IndexVersion ADD_ROLE_MAPPING_CLEANUP_MIGRATION = def(8_518_00_0, Version.LUCENE_9_12_0);
- public static final IndexVersion LOGSDB_DEFAULT_IGNORE_DYNAMIC_BEYOND_LIMIT_BACKPORT = def(8_519_00_0, Version.LUCENE_9_12_0);
- public static final IndexVersion TIME_BASED_K_ORDERED_DOC_ID_BACKPORT = def(8_520_00_0, Version.LUCENE_9_12_0);
- public static final IndexVersion V8_DEPRECATE_SOURCE_MODE_MAPPER = def(8_521_00_0, Version.LUCENE_9_12_0);
- public static final IndexVersion USE_SYNTHETIC_SOURCE_FOR_RECOVERY_BACKPORT = def(8_522_00_0, Version.LUCENE_9_12_0);
- public static final IndexVersion UPGRADE_TO_LUCENE_9_12_1 = def(8_523_00_0, parseUnchecked("9.12.1"));
- public static final IndexVersion INFERENCE_METADATA_FIELDS_BACKPORT = def(8_524_00_0, parseUnchecked("9.12.1"));
- public static final IndexVersion LOGSB_OPTIONAL_SORTING_ON_HOST_NAME_BACKPORT = def(8_525_00_0, parseUnchecked("9.12.1"));
- public static final IndexVersion UPGRADE_TO_LUCENE_10_0_0 = def(9_000_00_0, Version.LUCENE_10_0_0);
- public static final IndexVersion LOGSDB_DEFAULT_IGNORE_DYNAMIC_BEYOND_LIMIT = def(9_001_00_0, Version.LUCENE_10_0_0);
- public static final IndexVersion TIME_BASED_K_ORDERED_DOC_ID = def(9_002_00_0, Version.LUCENE_10_0_0);
- public static final IndexVersion DEPRECATE_SOURCE_MODE_MAPPER = def(9_003_00_0, Version.LUCENE_10_0_0);
- public static final IndexVersion USE_SYNTHETIC_SOURCE_FOR_RECOVERY = def(9_004_00_0, Version.LUCENE_10_0_0);
- public static final IndexVersion INFERENCE_METADATA_FIELDS = def(9_005_00_0, Version.LUCENE_10_0_0);
- public static final IndexVersion LOGSB_OPTIONAL_SORTING_ON_HOST_NAME = def(9_006_00_0, Version.LUCENE_10_0_0);
- public static final IndexVersion SOURCE_MAPPER_MODE_ATTRIBUTE_NOOP = def(9_007_00_0, Version.LUCENE_10_0_0);
- public static final IndexVersion HOSTNAME_DOC_VALUES_SPARSE_INDEX = def(9_008_00_0, Version.LUCENE_10_0_0);
- public static final IndexVersion UPGRADE_TO_LUCENE_10_1_0 = def(9_009_00_0, Version.LUCENE_10_1_0);
+ public static final IndexVersion NEW_INDEXVERSION_FORMAT = def(8_501_0_00, Version.LUCENE_9_9_1);
+ public static final IndexVersion UPGRADE_LUCENE_9_9_2 = def(8_502_0_00, Version.LUCENE_9_9_2);
+ public static final IndexVersion TIME_SERIES_ID_HASHING = def(8_502_0_01, Version.LUCENE_9_9_2);
+ public static final IndexVersion UPGRADE_TO_LUCENE_9_10 = def(8_503_0_00, Version.LUCENE_9_10_0);
+ public static final IndexVersion TIME_SERIES_ROUTING_HASH_IN_ID = def(8_504_0_00, Version.LUCENE_9_10_0);
+ public static final IndexVersion DEFAULT_DENSE_VECTOR_TO_INT8_HNSW = def(8_505_0_00, Version.LUCENE_9_10_0);
+ public static final IndexVersion DOC_VALUES_FOR_IGNORED_META_FIELD = def(8_505_0_01, Version.LUCENE_9_10_0);
+ public static final IndexVersion SOURCE_MAPPER_LOSSY_PARAMS_CHECK = def(8_506_0_00, Version.LUCENE_9_10_0);
+ public static final IndexVersion SEMANTIC_TEXT_FIELD_TYPE = def(8_507_0_00, Version.LUCENE_9_10_0);
+ public static final IndexVersion UPGRADE_TO_LUCENE_9_11 = def(8_508_0_00, Version.LUCENE_9_11_0);
+ public static final IndexVersion UNIQUE_TOKEN_FILTER_POS_FIX = def(8_509_0_00, Version.LUCENE_9_11_0);
+ public static final IndexVersion ADD_SECURITY_MIGRATION = def(8_510_0_00, Version.LUCENE_9_11_0);
+ public static final IndexVersion UPGRADE_TO_LUCENE_9_11_1 = def(8_511_0_00, Version.LUCENE_9_11_1);
+ public static final IndexVersion INDEX_SORTING_ON_NESTED = def(8_512_0_00, Version.LUCENE_9_11_1);
+ public static final IndexVersion LENIENT_UPDATEABLE_SYNONYMS = def(8_513_0_00, Version.LUCENE_9_11_1);
+ public static final IndexVersion ENABLE_IGNORE_MALFORMED_LOGSDB = def(8_514_0_00, Version.LUCENE_9_11_1);
+ public static final IndexVersion MERGE_ON_RECOVERY_VERSION = def(8_515_0_00, Version.LUCENE_9_11_1);
+ public static final IndexVersion UPGRADE_TO_LUCENE_9_12 = def(8_516_0_00, Version.LUCENE_9_12_0);
+ public static final IndexVersion ENABLE_IGNORE_ABOVE_LOGSDB = def(8_517_0_00, Version.LUCENE_9_12_0);
+ public static final IndexVersion ADD_ROLE_MAPPING_CLEANUP_MIGRATION = def(8_518_0_00, Version.LUCENE_9_12_0);
+ public static final IndexVersion LOGSDB_DEFAULT_IGNORE_DYNAMIC_BEYOND_LIMIT_BACKPORT = def(8_519_0_00, Version.LUCENE_9_12_0);
+ public static final IndexVersion TIME_BASED_K_ORDERED_DOC_ID_BACKPORT = def(8_520_0_00, Version.LUCENE_9_12_0);
+ public static final IndexVersion V8_DEPRECATE_SOURCE_MODE_MAPPER = def(8_521_0_00, Version.LUCENE_9_12_0);
+ public static final IndexVersion USE_SYNTHETIC_SOURCE_FOR_RECOVERY_BACKPORT = def(8_522_0_00, Version.LUCENE_9_12_0);
+ public static final IndexVersion UPGRADE_TO_LUCENE_9_12_1 = def(8_523_0_00, parseUnchecked("9.12.1"));
+ public static final IndexVersion INFERENCE_METADATA_FIELDS_BACKPORT = def(8_524_0_00, parseUnchecked("9.12.1"));
+ public static final IndexVersion LOGSB_OPTIONAL_SORTING_ON_HOST_NAME_BACKPORT = def(8_525_0_00, parseUnchecked("9.12.1"));
+ public static final IndexVersion UPGRADE_TO_LUCENE_10_0_0 = def(9_000_0_00, Version.LUCENE_10_0_0);
+ public static final IndexVersion LOGSDB_DEFAULT_IGNORE_DYNAMIC_BEYOND_LIMIT = def(9_001_0_00, Version.LUCENE_10_0_0);
+ public static final IndexVersion TIME_BASED_K_ORDERED_DOC_ID = def(9_002_0_00, Version.LUCENE_10_0_0);
+ public static final IndexVersion DEPRECATE_SOURCE_MODE_MAPPER = def(9_003_0_00, Version.LUCENE_10_0_0);
+ public static final IndexVersion USE_SYNTHETIC_SOURCE_FOR_RECOVERY = def(9_004_0_00, Version.LUCENE_10_0_0);
+ public static final IndexVersion INFERENCE_METADATA_FIELDS = def(9_005_0_00, Version.LUCENE_10_0_0);
+ public static final IndexVersion LOGSB_OPTIONAL_SORTING_ON_HOST_NAME = def(9_006_0_00, Version.LUCENE_10_0_0);
+ public static final IndexVersion SOURCE_MAPPER_MODE_ATTRIBUTE_NOOP = def(9_007_0_00, Version.LUCENE_10_0_0);
+ public static final IndexVersion HOSTNAME_DOC_VALUES_SPARSE_INDEX = def(9_008_0_00, Version.LUCENE_10_0_0);
+ public static final IndexVersion UPGRADE_TO_LUCENE_10_1_0 = def(9_009_0_00, Version.LUCENE_10_1_0);
/*
* STOP! READ THIS FIRST! No, really,
* ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _
@@ -160,17 +159,17 @@ private static Version parseUnchecked(String version) {
* To add a new index version, add a new constant at the bottom of the list, above this comment. Don't add other lines,
* comments, etc. The version id has the following layout:
*
- * M_NNN_SS_P
+ * M_NNN_S_PP
*
* M - The major version of Elasticsearch
* NNN - The server version part
- * SS - The serverless version part. It should always be 00 here, it is used by serverless only.
- * P - The patch version part
+ * S - The subsidiary version part. It should always be 0 here, it is only used in subsidiary repositories.
+ * PP - The patch version part
*
* To determine the id of the next IndexVersion constant, do the following:
* - Use the same major version, unless bumping majors
* - Bump the server version part by 1, unless creating a patch version
- * - Leave the serverless part as 00
+ * - Leave the subsidiary part as 0
* - Bump the patch part if creating a patch version
*
* If a patch version is created, it should be placed sorted among the other existing constants.
@@ -250,10 +249,6 @@ static NavigableMap getAllVersionIds(Class> cls) {
return Collections.unmodifiableNavigableMap(builder);
}
- static Collection getAllWriteVersions() {
- return VERSION_IDS.values().stream().filter(v -> v.onOrAfter(IndexVersions.MINIMUM_COMPATIBLE)).collect(Collectors.toSet());
- }
-
static Collection getAllVersions() {
return VERSION_IDS.values();
}
diff --git a/server/src/main/java/org/elasticsearch/index/translog/TranslogWriter.java b/server/src/main/java/org/elasticsearch/index/translog/TranslogWriter.java
index 8cf631b660b1e..36b6709661017 100644
--- a/server/src/main/java/org/elasticsearch/index/translog/TranslogWriter.java
+++ b/server/src/main/java/org/elasticsearch/index/translog/TranslogWriter.java
@@ -29,6 +29,7 @@
import org.elasticsearch.index.engine.TranslogOperationAsserter;
import org.elasticsearch.index.seqno.SequenceNumbers;
import org.elasticsearch.index.shard.ShardId;
+import org.elasticsearch.search.lookup.Source;
import java.io.Closeable;
import java.io.IOException;
@@ -298,8 +299,10 @@ private synchronized boolean assertNoSeqNumberConflict(long seqNo, BytesReferenc
+ "], with different data. "
+ "prvOp ["
+ prvOp
+ + (prvOp instanceof Translog.Index index ? " source: " + Source.fromBytes(index.source()).source() : "")
+ "], newOp ["
+ newOp
+ + (newOp instanceof Translog.Index index ? " source: " + Source.fromBytes(index.source()).source() : "")
+ "]",
previous.v2()
);
diff --git a/server/src/main/java/org/elasticsearch/internal/VersionExtension.java b/server/src/main/java/org/elasticsearch/internal/VersionExtension.java
index 5a6c7c1f3671d..fc947738c9e33 100644
--- a/server/src/main/java/org/elasticsearch/internal/VersionExtension.java
+++ b/server/src/main/java/org/elasticsearch/internal/VersionExtension.java
@@ -12,16 +12,16 @@
import org.elasticsearch.TransportVersion;
import org.elasticsearch.index.IndexVersion;
-import java.util.List;
+import java.util.Collection;
/**
* Allows plugging in current version elements.
*/
public interface VersionExtension {
/**
- * Returns list of {@link TransportVersion} defined by extension
+ * Returns additional {@link TransportVersion} defined by extension
*/
- List getTransportVersions();
+ Collection getTransportVersions();
/**
* Returns the {@link IndexVersion} that Elasticsearch should use.
diff --git a/server/src/test/java/org/elasticsearch/TransportVersionTests.java b/server/src/test/java/org/elasticsearch/TransportVersionTests.java
index 00429035f97d3..9b02b66583e78 100644
--- a/server/src/test/java/org/elasticsearch/TransportVersionTests.java
+++ b/server/src/test/java/org/elasticsearch/TransportVersionTests.java
@@ -13,16 +13,13 @@
import org.elasticsearch.test.TransportVersionUtils;
import java.lang.reflect.Modifier;
-import java.util.Collections;
-import java.util.List;
import java.util.Set;
-import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith;
-import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.lessThan;
@@ -70,13 +67,11 @@ public static class DuplicatedIdFakeVersion {
public void testStaticTransportVersionChecks() {
assertThat(
TransportVersions.collectAllVersionIdsDefinedInClass(CorrectFakeVersion.class),
- equalTo(
- List.of(
- CorrectFakeVersion.V_0_000_002,
- CorrectFakeVersion.V_0_000_003,
- CorrectFakeVersion.V_0_000_004,
- CorrectFakeVersion.V_0_00_01
- )
+ contains(
+ CorrectFakeVersion.V_0_000_002,
+ CorrectFakeVersion.V_0_000_003,
+ CorrectFakeVersion.V_0_000_004,
+ CorrectFakeVersion.V_0_00_01
)
);
AssertionError e = expectThrows(
@@ -162,15 +157,15 @@ public void testMax() {
}
public void testIsPatchFrom() {
- TransportVersion patchVersion = TransportVersion.fromId(8_800_00_4);
- assertThat(TransportVersion.fromId(8_799_00_0).isPatchFrom(patchVersion), is(false));
- assertThat(TransportVersion.fromId(8_799_00_9).isPatchFrom(patchVersion), is(false));
- assertThat(TransportVersion.fromId(8_800_00_0).isPatchFrom(patchVersion), is(false));
- assertThat(TransportVersion.fromId(8_800_00_3).isPatchFrom(patchVersion), is(false));
- assertThat(TransportVersion.fromId(8_800_00_4).isPatchFrom(patchVersion), is(true));
- assertThat(TransportVersion.fromId(8_800_00_9).isPatchFrom(patchVersion), is(true));
- assertThat(TransportVersion.fromId(8_800_01_0).isPatchFrom(patchVersion), is(false));
- assertThat(TransportVersion.fromId(8_801_00_0).isPatchFrom(patchVersion), is(false));
+ TransportVersion patchVersion = TransportVersion.fromId(8_800_0_04);
+ assertThat(TransportVersion.fromId(8_799_0_00).isPatchFrom(patchVersion), is(false));
+ assertThat(TransportVersion.fromId(8_799_0_09).isPatchFrom(patchVersion), is(false));
+ assertThat(TransportVersion.fromId(8_800_0_00).isPatchFrom(patchVersion), is(false));
+ assertThat(TransportVersion.fromId(8_800_0_03).isPatchFrom(patchVersion), is(false));
+ assertThat(TransportVersion.fromId(8_800_0_04).isPatchFrom(patchVersion), is(true));
+ assertThat(TransportVersion.fromId(8_800_0_49).isPatchFrom(patchVersion), is(true));
+ assertThat(TransportVersion.fromId(8_800_1_00).isPatchFrom(patchVersion), is(false));
+ assertThat(TransportVersion.fromId(8_801_0_00).isPatchFrom(patchVersion), is(false));
}
public void testVersionConstantPresent() {
@@ -185,7 +180,20 @@ public void testVersionConstantPresent() {
}
public void testCURRENTIsLatest() {
- assertThat(Collections.max(TransportVersion.getAllVersions()), is(TransportVersion.current()));
+ assertThat(TransportVersion.getAllVersions().getLast(), is(TransportVersion.current()));
+ }
+
+ public void testPatchVersionsStillAvailable() {
+ for (TransportVersion tv : TransportVersion.getAllVersions()) {
+ if (tv.onOrAfter(TransportVersions.V_8_9_X) && (tv.id() % 100) > 90) {
+ fail(
+ "Transport version "
+ + tv
+ + " is nearing the limit of available patch numbers."
+ + " Please inform the Core/Infra team that isPatchFrom may need to be modified"
+ );
+ }
+ }
}
public void testToReleaseVersion() {
@@ -199,40 +207,4 @@ public void testToString() {
assertEquals("2000099", TransportVersion.fromId(2_00_00_99).toString());
assertEquals("5000099", TransportVersion.fromId(5_00_00_99).toString());
}
-
- /**
- * Until 9.0 bumps its transport version to 9_000_00_0, all transport changes must be backported to 8.x.
- * This test ensures transport versions are dense, so that we have confidence backports have not been missed.
- * Note that it does not ensure patches are not missed, but it should catch the majority of misordered
- * or missing transport versions.
- */
- public void testDenseTransportVersions() {
- Set missingVersions = new TreeSet<>();
- TransportVersion previous = null;
- for (var tv : TransportVersion.getAllVersions()) {
- if (tv.before(TransportVersions.V_8_16_0)) {
- continue;
- }
- if (previous == null) {
- previous = tv;
- continue;
- }
-
- if (previous.id() + 1000 < tv.id()) {
- int nextId = previous.id();
- do {
- nextId = (nextId + 1000) / 1000 * 1000;
- missingVersions.add(nextId);
- } while (nextId + 1000 < tv.id());
- }
- previous = tv;
- }
- if (missingVersions.isEmpty() == false) {
- StringBuilder msg = new StringBuilder("Missing transport versions:\n");
- for (Integer id : missingVersions) {
- msg.append(" " + id + "\n");
- }
- fail(msg.toString());
- }
- }
}
diff --git a/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesNodeResponseTests.java b/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesNodeResponseTests.java
index c99c671c69148..fa57431cc582a 100644
--- a/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesNodeResponseTests.java
+++ b/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesNodeResponseTests.java
@@ -20,7 +20,6 @@
import org.elasticsearch.test.AbstractWireSerializingTestCase;
import org.elasticsearch.test.TransportVersionUtils;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -37,7 +36,6 @@
import static org.elasticsearch.action.fieldcaps.FieldCapabilitiesIndexResponseTests.randomMappingHashToIndices;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
-import static org.hamcrest.Matchers.nullValue;
public class FieldCapabilitiesNodeResponseTests extends AbstractWireSerializingTestCase {
@@ -145,48 +143,6 @@ public void testSerializeNodeResponseBetweenNewNodes() throws Exception {
}
}
- public void testSerializeNodeResponseBetweenOldNodes() throws IOException {
- final TransportVersion minCompactVersion = TransportVersions.MINIMUM_COMPATIBLE;
- assertTrue("Remove this test once minCompactVersion >= 8.2.0", minCompactVersion.before(TransportVersions.V_8_2_0));
- List indexResponses = CollectionUtils.concatLists(
- randomIndexResponsesWithMappingHash(randomMappingHashToIndices()),
- randomIndexResponsesWithoutMappingHash()
- );
- Randomness.shuffle(indexResponses);
- FieldCapabilitiesNodeResponse inResponse = randomNodeResponse(indexResponses);
- TransportVersion version = TransportVersionUtils.randomVersionBetween(
- random(),
- minCompactVersion,
- TransportVersionUtils.getPreviousVersion(TransportVersions.V_8_2_0)
- );
- final FieldCapabilitiesNodeResponse outResponse = copyInstance(inResponse, version);
- assertThat(outResponse.getFailures().keySet(), equalTo(inResponse.getFailures().keySet()));
- assertThat(outResponse.getUnmatchedShardIds(), equalTo(inResponse.getUnmatchedShardIds()));
- final List inList = inResponse.getIndexResponses();
- final List outList = outResponse.getIndexResponses();
- assertThat(outList, hasSize(inList.size()));
- for (int i = 0; i < inList.size(); i++) {
- assertThat("Responses between old nodes don't have mapping hash", outList.get(i).getIndexMappingHash(), nullValue());
- assertThat(outList.get(i).getIndexName(), equalTo(inList.get(i).getIndexName()));
- assertThat(outList.get(i).canMatch(), equalTo(inList.get(i).canMatch()));
- Map outCap = outList.get(i).get();
- Map inCap = inList.get(i).get();
- if (version.onOrAfter(TransportVersions.V_8_0_0)) {
- assertThat(outCap, equalTo(inCap));
- } else {
- // Exclude metric types which was introduced in 8.0
- assertThat(outCap.keySet(), equalTo(inCap.keySet()));
- for (String field : outCap.keySet()) {
- assertThat(outCap.get(field).name(), equalTo(inCap.get(field).name()));
- assertThat(outCap.get(field).type(), equalTo(inCap.get(field).type()));
- assertThat(outCap.get(field).isSearchable(), equalTo(inCap.get(field).isSearchable()));
- assertThat(outCap.get(field).isAggregatable(), equalTo(inCap.get(field).isAggregatable()));
- assertThat(outCap.get(field).meta(), equalTo(inCap.get(field).meta()));
- }
- }
- }
- }
-
private static FieldCapabilitiesNodeResponse randomNodeResponse(List indexResponses) {
int numUnmatched = randomIntBetween(0, 3);
final Set unmatchedShardIds = new HashSet<>();
diff --git a/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponseTests.java b/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponseTests.java
index 6ea4a1d3dc46b..ceb84e4b2a0d9 100644
--- a/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponseTests.java
+++ b/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponseTests.java
@@ -40,7 +40,6 @@
import static org.elasticsearch.action.fieldcaps.FieldCapabilitiesIndexResponseTests.randomMappingHashToIndices;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
-import static org.hamcrest.Matchers.nullValue;
public class FieldCapabilitiesResponseTests extends AbstractWireSerializingTestCase {
@@ -198,48 +197,4 @@ public void testSerializeCCSResponseBetweenNewClusters() throws Exception {
}
}
}
-
- public void testSerializeCCSResponseBetweenOldClusters() throws IOException {
- TransportVersion minCompactVersion = TransportVersions.MINIMUM_COMPATIBLE;
- assertTrue("Remove this test once minCompactVersion >= 8.2.0", minCompactVersion.before(TransportVersions.V_8_2_0));
- List indexResponses = CollectionUtils.concatLists(
- randomIndexResponsesWithMappingHash(randomMappingHashToIndices()),
- randomIndexResponsesWithoutMappingHash()
- );
- Randomness.shuffle(indexResponses);
- FieldCapabilitiesResponse inResponse = randomCCSResponse(indexResponses);
- TransportVersion version = TransportVersionUtils.randomVersionBetween(
- random(),
- minCompactVersion,
- TransportVersionUtils.getPreviousVersion(TransportVersions.V_8_2_0)
- );
- final FieldCapabilitiesResponse outResponse = copyInstance(inResponse, version);
- assertThat(
- outResponse.getFailures().stream().flatMap(f -> Arrays.stream(f.getIndices())).toList(),
- equalTo(inResponse.getFailures().stream().flatMap(f -> Arrays.stream(f.getIndices())).toList())
- );
- final List inList = inResponse.getIndexResponses();
- final List outList = outResponse.getIndexResponses();
- assertThat(outList, hasSize(inList.size()));
- for (int i = 0; i < inList.size(); i++) {
- assertThat("Responses between old clusters don't have mapping hash", outList.get(i).getIndexMappingHash(), nullValue());
- assertThat(outList.get(i).getIndexName(), equalTo(inList.get(i).getIndexName()));
- assertThat(outList.get(i).canMatch(), equalTo(inList.get(i).canMatch()));
- Map outCap = outList.get(i).get();
- Map inCap = inList.get(i).get();
- if (version.onOrAfter(TransportVersions.V_8_0_0)) {
- assertThat(outCap, equalTo(inCap));
- } else {
- // Exclude metric types which was introduced in 8.0
- assertThat(outCap.keySet(), equalTo(inCap.keySet()));
- for (String field : outCap.keySet()) {
- assertThat(outCap.get(field).name(), equalTo(inCap.get(field).name()));
- assertThat(outCap.get(field).type(), equalTo(inCap.get(field).type()));
- assertThat(outCap.get(field).isSearchable(), equalTo(inCap.get(field).isSearchable()));
- assertThat(outCap.get(field).isAggregatable(), equalTo(inCap.get(field).isAggregatable()));
- assertThat(outCap.get(field).meta(), equalTo(inCap.get(field).meta()));
- }
- }
- }
- }
}
diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchQueryThenFetchAsyncActionTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchQueryThenFetchAsyncActionTests.java
index f005f862720ff..661a9fd8c854c 100644
--- a/server/src/test/java/org/elasticsearch/action/search/SearchQueryThenFetchAsyncActionTests.java
+++ b/server/src/test/java/org/elasticsearch/action/search/SearchQueryThenFetchAsyncActionTests.java
@@ -220,7 +220,6 @@ protected void run() {
assertFalse(canReturnNullResponse.get());
assertThat(numWithTopDocs.get(), equalTo(0));
} else {
- assertTrue(canReturnNullResponse.get());
if (withCollapse) {
assertThat(numWithTopDocs.get(), equalTo(0));
} else {
diff --git a/server/src/test/java/org/elasticsearch/cluster/action/shard/ShardStateActionTests.java b/server/src/test/java/org/elasticsearch/cluster/action/shard/ShardStateActionTests.java
index 3c680d891ff13..75cc99e4c280e 100644
--- a/server/src/test/java/org/elasticsearch/cluster/action/shard/ShardStateActionTests.java
+++ b/server/src/test/java/org/elasticsearch/cluster/action/shard/ShardStateActionTests.java
@@ -612,11 +612,7 @@ public void testStartedShardEntrySerializationWithOlderTransportVersion() throws
final String allocationId = randomRealisticUnicodeOfCodepointLengthBetween(10, 100);
final long primaryTerm = randomIntBetween(0, 100);
final String message = randomRealisticUnicodeOfCodepointLengthBetween(10, 100);
- final TransportVersion version = randomFrom(
- getFirstVersion(),
- getPreviousVersion(TransportVersions.MINIMUM_COMPATIBLE),
- getPreviousVersion(TransportVersions.V_8_15_0)
- );
+ final TransportVersion version = randomFrom(getFirstVersion(), getPreviousVersion(TransportVersions.V_8_15_0));
final ShardLongFieldRange timestampRange = ShardLongFieldRangeWireTests.randomRange();
final ShardLongFieldRange eventIngestedRange = ShardLongFieldRangeWireTests.randomRange();
var startedShardEntry = new StartedShardEntry(shardId, allocationId, primaryTerm, message, timestampRange, eventIngestedRange);
diff --git a/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodeTests.java b/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodeTests.java
index a91cef576df33..744a12d5ab6e0 100644
--- a/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodeTests.java
+++ b/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodeTests.java
@@ -31,8 +31,6 @@
import static java.util.Collections.emptySet;
import static org.elasticsearch.test.NodeRoles.nonRemoteClusterClientNode;
import static org.elasticsearch.test.NodeRoles.remoteClusterClientNode;
-import static org.elasticsearch.test.TransportVersionUtils.getPreviousVersion;
-import static org.elasticsearch.test.TransportVersionUtils.randomVersionBetween;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
@@ -274,39 +272,5 @@ public void testDiscoveryNodeMinReadOnlyVersionSerialization() throws Exception
}
}
}
-
- {
- var oldVersion = randomVersionBetween(
- random(),
- TransportVersions.MINIMUM_COMPATIBLE,
- getPreviousVersion(TransportVersions.NODE_VERSION_INFORMATION_WITH_MIN_READ_ONLY_INDEX_VERSION)
- );
- try (var out = new BytesStreamOutput()) {
- out.setTransportVersion(oldVersion);
- node.writeTo(out);
-
- try (var in = StreamInput.wrap(out.bytes().array())) {
- in.setTransportVersion(oldVersion);
-
- var deserialized = new DiscoveryNode(in);
- assertThat(deserialized.getId(), equalTo(node.getId()));
- assertThat(deserialized.getAddress(), equalTo(node.getAddress()));
- assertThat(deserialized.getMinIndexVersion(), equalTo(node.getMinIndexVersion()));
- assertThat(deserialized.getMaxIndexVersion(), equalTo(node.getMaxIndexVersion()));
- assertThat(deserialized.getMinReadOnlyIndexVersion(), equalTo(node.getMinIndexVersion()));
- assertThat(
- deserialized.getVersionInformation(),
- equalTo(
- new VersionInformation(
- node.getBuildVersion(),
- node.getMinIndexVersion(),
- node.getMinIndexVersion(),
- node.getMaxIndexVersion()
- )
- )
- );
- }
- }
- }
}
}
diff --git a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/allocator/AllocationBalancingRoundSummaryServiceTests.java b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/allocator/AllocationBalancingRoundSummaryServiceTests.java
new file mode 100644
index 0000000000000..337fad01f905b
--- /dev/null
+++ b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/allocator/AllocationBalancingRoundSummaryServiceTests.java
@@ -0,0 +1,256 @@
+/*
+ * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+package org.elasticsearch.cluster.routing.allocation.allocator;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.elasticsearch.common.settings.ClusterSettings;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.util.concurrent.DeterministicTaskQueue;
+import org.elasticsearch.core.TimeValue;
+import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.test.MockLog;
+import org.elasticsearch.threadpool.ThreadPool;
+import org.junit.Before;
+
+public class AllocationBalancingRoundSummaryServiceTests extends ESTestCase {
+ private static final Logger logger = LogManager.getLogger(AllocationBalancingRoundSummaryServiceTests.class);
+
+ private static final String BALANCING_SUMMARY_MSG_PREFIX = "Balancing round summaries:*";
+
+ final Settings enabledSummariesSettings = Settings.builder()
+ .put(AllocationBalancingRoundSummaryService.ENABLE_BALANCER_ROUND_SUMMARIES_SETTING.getKey(), true)
+ .build();
+ final Settings disabledDefaultEmptySettings = Settings.builder().build();
+ final Settings enabledButNegativeIntervalSettings = Settings.builder()
+ .put(AllocationBalancingRoundSummaryService.ENABLE_BALANCER_ROUND_SUMMARIES_SETTING.getKey(), true)
+ .put(AllocationBalancingRoundSummaryService.BALANCER_ROUND_SUMMARIES_LOG_INTERVAL_SETTING.getKey(), TimeValue.MINUS_ONE)
+ .build();
+
+ ClusterSettings enabledClusterSettings = new ClusterSettings(enabledSummariesSettings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
+ ClusterSettings disabledDefaultEmptyClusterSettings = new ClusterSettings(
+ disabledDefaultEmptySettings,
+ ClusterSettings.BUILT_IN_CLUSTER_SETTINGS
+ );
+ ClusterSettings enabledButNegativeIntervalClusterSettings = new ClusterSettings(
+ enabledButNegativeIntervalSettings,
+ ClusterSettings.BUILT_IN_CLUSTER_SETTINGS
+ );
+
+ // Construction parameters for the service.
+
+ DeterministicTaskQueue deterministicTaskQueue;
+ ThreadPool testThreadPool;
+
+ @Before
+ public void setUpThreadPool() {
+ deterministicTaskQueue = new DeterministicTaskQueue();
+ testThreadPool = deterministicTaskQueue.getThreadPool();
+ }
+
+ /**
+ * Test that the service is disabled and no logging occurs when
+ * {@link AllocationBalancingRoundSummaryService#ENABLE_BALANCER_ROUND_SUMMARIES_SETTING} defaults to false.
+ */
+ public void testServiceDisabledByDefault() {
+ var service = new AllocationBalancingRoundSummaryService(testThreadPool, disabledDefaultEmptyClusterSettings);
+
+ try (var mockLog = MockLog.capture(AllocationBalancingRoundSummaryService.class)) {
+ /**
+ * Add a summary and check it is not logged.
+ */
+
+ service.addBalancerRoundSummary(new BalancingRoundSummary(50));
+ service.verifyNumberOfSummaries(0); // when summaries are disabled, summaries are not retained when added.
+ mockLog.addExpectation(
+ new MockLog.UnseenEventExpectation(
+ "Running balancer summary logging",
+ AllocationBalancingRoundSummaryService.class.getName(),
+ Level.INFO,
+ "*"
+ )
+ );
+
+ if (deterministicTaskQueue.hasDeferredTasks()) {
+ deterministicTaskQueue.advanceTime();
+ }
+ deterministicTaskQueue.runAllRunnableTasks();
+ mockLog.awaitAllExpectationsMatched();
+ service.verifyNumberOfSummaries(0);
+ }
+ }
+
+ public void testEnabledService() {
+ var service = new AllocationBalancingRoundSummaryService(testThreadPool, enabledClusterSettings);
+
+ try (var mockLog = MockLog.capture(AllocationBalancingRoundSummaryService.class)) {
+ /**
+ * Add a summary and check the service logs a report on it.
+ */
+
+ service.addBalancerRoundSummary(new BalancingRoundSummary(50));
+ service.verifyNumberOfSummaries(1);
+ mockLog.addExpectation(
+ new MockLog.SeenEventExpectation(
+ "Running balancer summary logging",
+ AllocationBalancingRoundSummaryService.class.getName(),
+ Level.INFO,
+ BALANCING_SUMMARY_MSG_PREFIX
+ )
+ );
+
+ deterministicTaskQueue.advanceTime();
+ deterministicTaskQueue.runAllRunnableTasks();
+ mockLog.awaitAllExpectationsMatched();
+ service.verifyNumberOfSummaries(0);
+
+ /**
+ * Add a second summary, check for more logging.
+ */
+
+ service.addBalancerRoundSummary(new BalancingRoundSummary(200));
+ service.verifyNumberOfSummaries(1);
+ mockLog.addExpectation(
+ new MockLog.SeenEventExpectation(
+ "Running balancer summary logging a second time",
+ AllocationBalancingRoundSummaryService.class.getName(),
+ Level.INFO,
+ BALANCING_SUMMARY_MSG_PREFIX
+ )
+ );
+
+ deterministicTaskQueue.advanceTime();
+ deterministicTaskQueue.runAllRunnableTasks();
+ mockLog.awaitAllExpectationsMatched();
+ service.verifyNumberOfSummaries(0);
+ }
+ }
+
+ /**
+ * The service should combine multiple summaries together into a single report when multiple summaries were added since the last report.
+ */
+ public void testCombinedSummary() {
+ var service = new AllocationBalancingRoundSummaryService(testThreadPool, enabledClusterSettings);
+
+ try (var mockLog = MockLog.capture(AllocationBalancingRoundSummaryService.class)) {
+ service.addBalancerRoundSummary(new BalancingRoundSummary(50));
+ service.addBalancerRoundSummary(new BalancingRoundSummary(100));
+ service.verifyNumberOfSummaries(2);
+ mockLog.addExpectation(
+ new MockLog.SeenEventExpectation(
+ "Running balancer summary logging of combined summaries",
+ AllocationBalancingRoundSummaryService.class.getName(),
+ Level.INFO,
+ "*150*"
+ )
+ );
+
+ deterministicTaskQueue.advanceTime();
+ deterministicTaskQueue.runAllRunnableTasks();
+ mockLog.awaitAllExpectationsMatched();
+ service.verifyNumberOfSummaries(0);
+ }
+ }
+
+ /**
+ * The service shouldn't log anything when there haven't been any summaries added since the last report.
+ */
+ public void testNoSummariesToReport() {
+ var service = new AllocationBalancingRoundSummaryService(testThreadPool, enabledClusterSettings);
+
+ try (var mockLog = MockLog.capture(AllocationBalancingRoundSummaryService.class)) {
+ /**
+ * First add some summaries to report, ensuring that the logging is active.
+ */
+
+ service.addBalancerRoundSummary(new BalancingRoundSummary(50));
+ service.verifyNumberOfSummaries(1);
+ mockLog.addExpectation(
+ new MockLog.SeenEventExpectation(
+ "Running balancer summary logging of combined summaries",
+ AllocationBalancingRoundSummaryService.class.getName(),
+ Level.INFO,
+ BALANCING_SUMMARY_MSG_PREFIX
+ )
+ );
+
+ deterministicTaskQueue.advanceTime();
+ deterministicTaskQueue.runAllRunnableTasks();
+ mockLog.awaitAllExpectationsMatched();
+ service.verifyNumberOfSummaries(0);
+
+ /**
+ * Now check that there are no further log messages because there were no further summaries added.
+ */
+
+ mockLog.addExpectation(
+ new MockLog.UnseenEventExpectation(
+ "No balancer round summary to log",
+ AllocationBalancingRoundSummaryService.class.getName(),
+ Level.INFO,
+ "*"
+ )
+ );
+
+ deterministicTaskQueue.advanceTime();
+ deterministicTaskQueue.runAllRunnableTasks();
+ mockLog.awaitAllExpectationsMatched();
+ service.verifyNumberOfSummaries(0);
+ }
+ }
+
+ /**
+ * Test that the service is disabled by setting {@link AllocationBalancingRoundSummaryService#ENABLE_BALANCER_ROUND_SUMMARIES_SETTING}
+ * to false.
+ */
+ public void testEnableAndThenDisableService() {
+ var disabledSettingsUpdate = Settings.builder()
+ .put(AllocationBalancingRoundSummaryService.ENABLE_BALANCER_ROUND_SUMMARIES_SETTING.getKey(), false)
+ .build();
+ ClusterSettings clusterSettings = new ClusterSettings(enabledSummariesSettings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
+ var service = new AllocationBalancingRoundSummaryService(testThreadPool, clusterSettings);
+
+ try (var mockLog = MockLog.capture(AllocationBalancingRoundSummaryService.class)) {
+ /**
+ * Add some summaries, but then disable the service before logging occurs. Disabling the service should drain and discard any
+ * summaries waiting to be reported.
+ */
+
+ service.addBalancerRoundSummary(new BalancingRoundSummary(50));
+ service.verifyNumberOfSummaries(1);
+
+ clusterSettings.applySettings(disabledSettingsUpdate);
+ service.verifyNumberOfSummaries(0);
+
+ /**
+ * Verify that any additional summaries are not retained, since the service is disabled.
+ */
+
+ service.addBalancerRoundSummary(new BalancingRoundSummary(50));
+ service.verifyNumberOfSummaries(0);
+
+ // Check that the service never logged anything.
+ mockLog.addExpectation(
+ new MockLog.UnseenEventExpectation(
+ "Running balancer summary logging",
+ AllocationBalancingRoundSummaryService.class.getName(),
+ Level.INFO,
+ "*"
+ )
+ );
+ deterministicTaskQueue.advanceTime();
+ deterministicTaskQueue.runAllRunnableTasks();
+ mockLog.awaitAllExpectationsMatched();
+ service.verifyNumberOfSummaries(0);
+ }
+ }
+
+}
diff --git a/server/src/test/java/org/elasticsearch/index/mapper/MappingParserTests.java b/server/src/test/java/org/elasticsearch/index/mapper/MappingParserTests.java
index b87ab09c530d6..4b674cf1985b2 100644
--- a/server/src/test/java/org/elasticsearch/index/mapper/MappingParserTests.java
+++ b/server/src/test/java/org/elasticsearch/index/mapper/MappingParserTests.java
@@ -22,7 +22,6 @@
import org.elasticsearch.index.similarity.SimilarityService;
import org.elasticsearch.indices.IndicesModule;
import org.elasticsearch.script.ScriptService;
-import org.elasticsearch.test.TransportVersionUtils;
import org.elasticsearch.test.index.IndexVersionUtils;
import org.elasticsearch.xcontent.XContentBuilder;
import org.hamcrest.CoreMatchers;
@@ -327,11 +326,7 @@ public void testBlankFieldNameBefore8_6_0() throws Exception {
IndexVersions.MINIMUM_READONLY_COMPATIBLE,
IndexVersions.V_8_5_0
);
- TransportVersion transportVersion = TransportVersionUtils.randomVersionBetween(
- random(),
- TransportVersions.MINIMUM_COMPATIBLE,
- TransportVersions.V_8_5_0
- );
+ TransportVersion transportVersion = TransportVersions.V_8_5_0;
{
XContentBuilder builder = mapping(b -> b.startObject(" ").field("type", randomFieldType()).endObject());
MappingParser mappingParser = createMappingParser(Settings.EMPTY, version, transportVersion);
diff --git a/test/framework/src/main/java/org/elasticsearch/index/KnownIndexVersions.java b/test/framework/src/main/java/org/elasticsearch/index/KnownIndexVersions.java
index 4f559a5f3eaef..8aea7a5713cf1 100644
--- a/test/framework/src/main/java/org/elasticsearch/index/KnownIndexVersions.java
+++ b/test/framework/src/main/java/org/elasticsearch/index/KnownIndexVersions.java
@@ -9,7 +9,9 @@
package org.elasticsearch.index;
-import java.util.List;
+import java.util.Collections;
+import java.util.NavigableSet;
+import java.util.TreeSet;
/**
* Provides access to all known index versions
@@ -18,10 +20,12 @@ public class KnownIndexVersions {
/**
* A sorted list of all known index versions
*/
- public static final List ALL_VERSIONS = List.copyOf(IndexVersions.getAllVersions());
+ public static final NavigableSet ALL_VERSIONS = Collections.unmodifiableNavigableSet(
+ new TreeSet<>(IndexVersions.getAllVersions())
+ );
/**
* A sorted list of all known index versions that can be written to
*/
- public static final List ALL_WRITE_VERSIONS = List.copyOf(IndexVersions.getAllWriteVersions());
+ public static final NavigableSet ALL_WRITE_VERSIONS = ALL_VERSIONS.tailSet(IndexVersions.MINIMUM_COMPATIBLE, true);
}
diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractBWCSerializationTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractBWCSerializationTestCase.java
index d931340365cd6..22044e079018b 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/AbstractBWCSerializationTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractBWCSerializationTestCase.java
@@ -14,7 +14,7 @@
import org.elasticsearch.xcontent.ToXContent;
import java.io.IOException;
-import java.util.List;
+import java.util.Collection;
import static org.elasticsearch.test.BWCVersions.DEFAULT_BWC_VERSIONS;
@@ -28,7 +28,7 @@ public abstract class AbstractBWCSerializationTestCase bwcVersions() {
+ protected Collection bwcVersions() {
return DEFAULT_BWC_VERSIONS;
}
diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractXContentTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractXContentTestCase.java
index 24b853c8f6ddb..cc35f63d289eb 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/AbstractXContentTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractXContentTestCase.java
@@ -145,21 +145,8 @@ private XContentTester(
public void test() throws IOException {
for (int runs = 0; runs < numberOfTestRuns; runs++) {
XContentType xContentType = randomFrom(XContentType.values()).canonical();
- T testInstance = null;
+ T testInstance = instanceSupplier.apply(xContentType);
try {
- if (xContentType.equals(XContentType.YAML)) {
- testInstance = randomValueOtherThanMany(instance -> {
- // unicode character U+0085 (NEXT LINE (NEL)) doesn't survive YAML round trip tests (see #97716)
- // get a new random instance if we detect this character in the xContent output
- try {
- return toXContent.apply(instance, xContentType).utf8ToString().contains("\u0085");
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }, () -> instanceSupplier.apply(xContentType));
- } else {
- testInstance = instanceSupplier.apply(xContentType);
- }
BytesReference originalXContent = toXContent.apply(testInstance, xContentType);
BytesReference shuffledContent = insertRandomFieldsAndShuffle(
originalXContent,
@@ -186,9 +173,7 @@ public void test() throws IOException {
dispose.accept(parsed);
}
} finally {
- if (testInstance != null) {
- dispose.accept(testInstance);
- }
+ dispose.accept(testInstance);
}
}
}
diff --git a/test/framework/src/main/java/org/elasticsearch/test/BWCVersions.java b/test/framework/src/main/java/org/elasticsearch/test/BWCVersions.java
index 49859071b03cf..1cd0d0ddc4cd2 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/BWCVersions.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/BWCVersions.java
@@ -12,17 +12,14 @@
import org.elasticsearch.TransportVersion;
import org.elasticsearch.TransportVersions;
-import java.util.Collections;
-import java.util.List;
+import java.util.NavigableSet;
public final class BWCVersions {
private BWCVersions() {}
- public static List getAllBWCVersions() {
- List allVersions = TransportVersion.getAllVersions();
- int minCompatVersion = Collections.binarySearch(allVersions, TransportVersions.MINIMUM_COMPATIBLE);
- return allVersions.subList(minCompatVersion, allVersions.size());
+ public static NavigableSet getAllBWCVersions() {
+ return TransportVersionUtils.allReleasedVersions().tailSet(TransportVersions.MINIMUM_COMPATIBLE, true);
}
- public static final List DEFAULT_BWC_VERSIONS = getAllBWCVersions();
+ public static final NavigableSet DEFAULT_BWC_VERSIONS = getAllBWCVersions();
}
diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java
index a271c999a2ba7..227d7ca3046f8 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java
@@ -2659,6 +2659,15 @@ public static T expectThrows(Class expectedType, Reques
);
}
+ /**
+ * Checks a specific exception class with matched message is thrown by the given runnable, and returns it.
+ */
+ public static T expectThrows(Class expectedType, Matcher messageMatcher, ThrowingRunnable runnable) {
+ var e = expectThrows(expectedType, runnable);
+ assertThat(e.getMessage(), messageMatcher);
+ return e;
+ }
+
/**
* Same as {@link #runInParallel(int, IntConsumer)} but also attempts to start all tasks at the same time by blocking execution on a
* barrier until all threads are started and ready to execute their task.
diff --git a/test/framework/src/main/java/org/elasticsearch/test/TransportVersionUtils.java b/test/framework/src/main/java/org/elasticsearch/test/TransportVersionUtils.java
index 0c7274a36b49a..9c7114425b8db 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/TransportVersionUtils.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/TransportVersionUtils.java
@@ -14,15 +14,23 @@
import org.elasticsearch.core.Nullable;
import java.util.Collections;
-import java.util.List;
+import java.util.NavigableSet;
import java.util.Random;
import java.util.Set;
+import java.util.TreeSet;
import java.util.stream.Collectors;
+import static org.apache.lucene.tests.util.LuceneTestCase.random;
+
public class TransportVersionUtils {
+
+ private static final NavigableSet RELEASED_VERSIONS = Collections.unmodifiableNavigableSet(
+ new TreeSet<>(TransportVersion.getAllVersions())
+ );
+
/** Returns all released versions */
- public static List allReleasedVersions() {
- return TransportVersion.getAllVersions();
+ public static NavigableSet allReleasedVersions() {
+ return RELEASED_VERSIONS;
}
/** Returns the oldest known {@link TransportVersion} */
@@ -32,7 +40,7 @@ public static TransportVersion getFirstVersion() {
/** Returns a random {@link TransportVersion} from all available versions. */
public static TransportVersion randomVersion() {
- return ESTestCase.randomFrom(allReleasedVersions());
+ return VersionUtils.randomFrom(random(), allReleasedVersions(), TransportVersion::fromId);
}
/** Returns a random {@link TransportVersion} from all available versions without the ignore set */
@@ -42,7 +50,7 @@ public static TransportVersion randomVersion(Set ignore) {
/** Returns a random {@link TransportVersion} from all available versions. */
public static TransportVersion randomVersion(Random random) {
- return allReleasedVersions().get(random.nextInt(allReleasedVersions().size()));
+ return VersionUtils.randomFrom(random, allReleasedVersions(), TransportVersion::fromId);
}
/** Returns a random {@link TransportVersion} between minVersion and maxVersion (inclusive). */
@@ -55,24 +63,21 @@ public static TransportVersion randomVersionBetween(
throw new IllegalArgumentException("maxVersion [" + maxVersion + "] cannot be less than minVersion [" + minVersion + "]");
}
- int minVersionIndex = 0;
- List allReleasedVersions = allReleasedVersions();
+ NavigableSet versions = allReleasedVersions();
if (minVersion != null) {
- minVersionIndex = Collections.binarySearch(allReleasedVersions, minVersion);
+ if (versions.contains(minVersion) == false) {
+ throw new IllegalArgumentException("minVersion [" + minVersion + "] does not exist.");
+ }
+ versions = versions.tailSet(minVersion, true);
}
- int maxVersionIndex = allReleasedVersions.size() - 1;
if (maxVersion != null) {
- maxVersionIndex = Collections.binarySearch(allReleasedVersions, maxVersion);
- }
- if (minVersionIndex < 0) {
- throw new IllegalArgumentException("minVersion [" + minVersion + "] does not exist.");
- } else if (maxVersionIndex < 0) {
- throw new IllegalArgumentException("maxVersion [" + maxVersion + "] does not exist.");
- } else {
- // minVersionIndex is inclusive so need to add 1 to this index
- int range = maxVersionIndex + 1 - minVersionIndex;
- return allReleasedVersions.get(minVersionIndex + random.nextInt(range));
+ if (versions.contains(maxVersion) == false) {
+ throw new IllegalArgumentException("maxVersion [" + maxVersion + "] does not exist.");
+ }
+ versions = versions.headSet(maxVersion, true);
}
+
+ return VersionUtils.randomFrom(random, versions, TransportVersion::fromId);
}
public static TransportVersion getPreviousVersion() {
@@ -82,16 +87,11 @@ public static TransportVersion getPreviousVersion() {
}
public static TransportVersion getPreviousVersion(TransportVersion version) {
- int place = Collections.binarySearch(allReleasedVersions(), version);
- if (place < 0) {
- // version does not exist - need the item before the index this version should be inserted
- place = -(place + 1);
- }
-
- if (place < 1) {
+ TransportVersion lower = allReleasedVersions().lower(version);
+ if (lower == null) {
throw new IllegalArgumentException("couldn't find any released versions before [" + version + "]");
}
- return allReleasedVersions().get(place - 1);
+ return lower;
}
public static TransportVersion getNextVersion(TransportVersion version) {
@@ -99,17 +99,8 @@ public static TransportVersion getNextVersion(TransportVersion version) {
}
public static TransportVersion getNextVersion(TransportVersion version, boolean createIfNecessary) {
- List allReleasedVersions = allReleasedVersions();
- int place = Collections.binarySearch(allReleasedVersions, version);
- if (place < 0) {
- // version does not exist - need the item at the index this version should be inserted
- place = -(place + 1);
- } else {
- // need the *next* version
- place++;
- }
-
- if (place < 0 || place >= allReleasedVersions.size()) {
+ TransportVersion higher = allReleasedVersions().higher(version);
+ if (higher == null) {
if (createIfNecessary) {
// create a new transport version one greater than specified
return new TransportVersion(version.id() + 1);
@@ -117,7 +108,7 @@ public static TransportVersion getNextVersion(TransportVersion version, boolean
throw new IllegalArgumentException("couldn't find any released versions after [" + version + "]");
}
}
- return allReleasedVersions.get(place);
+ return higher;
}
/** Returns a random {@code TransportVersion} that is compatible with {@link TransportVersion#current()} */
diff --git a/test/framework/src/main/java/org/elasticsearch/test/VersionUtils.java b/test/framework/src/main/java/org/elasticsearch/test/VersionUtils.java
index 8b7ab620774b9..311f032088f74 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/VersionUtils.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/VersionUtils.java
@@ -9,23 +9,31 @@
package org.elasticsearch.test;
+import com.carrotsearch.randomizedtesting.generators.RandomNumbers;
+
import org.elasticsearch.Build;
import org.elasticsearch.Version;
+import org.elasticsearch.common.VersionId;
import org.elasticsearch.core.Nullable;
+import java.util.Collections;
import java.util.List;
-import java.util.Optional;
+import java.util.NavigableSet;
import java.util.Random;
+import java.util.TreeSet;
+import java.util.function.IntFunction;
/** Utilities for selecting versions in tests */
public class VersionUtils {
- private static final List ALL_VERSIONS = Version.getDeclaredVersions(Version.class);
+ private static final NavigableSet ALL_VERSIONS = Collections.unmodifiableNavigableSet(
+ new TreeSet<>(Version.getDeclaredVersions(Version.class))
+ );
/**
* Returns an immutable, sorted list containing all versions, both released and unreleased.
*/
- public static List allVersions() {
+ public static NavigableSet allVersions() {
return ALL_VERSIONS;
}
@@ -33,13 +41,11 @@ public static List allVersions() {
* Get the version before {@code version}.
*/
public static Version getPreviousVersion(Version version) {
- for (int i = ALL_VERSIONS.size() - 1; i >= 0; i--) {
- Version v = ALL_VERSIONS.get(i);
- if (v.before(version)) {
- return v;
- }
+ var versions = ALL_VERSIONS.headSet(version, false);
+ if (versions.isEmpty()) {
+ throw new IllegalArgumentException("couldn't find any versions before [" + version + "]");
}
- throw new IllegalArgumentException("couldn't find any versions before [" + version + "]");
+ return versions.getLast();
}
/**
@@ -56,8 +62,7 @@ public static Version getPreviousVersion() {
* where the minor version is less than the currents minor version.
*/
public static Version getPreviousMinorVersion() {
- for (int i = ALL_VERSIONS.size() - 1; i >= 0; i--) {
- Version v = ALL_VERSIONS.get(i);
+ for (Version v : ALL_VERSIONS.descendingSet()) {
if (v.minor < Version.CURRENT.minor || v.major < Version.CURRENT.major) {
return v;
}
@@ -67,12 +72,12 @@ public static Version getPreviousMinorVersion() {
/** Returns the oldest {@link Version} */
public static Version getFirstVersion() {
- return ALL_VERSIONS.get(0);
+ return ALL_VERSIONS.getFirst();
}
/** Returns a random {@link Version} from all available versions. */
public static Version randomVersion(Random random) {
- return ALL_VERSIONS.get(random.nextInt(ALL_VERSIONS.size()));
+ return randomFrom(random, ALL_VERSIONS, Version::fromId);
}
/** Returns a random {@link Version} from all available versions, that is compatible with the given version. */
@@ -83,38 +88,42 @@ public static Version randomCompatibleVersion(Random random, Version version) {
/** Returns a random {@link Version} between minVersion and maxVersion (inclusive). */
public static Version randomVersionBetween(Random random, @Nullable Version minVersion, @Nullable Version maxVersion) {
- int minVersionIndex = 0;
+ if (minVersion != null && maxVersion != null && maxVersion.before(minVersion)) {
+ throw new IllegalArgumentException("maxVersion [" + maxVersion + "] cannot be less than minVersion [" + minVersion + "]");
+ }
+
+ NavigableSet versions = ALL_VERSIONS;
if (minVersion != null) {
- minVersionIndex = ALL_VERSIONS.indexOf(minVersion);
+ if (versions.contains(minVersion) == false) {
+ throw new IllegalArgumentException("minVersion [" + minVersion + "] does not exist.");
+ }
+ versions = versions.tailSet(minVersion, true);
}
- int maxVersionIndex = ALL_VERSIONS.size() - 1;
if (maxVersion != null) {
- maxVersionIndex = ALL_VERSIONS.indexOf(maxVersion);
- }
- if (minVersionIndex == -1) {
- throw new IllegalArgumentException("minVersion [" + minVersion + "] does not exist.");
- } else if (maxVersionIndex == -1) {
- throw new IllegalArgumentException("maxVersion [" + maxVersion + "] does not exist.");
- } else if (minVersionIndex > maxVersionIndex) {
- throw new IllegalArgumentException("maxVersion [" + maxVersion + "] cannot be less than minVersion [" + minVersion + "]");
- } else {
- // minVersionIndex is inclusive so need to add 1 to this index
- int range = maxVersionIndex + 1 - minVersionIndex;
- return ALL_VERSIONS.get(minVersionIndex + random.nextInt(range));
+ if (versions.contains(maxVersion) == false) {
+ throw new IllegalArgumentException("maxVersion [" + maxVersion + "] does not exist.");
+ }
+ versions = versions.headSet(maxVersion, true);
}
- }
- /** returns the first future compatible version */
- public static Version compatibleFutureVersion(Version version) {
- final Optional opt = ALL_VERSIONS.stream().filter(version::before).filter(v -> v.isCompatible(version)).findAny();
- assert opt.isPresent() : "no future compatible version for " + version;
- return opt.get();
+ return randomFrom(random, versions, Version::fromId);
}
/** Returns the maximum {@link Version} that is compatible with the given version. */
public static Version maxCompatibleVersion(Version version) {
- final List compatible = ALL_VERSIONS.stream().filter(version::isCompatible).filter(version::onOrBefore).toList();
- assert compatible.size() > 0;
- return compatible.get(compatible.size() - 1);
+ return ALL_VERSIONS.tailSet(version, true).descendingSet().stream().filter(version::isCompatible).findFirst().orElseThrow();
+ }
+
+ public static > T randomFrom(Random random, NavigableSet set, IntFunction ctor) {
+ // get the first and last id, pick a random id in the middle, then find that id in the set in O(nlogn) time
+ // this assumes the id numbers are reasonably evenly distributed in the set
+ assert set.isEmpty() == false;
+ int lowest = set.getFirst().id();
+ int highest = set.getLast().id();
+
+ T randomId = ctor.apply(RandomNumbers.randomIntBetween(random, lowest, highest));
+ // try to find the id below, then the id above. We're just looking for *some* item in the set that is close to randomId
+ T found = set.floor(randomId);
+ return found != null ? found : set.ceiling(randomId);
}
}
diff --git a/test/framework/src/main/java/org/elasticsearch/test/index/IndexVersionUtils.java b/test/framework/src/main/java/org/elasticsearch/test/index/IndexVersionUtils.java
index 667149e4bdd3e..5bf20b18abc72 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/index/IndexVersionUtils.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/index/IndexVersionUtils.java
@@ -14,41 +14,43 @@
import org.elasticsearch.index.IndexVersions;
import org.elasticsearch.index.KnownIndexVersions;
import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.test.VersionUtils;
-import java.util.Collections;
-import java.util.List;
+import java.util.NavigableSet;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
+import static org.apache.lucene.tests.util.LuceneTestCase.random;
+
public class IndexVersionUtils {
- private static final List ALL_VERSIONS = KnownIndexVersions.ALL_VERSIONS;
- private static final List ALL_WRITE_VERSIONS = KnownIndexVersions.ALL_WRITE_VERSIONS;
+ private static final NavigableSet ALL_VERSIONS = KnownIndexVersions.ALL_VERSIONS;
+ private static final NavigableSet ALL_WRITE_VERSIONS = KnownIndexVersions.ALL_WRITE_VERSIONS;
/** Returns all released versions */
- public static List allReleasedVersions() {
+ public static NavigableSet allReleasedVersions() {
return ALL_VERSIONS;
}
/** Returns the oldest known {@link IndexVersion}. This version can only be read from and not written to */
public static IndexVersion getLowestReadCompatibleVersion() {
- return ALL_VERSIONS.get(0);
+ return ALL_VERSIONS.getFirst();
}
/** Returns the oldest known {@link IndexVersion} that can be written to */
public static IndexVersion getLowestWriteCompatibleVersion() {
- return ALL_WRITE_VERSIONS.get(0);
+ return ALL_WRITE_VERSIONS.getFirst();
}
/** Returns a random {@link IndexVersion} from all available versions. */
public static IndexVersion randomVersion() {
- return ESTestCase.randomFrom(ALL_VERSIONS);
+ return VersionUtils.randomFrom(random(), ALL_VERSIONS, IndexVersion::fromId);
}
/** Returns a random {@link IndexVersion} from all versions that can be written to. */
public static IndexVersion randomWriteVersion() {
- return ESTestCase.randomFrom(ALL_WRITE_VERSIONS);
+ return VersionUtils.randomFrom(random(), ALL_WRITE_VERSIONS, IndexVersion::fromId);
}
/** Returns a random {@link IndexVersion} from all available versions without the ignore set */
@@ -62,23 +64,21 @@ public static IndexVersion randomVersionBetween(Random random, @Nullable IndexVe
throw new IllegalArgumentException("maxVersion [" + maxVersion + "] cannot be less than minVersion [" + minVersion + "]");
}
- int minVersionIndex = 0;
+ NavigableSet versions = allReleasedVersions();
if (minVersion != null) {
- minVersionIndex = Collections.binarySearch(ALL_VERSIONS, minVersion);
+ if (versions.contains(minVersion) == false) {
+ throw new IllegalArgumentException("minVersion [" + minVersion + "] does not exist.");
+ }
+ versions = versions.tailSet(minVersion, true);
}
- int maxVersionIndex = ALL_VERSIONS.size() - 1;
if (maxVersion != null) {
- maxVersionIndex = Collections.binarySearch(ALL_VERSIONS, maxVersion);
- }
- if (minVersionIndex < 0) {
- throw new IllegalArgumentException("minVersion [" + minVersion + "] does not exist.");
- } else if (maxVersionIndex < 0) {
- throw new IllegalArgumentException("maxVersion [" + maxVersion + "] does not exist.");
- } else {
- // minVersionIndex is inclusive so need to add 1 to this index
- int range = maxVersionIndex + 1 - minVersionIndex;
- return ALL_VERSIONS.get(minVersionIndex + random.nextInt(range));
+ if (versions.contains(maxVersion) == false) {
+ throw new IllegalArgumentException("maxVersion [" + maxVersion + "] does not exist.");
+ }
+ versions = versions.headSet(maxVersion, true);
}
+
+ return VersionUtils.randomFrom(random, versions, IndexVersion::fromId);
}
public static IndexVersion getPreviousVersion() {
@@ -88,16 +88,11 @@ public static IndexVersion getPreviousVersion() {
}
public static IndexVersion getPreviousVersion(IndexVersion version) {
- int place = Collections.binarySearch(ALL_VERSIONS, version);
- if (place < 0) {
- // version does not exist - need the item before the index this version should be inserted
- place = -(place + 1);
- }
-
- if (place < 1) {
+ IndexVersion lower = allReleasedVersions().lower(version);
+ if (lower == null) {
throw new IllegalArgumentException("couldn't find any released versions before [" + version + "]");
}
- return ALL_VERSIONS.get(place - 1);
+ return lower;
}
public static IndexVersion getPreviousMajorVersion(IndexVersion version) {
@@ -105,19 +100,11 @@ public static IndexVersion getPreviousMajorVersion(IndexVersion version) {
}
public static IndexVersion getNextVersion(IndexVersion version) {
- int place = Collections.binarySearch(ALL_VERSIONS, version);
- if (place < 0) {
- // version does not exist - need the item at the index this version should be inserted
- place = -(place + 1);
- } else {
- // need the *next* version
- place++;
- }
-
- if (place < 0 || place >= ALL_VERSIONS.size()) {
+ IndexVersion higher = allReleasedVersions().higher(version);
+ if (higher == null) {
throw new IllegalArgumentException("couldn't find any released versions after [" + version + "]");
}
- return ALL_VERSIONS.get(place);
+ return higher;
}
/** Returns a random {@code IndexVersion} that is compatible with {@link IndexVersion#current()} */
diff --git a/test/framework/src/test/java/org/elasticsearch/test/AbstractXContentTestCaseTests.java b/test/framework/src/test/java/org/elasticsearch/test/AbstractXContentTestCaseTests.java
index e3cc3bba94a5c..b8f4dcb399ec7 100644
--- a/test/framework/src/test/java/org/elasticsearch/test/AbstractXContentTestCaseTests.java
+++ b/test/framework/src/test/java/org/elasticsearch/test/AbstractXContentTestCaseTests.java
@@ -12,13 +12,11 @@
import com.carrotsearch.randomizedtesting.RandomizedContext;
import org.elasticsearch.common.bytes.BytesReference;
-import org.elasticsearch.xcontent.ToXContentFragment;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentFactory;
import org.elasticsearch.xcontent.XContentParser;
import org.elasticsearch.xcontent.XContentType;
-import java.io.IOException;
import java.util.Map;
import static org.hamcrest.Matchers.equalTo;
@@ -51,42 +49,4 @@ public void testInsertRandomFieldsAndShuffle() throws Exception {
assertThat(mapOrdered.keySet().iterator().next(), not(equalTo("field")));
}
}
-
- private record TestToXContent(String field, String value) implements ToXContentFragment {
-
- @Override
- public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
- return builder.field(field, value);
- }
- }
-
- public void testYamlXContentRoundtripSanitization() throws Exception {
- var test = new AbstractXContentTestCase() {
-
- @Override
- protected TestToXContent createTestInstance() {
- // we need to randomly create both a "problematic" and an okay version in order to ensure that the sanitization code
- // can draw at least one okay version if polled often enough
- return randomBoolean() ? new TestToXContent("a\u0085b", "def") : new TestToXContent("a b", "def");
- }
-
- @Override
- protected TestToXContent doParseInstance(XContentParser parser) throws IOException {
- assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken());
- assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken());
- String name = parser.currentName();
- assertEquals(XContentParser.Token.VALUE_STRING, parser.nextToken());
- String value = parser.text();
- assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken());
- return new TestToXContent(name, value);
- };
-
- @Override
- protected boolean supportsUnknownFields() {
- return false;
- }
- };
- // testFromXContent runs 20 repetitions, enough to hit a YAML xcontent version very likely
- test.testFromXContent();
- }
}
diff --git a/test/framework/src/test/java/org/elasticsearch/test/VersionUtilsTests.java b/test/framework/src/test/java/org/elasticsearch/test/VersionUtilsTests.java
index 5ae7e5640fc91..9951878289d48 100644
--- a/test/framework/src/test/java/org/elasticsearch/test/VersionUtilsTests.java
+++ b/test/framework/src/test/java/org/elasticsearch/test/VersionUtilsTests.java
@@ -21,13 +21,6 @@
*/
public class VersionUtilsTests extends ESTestCase {
- public void testAllVersionsSorted() {
- List allVersions = VersionUtils.allVersions();
- for (int i = 0, j = 1; j < allVersions.size(); ++i, ++j) {
- assertTrue(allVersions.get(i).before(allVersions.get(j)));
- }
- }
-
public void testRandomVersionBetween() {
// TODO: rework this test to use a dummy Version class so these don't need to change with each release
// full range
@@ -50,9 +43,9 @@ public void testRandomVersionBetween() {
got = VersionUtils.randomVersionBetween(random(), null, fromId(7000099));
assertTrue(got.onOrAfter(VersionUtils.getFirstVersion()));
assertTrue(got.onOrBefore(fromId(7000099)));
- got = VersionUtils.randomVersionBetween(random(), null, VersionUtils.allVersions().get(0));
+ got = VersionUtils.randomVersionBetween(random(), null, VersionUtils.allVersions().getFirst());
assertTrue(got.onOrAfter(VersionUtils.getFirstVersion()));
- assertTrue(got.onOrBefore(VersionUtils.allVersions().get(0)));
+ assertTrue(got.onOrBefore(VersionUtils.allVersions().getFirst()));
// unbounded upper
got = VersionUtils.randomVersionBetween(random(), VersionUtils.getFirstVersion(), null);
diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/AbstractBWCWireSerializationTestCase.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/AbstractBWCWireSerializationTestCase.java
index 451c85936f3cb..d0dff954dec13 100644
--- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/AbstractBWCWireSerializationTestCase.java
+++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/AbstractBWCWireSerializationTestCase.java
@@ -12,7 +12,7 @@
import org.elasticsearch.test.AbstractWireSerializingTestCase;
import java.io.IOException;
-import java.util.List;
+import java.util.Collection;
import static org.elasticsearch.test.BWCVersions.DEFAULT_BWC_VERSIONS;
@@ -26,7 +26,7 @@ public abstract class AbstractBWCWireSerializationTestCase
/**
* The bwc versions to test serialization against
*/
- protected List bwcVersions() {
+ protected Collection bwcVersions() {
return DEFAULT_BWC_VERSIONS;
}
diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/AbstractChunkedBWCSerializationTestCase.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/AbstractChunkedBWCSerializationTestCase.java
index 0254406a2c8ec..e6b6ef3e3a06a 100644
--- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/AbstractChunkedBWCSerializationTestCase.java
+++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/AbstractChunkedBWCSerializationTestCase.java
@@ -13,7 +13,7 @@
import org.elasticsearch.test.AbstractChunkedSerializingTestCase;
import java.io.IOException;
-import java.util.List;
+import java.util.Collection;
import static org.elasticsearch.test.BWCVersions.DEFAULT_BWC_VERSIONS;
@@ -28,7 +28,7 @@ public abstract class AbstractChunkedBWCSerializationTestCase bwcVersions() {
+ protected Collection bwcVersions() {
return DEFAULT_BWC_VERSIONS;
}
diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/action/CoordinatedInferenceActionRequestTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/action/CoordinatedInferenceActionRequestTests.java
index 3ab5851815474..91070d5768f63 100644
--- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/action/CoordinatedInferenceActionRequestTests.java
+++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/action/CoordinatedInferenceActionRequestTests.java
@@ -120,7 +120,7 @@ protected CoordinatedInferenceAction.Request mutateInstanceForVersion(
instance.setPrefixType(TrainedModelPrefixStrings.PrefixType.NONE);
}
- return new CoordinatedInferenceAction.Request(
+ var newInstance = new CoordinatedInferenceAction.Request(
instance.getModelId(),
instance.getInputs(),
instance.getTaskSettings(),
@@ -131,5 +131,7 @@ protected CoordinatedInferenceAction.Request mutateInstanceForVersion(
instance.getHighPriority(),
instance.getRequestModelType()
);
+ newInstance.setPrefixType(instance.getPrefixType());
+ return newInstance;
}
}
diff --git a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/AbstractBWCSerializationTestCase.java b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/AbstractBWCSerializationTestCase.java
index fc41bdd627c95..2e8b8578b5056 100644
--- a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/AbstractBWCSerializationTestCase.java
+++ b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/AbstractBWCSerializationTestCase.java
@@ -10,23 +10,21 @@
import org.elasticsearch.TransportVersions;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.test.AbstractXContentSerializingTestCase;
+import org.elasticsearch.test.TransportVersionUtils;
import org.elasticsearch.xcontent.ToXContent;
import java.io.IOException;
-import java.util.Collections;
-import java.util.List;
+import java.util.NavigableSet;
import static org.hamcrest.Matchers.equalTo;
public abstract class AbstractBWCSerializationTestCase extends AbstractXContentSerializingTestCase {
- private static List getAllBWCVersions() {
- List allVersions = TransportVersion.getAllVersions();
- int minCompatVersion = Collections.binarySearch(allVersions, TransportVersions.MINIMUM_COMPATIBLE);
- return allVersions.subList(minCompatVersion, allVersions.size());
+ private static NavigableSet getAllBWCVersions() {
+ return TransportVersionUtils.allReleasedVersions().tailSet(TransportVersions.MINIMUM_COMPATIBLE, true);
}
- private static final List DEFAULT_BWC_VERSIONS = getAllBWCVersions();
+ private static final NavigableSet DEFAULT_BWC_VERSIONS = getAllBWCVersions();
protected abstract T mutateInstanceForVersion(T instance, TransportVersion version);
diff --git a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/AbstractBWCWireSerializingTestCase.java b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/AbstractBWCWireSerializingTestCase.java
index 30777f43597c8..76c2b3355e236 100644
--- a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/AbstractBWCWireSerializingTestCase.java
+++ b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/AbstractBWCWireSerializingTestCase.java
@@ -10,22 +10,20 @@
import org.elasticsearch.TransportVersions;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.test.AbstractWireSerializingTestCase;
+import org.elasticsearch.test.TransportVersionUtils;
import java.io.IOException;
-import java.util.Collections;
-import java.util.List;
+import java.util.NavigableSet;
import static org.hamcrest.Matchers.equalTo;
public abstract class AbstractBWCWireSerializingTestCase extends AbstractWireSerializingTestCase {
- private static List getAllBWCVersions() {
- List allVersions = TransportVersion.getAllVersions();
- int minCompatVersion = Collections.binarySearch(allVersions, TransportVersions.MINIMUM_COMPATIBLE);
- return allVersions.subList(minCompatVersion, allVersions.size());
+ private static NavigableSet getAllBWCVersions() {
+ return TransportVersionUtils.allReleasedVersions().tailSet(TransportVersions.MINIMUM_COMPATIBLE, true);
}
- private static final List DEFAULT_BWC_VERSIONS = getAllBWCVersions();
+ private static final NavigableSet DEFAULT_BWC_VERSIONS = getAllBWCVersions();
protected abstract T mutateInstanceForVersion(T instance, TransportVersion version);
diff --git a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/MetadataAttribute.java b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/MetadataAttribute.java
index 0f1cfbb85039c..dc75ac3a96248 100644
--- a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/MetadataAttribute.java
+++ b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/MetadataAttribute.java
@@ -32,6 +32,7 @@ public class MetadataAttribute extends TypedAttribute {
public static final String TIMESTAMP_FIELD = "@timestamp";
public static final String TSID_FIELD = "_tsid";
public static final String SCORE = "_score";
+ public static final String INDEX = "_index";
static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
Attribute.class,
@@ -42,7 +43,7 @@ public class MetadataAttribute extends TypedAttribute {
private static final Map> ATTRIBUTES_MAP = Map.of(
"_version",
tuple(DataType.LONG, false), // _version field is not searchable
- "_index",
+ INDEX,
tuple(DataType.KEYWORD, true),
IdFieldMapper.NAME,
tuple(DataType.KEYWORD, false), // actually searchable, but fielddata access on the _id field is disallowed by default
diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-remote.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-remote.csv-spec
index 4d7ee9b1b5af6..88c4fbf7de6cc 100644
--- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-remote.csv-spec
+++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-remote.csv-spec
@@ -39,7 +39,7 @@ max:integer |_index:keyword
;
metaIndexAliasedInAggs
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: metadata_fields_remote_test
from employees metadata _index | eval _i = _index | stats max = max(emp_no) by _i | SORT _i;
diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata.csv-spec
index a213c378d33d8..1f41ffdb60691 100644
--- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata.csv-spec
+++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata.csv-spec
@@ -40,7 +40,7 @@ max:integer |_index:keyword
;
metaIndexSorted
-required_capability: metadata_fields
+required_capability: index_metadata_field
from employees metadata _index | sort _index, emp_no desc | keep emp_no, _index | limit 2;
@@ -50,7 +50,7 @@ emp_no:integer |_index:keyword
;
metaIndexWithInPredicate
-required_capability: metadata_fields
+required_capability: index_metadata_field
from employees metadata _index | where _index in ("employees", "foobar") | sort emp_no desc | keep emp_no, _index | limit 2;
@@ -60,7 +60,7 @@ emp_no:integer |_index:keyword
;
metaIndexAliasedInAggs
-required_capability: metadata_fields
+required_capability: index_metadata_field
from employees metadata _index | eval _i = _index | stats max = max(emp_no) by _i;
diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/union_types.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/union_types.csv-spec
index a2f491e20e3b9..8b19bc589fcff 100644
--- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/union_types.csv-spec
+++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/union_types.csv-spec
@@ -133,7 +133,7 @@ mc:l | count:l
multiIndexIpString
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: casting_operator
required_capability: union_types_remove_fields
@@ -162,7 +162,7 @@ sample_data_str | 2023-10-23T12:15:03.360Z | 172.21.2.162 | 3450233
multiIndexIpStringRename
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: casting_operator
required_capability: union_types_remove_fields
@@ -191,7 +191,7 @@ sample_data_str | 2023-10-23T12:15:03.360Z | 172.21.2.162 | 3450233
multiIndexIpStringRenameToString
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
FROM sample_data, sample_data_str METADATA _index
@@ -219,7 +219,7 @@ sample_data_str | 2023-10-23T12:15:03.360Z | 172.21.2.162 | 3450233
multiIndexWhereIpString
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
FROM sample_data, sample_data_str METADATA _index
@@ -237,7 +237,7 @@ sample_data_str | 2023-10-23T12:15:03.360Z | 3450233 | Connected
multiIndexWhereIpStringLike
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
FROM sample_data, sample_data_str METADATA _index
@@ -445,7 +445,7 @@ count:long | message:keyword
multiIndexMissingIpToString
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_missing_field
FROM sample_data, sample_data_str, missing_ip_sample_data METADATA _index
@@ -480,7 +480,7 @@ sample_data_str | 2023-10-23T12:15:03.360Z | 172.21.2.162 | 3450
multiIndexMissingIpToIp
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_missing_field
FROM sample_data, sample_data_str, missing_ip_sample_data METADATA _index
@@ -515,7 +515,7 @@ sample_data_str | 2023-10-23T12:15:03.360Z | 172.21.2.162 | 3450233
multiIndexTsLong
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
FROM sample_data, sample_data_ts_long METADATA _index
@@ -543,7 +543,7 @@ sample_data_ts_long | 2023-10-23T12:15:03.360Z | 172.21.2.162 | 3450233
multiIndexTsLongRename
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
FROM sample_data, sample_data_ts_long METADATA _index
@@ -573,7 +573,7 @@ sample_data_ts_long | 2023-10-23T12:15:03.360Z | 172.21.2.162 | 3450233
multiIndexTsNanosRename
required_capability: to_date_nanos
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
FROM sample_data, sample_data_ts_nanos METADATA _index
@@ -602,7 +602,7 @@ sample_data_ts_nanos | 2023-10-23T12:15:03.360Z | 172.21.2.162 | 3450233
multiIndexTsNanosRenameToNanos
required_capability: to_date_nanos
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
FROM sample_data, sample_data_ts_nanos METADATA _index
@@ -631,7 +631,7 @@ sample_data_ts_nanos | 2023-10-23T12:15:03.360123456Z | 172.21.2.162 | 34502
multiIndex sort millis and nanos as nanos
required_capability: to_date_nanos
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
FROM sample_data, sample_data_ts_nanos METADATA _index
@@ -660,7 +660,7 @@ sample_data | 2023-10-23T12:15:03.360000000Z | 172.21.2.162 | 34502
multiIndex sort millis and nanos as millis
required_capability: to_date_nanos
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
FROM sample_data, sample_data_ts_nanos METADATA _index
@@ -691,7 +691,7 @@ multiIndexTsNanosRenameToNanosWithFiltering
required_capability: to_date_nanos
required_capability: date_nanos_binary_comparison
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
FROM sample_data, sample_data_ts_nanos METADATA _index
@@ -716,7 +716,7 @@ sample_data_ts_nanos | 2023-10-23T13:33:34.937123456Z | 172.21.0.5 | 12323
multiIndexTsLongRenameToString
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
FROM sample_data, sample_data_ts_long METADATA _index
@@ -744,7 +744,7 @@ sample_data_ts_long | 2023-10-23T12:15:03.360Z | 172.21.2.162 | 3450233
multiIndexWhereTsLong
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
FROM sample_data, sample_data_ts_long METADATA _index
@@ -979,7 +979,7 @@ count:long | message:keyword
multiIndexIpStringTsLong
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
required_capability: to_date_nanos
@@ -1022,7 +1022,7 @@ sample_data_ts_nanos | 2023-10-23T12:15:03.360Z | 172.21.2.162 | 3450233
multiIndexIpStringTsLongDropped
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: to_date_nanos
FROM sample_data* METADATA _index
@@ -1064,7 +1064,7 @@ sample_data_ts_nanos | 8268153 | Connection error
multiIndexIpStringTsLongRename
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
required_capability: to_date_nanos
@@ -1107,7 +1107,7 @@ sample_data_ts_nanos | 2023-10-23T12:15:03.360Z | 172.21.2.162 | 3450233
multiIndexIpStringTsLongRenameDropped
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: to_date_nanos
FROM sample_data* METADATA _index
@@ -1149,7 +1149,7 @@ sample_data_ts_nanos | 8268153 | Connection error
multiIndexIpStringTsLongRenameToString
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
required_capability: to_date_nanos
@@ -1192,7 +1192,7 @@ sample_data_ts_nanos | 2023-10-23T12:15:03.360Z | 172.21.2.162 | 3450233
multiIndexWhereIpStringTsLong
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
required_capability: to_date_nanos
@@ -1226,7 +1226,7 @@ count:long | message:keyword
multiIndexWhereIpStringLikeTsLong
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
required_capability: to_date_nanos
@@ -1260,7 +1260,7 @@ count:long | message:keyword
multiIndexMultiColumnTypesRename
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
required_capability: to_date_nanos
@@ -1279,7 +1279,7 @@ null | null | 8268153 | Connectio
multiIndexMultiColumnTypesRenameAndKeep
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
required_capability: to_date_nanos
@@ -1299,7 +1299,7 @@ sample_data_ts_nanos | 2023-10-23T13:52:55.015Z | 2023-10-23T13:52:55.015123456
multiIndexMultiColumnTypesRenameAndDrop
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: union_types_remove_fields
required_capability: to_date_nanos
@@ -1591,7 +1591,7 @@ FROM sample_data, sample_data_ts_long
shortIntegerWidening
required_capability: union_types
-required_capability: metadata_fields
+required_capability: index_metadata_field
required_capability: casting_operator
required_capability: union_types_numeric_widening
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java
index 25518220e308b..b7ec21b96be37 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java
@@ -121,12 +121,17 @@ public enum Cap {
* Cast string literals to a desired data type for IN predicate and more types for BinaryComparison.
*/
STRING_LITERAL_AUTO_CASTING_EXTENDED,
-
/**
* Support for metadata fields.
*/
METADATA_FIELDS,
+ /**
+ * Support specifically for *just* the _index METADATA field. Used by CsvTests, since that is the only metadata field currently
+ * supported.
+ */
+ INDEX_METADATA_FIELD,
+
/**
* Support for timespan units abbreviations
*/
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/IndexResolver.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/IndexResolver.java
index b11a8580a1e18..3e4dd6849478a 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/IndexResolver.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/IndexResolver.java
@@ -21,6 +21,7 @@
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.esql.action.EsqlResolveFieldsAction;
+import org.elasticsearch.xpack.esql.core.expression.MetadataAttribute;
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.core.type.DateEsField;
import org.elasticsearch.xpack.esql.core.type.EsField;
@@ -50,7 +51,7 @@
public class IndexResolver {
public static final Set ALL_FIELDS = Set.of("*");
- public static final Set INDEX_METADATA_FIELD = Set.of("_index");
+ public static final Set INDEX_METADATA_FIELD = Set.of(MetadataAttribute.INDEX);
public static final String UNMAPPED = "unmapped";
public static final IndicesOptions FIELD_CAPS_INDICES_OPTIONS = IndicesOptions.builder()
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractAggregationTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractAggregationTestCase.java
index 87ea6315d4f3b..9f0fc34b7d539 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractAggregationTestCase.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractAggregationTestCase.java
@@ -61,7 +61,7 @@ public abstract class AbstractAggregationTestCase extends AbstractFunctionTestCa
* Use if possible, as this method may get updated with new checks in the future.
*